ecere/gui/controls/ListBox: Support for text without opaque background in ListBox
[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       // Draw gray grid
2047       if(style.alwaysEdit && style.fullRowSelect)
2048       {
2049          // Horizontal lines
2050          int y = (style.header) ? rowHeight : 0;
2051          int x = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
2052          int w = clientSize.w;
2053          int h = clientSize.h;
2054          DataRow row;
2055          DataField field;
2056
2057          // Fill out indent column
2058          if(style.collapse && !(style.treeBranch) && rows.first)
2059          {
2060             x += 15;
2061             surface.SetBackground(formColor);
2062             surface.Area(-scroll.x, 0, x, clientSize.h);
2063          }
2064
2065          surface.SetForeground(formColor);
2066          for(row = firstRowShown; row; row = row.GetNextRow())
2067          {
2068             y += rowHeight;
2069             surface.HLine(x + 1, w-1, y-1);
2070             if(y >= h)
2071                break;
2072          }
2073
2074          // Vertical lines
2075          for(field = fields.first; field; field = field.next)
2076          {
2077             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2078                clientSize.w - field.x : (field.width + EXTRA_SPACE);
2079             if(field.prev && y > 0)
2080                surface.VLine(0, y-1, x);
2081             x += width;
2082          }
2083       }
2084
2085       surface.foreground = this.foreground;
2086       surface.TextOpacity(false);
2087
2088       // Draw the tree branches
2089       if(style.treeBranch)
2090       {
2091          int y = -scroll.y + ((style.header) ? rowHeight : 0);
2092          surface.LineStipple(0x5555);
2093          surface.SetForeground(branchesColor);
2094          for(row = rows.first; row; row = row.GetNextRow() )
2095          {
2096             int x = -scroll.x + EXTRA_SPACE / 2-1;
2097             int rowStart = -scroll.x;
2098             int indent = 0;
2099             DataRow parent;
2100             int plusIndent;
2101
2102             for(parent = row.parent; parent; parent = parent.parent)
2103                if(!parent.header) indent += 20;
2104             if(style.rootCollapse) indent += 20;
2105
2106             plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2107
2108             x += indent;
2109
2110             // Vertical line
2111             if(row.subRows.first)
2112             {
2113                int numRows = 0;
2114                int y1 = y + PLUSY + 4;
2115                int y2;
2116                DataRow child;
2117             
2118                for(child = row.collapsed ? null : row.subRows.first; child && child != row; )
2119                {
2120                   numRows++;
2121                   if(child.subRows.first && !child.collapsed && child != row.subRows.last)
2122                      child = child.subRows.first;
2123                   else if(child.next)
2124                      child = child.next;
2125                   else
2126                   {
2127                      for(; child && child != row; child = child.parent)
2128                      {
2129                         if(child.next)
2130                         {
2131                            child = child.next;
2132                            break;
2133                         }
2134                      }
2135                   }
2136                }
2137                y2 = y + numRows * rowHeight + PLUSY + 4;
2138                surface.VLine(y1, y2, rowStart + plusIndent + 7 + 20);
2139             }
2140             surface.HLine(rowStart + plusIndent + 7, rowStart + indent, y + PLUSY + 4);
2141
2142             y += rowHeight;
2143             if(y >= clientSize.h)
2144                break;
2145          }
2146          // Root Vertical Lines
2147          if(style.rootCollapse && rows.first)
2148          {
2149             int numRows = 0;
2150             int y1, y2;
2151             DataRow child;
2152             y = -scroll.y + ((style.header) ? rowHeight : 0);
2153             y1 = y + PLUSY + 4;
2154             for(child = rows.first; child && child != rows.last; )
2155             {
2156                numRows++;
2157                if(child.subRows.first && !child.collapsed && child != rows.last)
2158                   child = child.subRows.first;
2159                else if(child.next)
2160                   child = child.next;
2161                else
2162                {
2163                   for(; child; child = child.parent)
2164                   {
2165                      if(child.next)
2166                      {
2167                         child = child.next;
2168                         break;
2169                      }
2170                   }
2171                }
2172             }
2173             y2 = y + numRows * rowHeight + PLUSY + 4;
2174             surface.VLine(y1, y2, -scroll.x + 11);
2175          }
2176          surface.LineStipple(0);
2177       }
2178
2179       for(row = firstRowShown; row; row = row.GetNextRow() )
2180       {
2181          int x = -scroll.x + EXTRA_SPACE / 2-1;
2182          DataField field;
2183          ListBoxCell cell;
2184          Color foreground = this.foreground /*black*/, background = this.background /*white*/;
2185          DataDisplayFlags dataDisplayFlags = 0;
2186          int rowStart = -scroll.x;
2187          int indent = 0;
2188          DataRow parent;
2189          Bitmap icon = row.icon ? row.icon.bitmap : null;
2190          int collapseRowStart;
2191
2192          for(parent = row.parent; parent; parent = parent.parent)
2193          {
2194             if(!parent.header)
2195             {
2196                if(style.treeBranch)
2197                   indent += 20;
2198                else
2199                   indent += 15;
2200             }
2201          }
2202          if(style.rootCollapse) indent += 20;
2203          x += indent;
2204
2205          dataDisplayFlags.fullRow = style.fullRowSelect;
2206          dataDisplayFlags.active = isActive;
2207          dataDisplayFlags.header = row.header;
2208
2209          surface.Clip(null);
2210          if(style.collapse)
2211          {
2212             collapseRowStart = rowStart;
2213
2214             if(!(style.treeBranch))
2215             {
2216                x += 15;
2217                rowStart += 15;
2218             }
2219          }
2220
2221          if(style.multiSelect)
2222          {
2223             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2224             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2225          }
2226          else
2227          {
2228             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2229             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2230             /*
2231             if(row == currentRow)
2232             {
2233                dataDisplayFlags.current = true;
2234                dataDisplayFlags.selectedFlag = true;
2235             }
2236             else if(!currentRow && row == firstRowShown)
2237             {
2238                dataDisplayFlags.current = true;
2239             }*/
2240          }
2241
2242          surface.TextOpacity(true);
2243
2244          background = this.background;
2245          foreground = this.foreground;
2246
2247          // Draw the current row background
2248          if(row.header)
2249          {
2250             background = formColor;
2251             surface.SetBackground(formColor);
2252             surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - 1);
2253             foreground = branchesColor;
2254          }
2255          else if(dataDisplayFlags.selected)
2256          {
2257             if(dataDisplayFlags.selected && (isActive || style.alwaysHL || (style.alwaysEdit && style.fullRowSelect)))
2258             {
2259                if(!isActive && style.alwaysEdit)
2260                   background = formColor;
2261                else
2262                   background = selectionColor ? selectionColor : SELECTION_COLOR;
2263                if(style.fullRowSelect)
2264                {
2265                   int offset = (style.alwaysEdit) ? 2 : 1;
2266                   surface.SetBackground(background);
2267                   surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - offset);
2268                }
2269                if(isActive || !(style.alwaysEdit))
2270                   foreground = selectionText ? selectionText : SELECTION_TEXT;
2271                else
2272                   foreground = branchesColor;
2273             }
2274          }
2275
2276          if(icon)
2277          {
2278             surface.Blit(icon, x + (20 - icon.width) /2,y + 2,0,0, icon.width, icon.height);
2279             x += 20;
2280          }
2281
2282          if(row.noneRow)
2283          {
2284             int width = clientSize.w;
2285             Box clip;
2286             dataDisplayFlags.firstField = true;
2287             clip.left = x - EXTRA_SPACE / 2+1;
2288             clip.top = y;
2289             clip.right = x + width - EXTRA_SPACE/2 - 0;
2290             clip.bottom = y + rowHeight - 1;
2291             surface.Clip(&clip);
2292
2293             surface.TextFont(font);
2294
2295             surface.SetForeground(foreground);
2296             surface.SetBackground(background);
2297
2298             class(String)._vTbl[__ecereVMethodID_class_OnDisplay](class(String), "(none)", surface, x, y - 1 + (rowHeight - fontH)/2, width - EXTRA_SPACE/2, null, Alignment::left, dataDisplayFlags);
2299          }
2300          else
2301          {
2302             if(!opacity) surface.TextOpacity(false);
2303             // Draw the rows
2304             for(field = fields.first; field; field = field.next)
2305             {
2306                uint index;
2307                int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2308                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
2309
2310                // Box clip = { x, y+1, x + field.width - EXTRA_SPACE - 1, y + rowHeight - 2 };
2311                Box clip;
2312
2313                //width -= EXTRA_SPACE;
2314
2315                if(!field.prev) width -= indent;
2316
2317
2318                dataDisplayFlags.firstField = field.prev ? false : true;
2319
2320                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2321                {
2322                   background = this.background;
2323                   foreground = this.foreground;
2324                }
2325
2326                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable)
2327                {
2328                   surface.Clip(null);
2329                   surface.SetBackground(background);
2330                   surface.Area(x-3, y, x+width-1, y + rowHeight-2);
2331                }
2332
2333                clip.left = x - EXTRA_SPACE / 2+1;
2334                clip.top = y;
2335                clip.right = x + width - EXTRA_SPACE/2 - 0;
2336                clip.bottom = y + rowHeight - 1;
2337                surface.Clip(&clip);
2338
2339                for(index = 0, cell = row.cells.first; cell && index != field.index; index++, cell = cell.next);
2340                // Should always be as many cells in the row as fields in the listbox
2341                if(cell && cell.isSet && field.dataType)
2342                {
2343                   if(row.header)
2344                      surface.TextFont(boldFont);
2345                   else
2346                      surface.TextFont(font);
2347
2348                   surface.SetForeground(foreground);
2349                   surface.SetBackground(background);
2350
2351                   if(field.dataType.type == noHeadClass || field.dataType.type == normalClass)
2352                      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);
2353                   else
2354                      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);
2355                }
2356
2357                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable)
2358                   background = formColor;
2359
2360                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2361                {
2362                   background = formColor;
2363                   foreground = this.background;
2364                }
2365
2366                x += width;// + EXTRA_SPACE;
2367
2368                if(row.header) break;
2369             }
2370          }
2371
2372          if(style.collapse)
2373          {
2374             int plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2375             surface.Clip(null);
2376             if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)))
2377             {
2378                surface.SetForeground(row.header ? headerCollapseForeground : this.foreground);
2379                surface.Rectangle(collapseRowStart + 3 + plusIndent, y + PLUSY, collapseRowStart + 11 + plusIndent, y + PLUSY + 8);
2380
2381                surface.SetBackground(row.header ? (formColor) : (this.background)); //white
2382                surface.Area(collapseRowStart + 4 + plusIndent, y + PLUSY + 1, collapseRowStart + 10 + plusIndent, y + PLUSY + 7);
2383
2384                surface.HLine(collapseRowStart + 5 + plusIndent, collapseRowStart + 9 + plusIndent, y+PLUSY+4);
2385                if(row.collapsed)
2386                   surface.VLine(y + PLUSY + 2, y + PLUSY + 6, collapseRowStart + 7 + plusIndent);
2387             }
2388
2389          }
2390
2391          // Draw the current row stipple
2392          if(style.fullRowSelect && !(style.alwaysEdit) && (dataDisplayFlags.current) && isActive)
2393          {
2394             surface.Clip(null);
2395             if(isActive)
2396             {
2397                surface.LineStipple(0x5555);
2398                if(dataDisplayFlags.selected)
2399                   surface.SetForeground(stippleColor);
2400                else
2401                   surface.SetForeground(this.foreground);
2402             }
2403             else
2404                surface.SetForeground(selectionColor ? selectionColor : SELECTION_COLOR);
2405             surface.Rectangle(0, y, clientSize.w-1, (y + rowHeight) - 1);
2406             surface.LineStipple(0);
2407          }
2408
2409          y += rowHeight;
2410          if(y >= clientSize.h)
2411             break;
2412       }
2413       if(firstRowShown) surface.Clip(null);
2414       if(this.dragRow && this.dropIndex != -1)
2415       {
2416          int dropIndex = this.dropIndex;
2417          int y;
2418
2419          if(!style.multiSelect && currentRow.index < this.dropIndex)
2420             dropIndex++;
2421          surface.DrawingChar(223);
2422
2423          y = style.header ? rowHeight : 0;
2424          y += dropIndex * rowHeight - scroll.y;
2425
2426          surface.SetForeground(Color { 85, 85, 255 });
2427          surface.HLine(0, clientSize.w-1, y);
2428          surface.HLine(0, clientSize.w-1, y + 1);
2429       }
2430    }
2431
2432    void OnDrawOverChildren(Surface surface)
2433    {
2434       if(draggingField && this.dropField)
2435       {
2436          int position = this.dropField.x;
2437          if(draggingField.x < position)
2438             position += this.dropField.width + EXTRA_SPACE;
2439
2440          surface.SetForeground(Color { 85, 85, 255 });
2441          surface.VLine(0, rowHeight - 1, position - scroll.x - 2);
2442          surface.VLine(0, rowHeight - 1, position - scroll.x);
2443       }
2444       if(sortField && !style.clearHeader && style.header)
2445       {
2446          DataField field = sortField;
2447          int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2448             clientSize.w - field.x : (field.width + EXTRA_SPACE);
2449          int tw = 0, th = 0;
2450          if(field.header)
2451             surface.TextExtent(field.header, strlen(field.header), &tw, &th);
2452          if(tw < width - EXTRA_SPACE)
2453          {
2454             bool up = field.sortOrder == 1;
2455             int x = 4, y = 4;
2456             Box clip = 
2457             { 
2458                field.x + 2 - scroll.x, 0, 
2459                field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight
2460             };
2461             surface.Clip(&clip);
2462             if(field.alignment == left || field.alignment == center)
2463             {
2464                if(field.alignment == center)
2465                   x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4;
2466                else
2467                   x = field.x + tw + EXTRA_SPACE + 4;
2468              
2469                x = Min(x, field.x + width - 4);
2470             }
2471             else if(field.alignment == right)
2472             {
2473                x = field.x + width - tw - EXTRA_SPACE - 4;
2474                x = Max(x, field.x + 2);
2475             }
2476             x -= scroll.x;
2477
2478             if(guiApp.textMode)
2479             {
2480                // surface.SetForeground((wmenu.selectedFlag == item) ? white : black);
2481                // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1);
2482             }
2483             else
2484             {
2485                if(up)
2486                {
2487                   surface.SetForeground(Color { 128,128,128 } );
2488                   surface.DrawLine(x + 3, y, x, y + 5);
2489                   surface.PutPixel(x + 1, y + 5);
2490                   surface.PutPixel(x + 1, y + 3);
2491                   surface.PutPixel(x + 2, y + 1);
2492             
2493                   surface.SetForeground(white);
2494                   surface.DrawLine(x + 4, y, x + 7, y + 5);
2495                   surface.PutPixel(x + 6, y + 5);
2496                   surface.PutPixel(x + 6, y + 3);
2497                   surface.PutPixel(x + 5, y + 1);
2498
2499                   surface.DrawLine(x, y + 6, x + 7, y + 6);
2500                }
2501                else
2502                {
2503                   surface.SetForeground(Color { 128,128,128 });
2504                   surface.DrawLine(x + 3, y+6, x, y+1);
2505                   surface.PutPixel(x + 1, y+1);
2506                   surface.PutPixel(x + 1, y+3);
2507                   surface.PutPixel(x + 2, y+5);
2508             
2509                   surface.SetForeground(white);
2510                   surface.DrawLine(x + 4, y+6, x + 7, y+1);
2511                   surface.PutPixel(x + 6, y+1);
2512                   surface.PutPixel(x + 6, y+3);
2513                   surface.PutPixel(x + 5, y+5);
2514
2515                   surface.DrawLine(x, y, x + 7, y);
2516                }
2517             }
2518             surface.Clip(null);
2519          }
2520       }
2521
2522    }
2523
2524    void OnResize(int w, int h)
2525    {
2526       DataField field;
2527       bool showEndBevel = false;
2528       int x = 0;
2529       if(style.collapse && !style.treeBranch)
2530          x += 15;
2531       for(field = fields.first; field; field = field.next)
2532       {
2533          int width = field.width + EXTRA_SPACE;
2534          field.x = x;
2535          x += width;
2536       }
2537       width = x;
2538
2539       SetScrollArea(
2540          width,
2541          (rowCount * rowHeight) + 
2542          ((style.header) ? rowHeight : 0) -
2543          ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
2544
2545       for(field = fields.first; field; field = field.next)
2546       {
2547          int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2548             clientSize.w - field.x : (field.width + EXTRA_SPACE);
2549          if(style.header && field.headButton)
2550          {
2551             showEndBevel = true;
2552             if(width > 0)
2553             {
2554                field.headButton.position = { field.x, 0 };
2555                field.headButton.size = { width, rowHeight };
2556                field.headButton.visible = true;
2557             }
2558             else
2559                field.headButton.visible = false;
2560          }
2561       }
2562
2563       if(!style.fillLastField && showEndBevel && endBevel)
2564       {
2565          endBevel.position = { x, 0 };
2566          endBevel.size = { clientSize.w + 2 - x, rowHeight };
2567          endBevel.visible = true;
2568       }
2569       else if(endBevel)
2570          endBevel.visible = false;
2571
2572       if(style.alwaysEdit && editData && editData.visible)
2573       {
2574          HideEditBox(true, false, true);
2575       }
2576    }
2577
2578    void AdaptToFieldWidth(DataField field, bool doScroll)
2579    {
2580       OnResize(clientSize.w, clientSize.h);
2581
2582       // Scroll appropriately
2583       if(doScroll)
2584       {
2585          if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2586                  field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2587          {
2588             SetScrollPosition(field.x + field.width + EXTRA_SPACE - clientSize.w, scroll.y);
2589          }
2590          else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2591                  field.x < scroll.x)
2592             SetScrollPosition(field.x, scroll.y);
2593       }
2594       Update(null);
2595    }
2596
2597    bool HeaderPushed(Button control, int x, int y, Modifiers mods)
2598    {
2599       DataField field = (DataField)control.id;
2600       // false: dont destroy edit box
2601       HideEditBox(true, false, true);
2602       if(style.resizable && ((!field && x < RESIZE_BORDER && fields.last) ||
2603          (field && x < RESIZE_BORDER && field.prev) || 
2604          (field && x >= control.clientSize.w - RESIZE_BORDER)))
2605       {
2606          if(!field)
2607             field = fields.last;
2608          else if(x < RESIZE_BORDER && field.prev)
2609             field = field.prev;
2610
2611          resizingField = field;
2612          this.resizeX = x + control.position.x;
2613          this.startWidth = field.width;
2614          this.oldX = x - scroll.x;
2615          return false;
2616       }
2617       else if(field)
2618       {
2619          draggingField = field;
2620          if(style.moveFields)
2621             field.headButton.stayDown = true;
2622          else if(!style.sortable)
2623             return false;
2624       }
2625       else
2626          return false;
2627       return true;
2628    }
2629
2630    bool HeaderMouseMove(Button control, int x, int y, Modifiers mods)
2631    {
2632       if(resizingField)
2633       {
2634          // Resize left
2635          DataField field = resizingField;
2636
2637          x += control.position.x;
2638
2639          // Tweak to prevent shrinking field if we're actually moving right
2640          if(x - scroll.x > this.oldX && 
2641             this.startWidth + x - this.resizeX < field.width)
2642          {
2643             this.oldX = x - scroll.x;
2644             return true;
2645          }
2646          this.oldX = x - scroll.x;
2647
2648          field.width = this.startWidth + x - this.resizeX;
2649          field.width = Max(field.width, - EXTRA_SPACE);
2650
2651          AdaptToFieldWidth(field, true);
2652       }
2653       else if(draggingField)
2654       {
2655          x += control.position.x;
2656          if(style.moveFields)
2657          {
2658             DataField field = fields.last;
2659             int fieldX = 0;
2660             for(field = fields.first; field; field = field.next)
2661             {
2662                fieldX += ((field.width || style.resizable) ? 
2663                   field.width : clientSize.w) + EXTRA_SPACE;
2664                if(fieldX > x) 
2665                   break;
2666             }
2667             if(draggingField == field)
2668             {
2669                // Reset scroll position
2670                if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2671                        field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2672                {
2673                   SetScrollPosition(
2674                      field.x + field.width + EXTRA_SPACE - clientSize.w, 
2675                      scroll.y);
2676                }
2677                else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2678                        field.x < scroll.x)
2679                   SetScrollPosition(field.x, scroll.y);
2680                field = null;
2681             }
2682             if(this.dropField != field)
2683             {
2684                this.dropField = field;
2685                if(field)
2686                {
2687                   int position = field.x;
2688                   // Moving right
2689                   if(draggingField.x < position)
2690                   {
2691                      position += field.width + EXTRA_SPACE - clientSize.w;
2692                      if(position > scroll.x)
2693                         SetScrollPosition(position, scroll.y);
2694                   }
2695                   // Moving Left
2696                   else
2697                   {
2698                      if(position < scroll.x)
2699                         SetScrollPosition(position, scroll.y);
2700                   }
2701
2702                   this.movingFields = true;
2703                }
2704                Update(null);
2705             }
2706          }
2707       }
2708       else if(style.resizable)
2709       {
2710          DataField field = (DataField)control.id;
2711          if(field)
2712          {
2713             if(x < RESIZE_BORDER && field.prev)
2714                control.cursor = guiApp.GetCursor(sizeWE);
2715             else if(x >= control.clientSize.w - RESIZE_BORDER)
2716                control.cursor = guiApp.GetCursor(sizeWE);
2717             else
2718                control.cursor = null;
2719          }
2720          else
2721          {
2722             if(x < RESIZE_BORDER && fields.last)
2723                control.cursor = guiApp.GetCursor(sizeWE);
2724             else
2725                control.cursor = null;
2726          }
2727       }
2728       return true;
2729    }
2730
2731    bool HeaderReleased(Button control, int x, int y, Modifiers mods)
2732    {
2733       if(resizingField)
2734       {
2735          NotifyResized(master, this, resizingField, mods);
2736          resizingField = null;
2737       }
2738
2739       if(draggingField)
2740       {
2741          bool result = true;
2742          
2743          if(style.moveFields)
2744          {
2745             if(dropField)
2746             {
2747                // Find which field
2748                DataField switchField = fields.last;
2749                DataField field;
2750                int fieldX = 0;
2751
2752                x += draggingField.x;
2753                for(field = fields.first; field; field = field.next)
2754                {
2755                   fieldX += ((field.width || style.resizable) ? 
2756                      field.width : clientSize.w) + EXTRA_SPACE;
2757                   if(fieldX > x) 
2758                   {
2759                      switchField = field;
2760                      break;
2761                   }
2762                }
2763                if(switchField && draggingField != switchField && this.dropField)
2764                {
2765                   for(field = fields.first; field; field = field.next)
2766                   {
2767                      if(field == switchField || field == draggingField)
2768                         break;
2769                   }
2770
2771                   // Switch field first: move before
2772                   if(field == switchField)
2773                      draggingField.Move(switchField.prev);
2774                   // Dragged field first: move after
2775                   else
2776                      draggingField.Move(switchField);
2777
2778                   NotifyMovedField(master, this, draggingField, mods);
2779                }
2780                draggingField.headButton.stayDown = false;
2781             }
2782             if(movingFields)
2783                result = false;
2784             dropField = null;
2785             movingFields = false;
2786          }
2787          draggingField = null;
2788          return result;
2789       }
2790       return true;
2791    }
2792
2793    bool HeaderClicked(Button control, int x, int y, Modifiers mods)
2794    {
2795       if(style.header && !this.dropField && style.sortable)
2796       {
2797          DataField field = (DataField)control.id;
2798          if(sortField == field)
2799             field.sortOrder *= -1;
2800          else
2801             sortField = field;
2802          if(field)
2803          {
2804             Sort(sortField, field.sortOrder);
2805             NotifySort(master, this, field, mods);
2806          }
2807       }
2808       return true;
2809    }
2810
2811    watch(visible)
2812    {
2813       if(style.freeSelect)
2814       {
2815          if(!visible)
2816          {
2817             ReleaseCapture();
2818             this.rolledOver = this.dragging = false;
2819          }
2820          /*else
2821             Capture();*/
2822       }
2823    };
2824
2825    bool OnLoadGraphics()
2826    {
2827       display.FontExtent(fontObject, "W", 1, null, &fontH); 
2828       if(!style.heightSet)
2829       {
2830          rowHeight = Max(fontH + 2, 16) + (style.alwaysEdit ? 1 : 0);
2831          SetScrollLineStep(8, rowHeight);
2832       }
2833       return true;
2834    }
2835
2836    void OnApplyGraphics()
2837    {
2838       SetScrollLineStep(8, rowHeight);
2839       if(style.header)
2840       {
2841          DataField field;
2842          for(field = fields.first; field; field = field.next)
2843          {
2844             if(field.headButton)
2845             {
2846                field.headButton.bevel = (!guiApp.textMode && !style.clearHeader);
2847                if(guiApp.textMode)
2848                   field.headButton.background = Color { 0, 170, 0 };
2849             }
2850          }
2851       }
2852       OnResize(clientSize.w, clientSize.h);
2853    }
2854
2855    bool OnResizing(int *w, int *h)
2856    {
2857       if(rows.first)
2858       {
2859          if(!initSize.w && (!anchor.left.type || !anchor.right.type) /**w*/)
2860          {
2861             // Use widest item
2862             DataRow row;
2863             int maxWidth = 0;
2864             Font font = fontObject;
2865             Font boldFont = this.boldFont.font;
2866             Display display = this.display;
2867             
2868             for(row = rows.first; row; row = row.GetNextRow())
2869             {
2870                Bitmap icon = row.icon ? row.icon.bitmap : null;
2871                int x = -scroll.x + EXTRA_SPACE / 2-1;
2872                DataField field;
2873                int indent = 0;
2874                DataRow parent;
2875                for(parent = row.parent; parent; parent = parent.parent)
2876                {
2877                   if(!parent.header)
2878                   {
2879                      if(style.treeBranch)
2880                         indent += 20;
2881                      else
2882                         indent += 15;
2883                   }
2884                }
2885                if(style.rootCollapse) indent += 20;
2886                x += indent;
2887                if(style.collapse && !(style.treeBranch)) x += 15;
2888                if(icon)
2889                   x += 20;
2890
2891                // Compute the rows size
2892                for(field = fields.first; field; field = field.next)
2893                {
2894                   if(((style.resizable && (!(style.alwaysEdit) || field.next)) || field.width) && !row.header)
2895                      x += field.width - (field.prev ? 0 : indent);
2896                   else
2897                   {
2898                      ListBoxCell cell;
2899                      uint index;
2900                      for(index = 0, cell = row.cells.first; index != field.index; index++, cell = cell.next);
2901
2902                      // Should always be as many cells in the row as fields in the listbox
2903                      if(cell && cell.isSet && field.dataType)
2904                      {
2905                         Bitmap icon = null;
2906                         char tempString[1024];
2907                         char * string;
2908                         int tw, th;
2909                         if(field.dataType.type == 0 normalClass || field.dataType.type == noHeadClass)
2910                            string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, cell.data[0], tempString, field.userData, null);
2911                         else
2912                            string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, cell.data, tempString, field.userData, null);
2913                         /* GCC-4.4 Bug!
2914                         if(!string) string = "";
2915                         display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
2916                         */
2917                         if(string)
2918                            display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
2919                         else
2920                            display.FontExtent(row.header ? boldFont : font, "", 0, &tw, &th);
2921                         x += tw;
2922                      }
2923                      if(row.header) break;
2924                   }
2925                   x += EXTRA_SPACE;
2926                }
2927                maxWidth = Max(maxWidth, x);
2928             }
2929             *w = maxWidth;
2930          }
2931          if(!*h)
2932          {
2933             *h = Min(this.maxShown, this.rowCount) * rowHeight;
2934          }
2935       }
2936       else
2937       {
2938          if(!*w) *w = rowHeight * 5;
2939          if(!*h) *h = rowHeight * 5;
2940       }
2941       return true;
2942    }
2943
2944    watch(font)
2945    {
2946       FontResource font = this.font;
2947       FontResource boldFont
2948       {
2949          faceName = font.faceName, size = font.size, bold = true
2950       };
2951       AddResource(boldFont);
2952       RemoveResource(this.boldFont);
2953       this.boldFont = boldFont;
2954
2955       OnLoadGraphics();
2956
2957       SetInitSize(initSize);
2958    };
2959
2960    bool OnMouseMove(int x, int y, Modifiers mods)
2961    {
2962       bool isTimer = false;
2963       int realX = x, realY = y;
2964
2965       if(insideNotifySelect) return true;
2966
2967       if(style.alwaysEdit && style.resizable &&
2968          resizingField && !(mods.isSideEffect))
2969       {
2970         // Resize left
2971          DataField field = resizingField;
2972          field.width = this.startWidth + x - this.resizeX;
2973          field.width = Max(field.width, - EXTRA_SPACE);
2974
2975          AdaptToFieldWidth(field, true);
2976       }
2977
2978       cursor = null;
2979       if(style.alwaysEdit && style.resizable)
2980       {
2981          int vx = -scroll.x - 1;
2982          DataField field;
2983
2984          if(style.collapse && !(style.treeBranch))
2985             vx += 15;
2986
2987          for(field = fields.first; field; field = field.next)
2988          {
2989             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2990                clientSize.w - field.x : (field.width + EXTRA_SPACE);
2991
2992             if(field.prev)
2993             {
2994                if(Abs(x - vx) < 2)
2995                {
2996                   cursor = guiApp.GetCursor(sizeWE);
2997                   break;
2998                }
2999             }
3000             vx += width + EXTRA_SPACE;
3001          }
3002       }
3003
3004       if((editData && editData.visible) || (style.alwaysEdit))
3005          return true;
3006       
3007       if(x == MAXINT && y == MAXINT)
3008       {
3009          x = this.mouseX;
3010          y = this.mouseY;
3011          isTimer = true;
3012       }
3013
3014       // ADDED THIS CHECK FOR FieldDropBox LEAKS
3015       if(/*!mods.isSideEffect && */(this.rolledOver || !this.dragging))
3016       {
3017          int rowY = (style.header) ? rowHeight : 0;
3018          DataRow row = null;
3019          int rowIndex;
3020
3021          mouseX = x;
3022          mouseY = y;
3023
3024          if(this.dragging &&
3025             ((vertScroll && vertScroll.visible && 
3026              (y < 0 || y >= clientSize.h)) ||
3027              (horzScroll && horzScroll.visible && 
3028              (x < 0 || x >= clientSize.w))))
3029          {
3030             timer.Start();
3031             if(isTimer)
3032             {
3033                if(vertScroll && vertScroll.visible && 
3034                   (y < 0 || y >= clientSize.h))
3035                   vertScroll.Action((y<0)?up:down, 0, 0);
3036                if(horzScroll && horzScroll.visible && 
3037                   (x < 0 || x >= clientSize.w))
3038                   horzScroll.Action((x<0)?up:down, 0, 0);
3039             }
3040          }
3041          else
3042             timer.Stop();
3043
3044          // This must be done after the scrolling took place
3045          rowIndex = firstRowShown ? firstRowShown.index : -1;
3046          y = Max(y, 0);
3047          y = Min(y, clientSize.h-1);
3048          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3049          {
3050             rowY += rowHeight;
3051             if(rowY > y)
3052             {
3053                break;
3054             }
3055          }
3056
3057          if(row && currentRow != row)
3058          {
3059             if(this.dragRow && style.moveRows)
3060             {
3061                if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3062                   rowIndex = -1;
3063                else if(style.multiSelect)
3064                {
3065                   DataRow thisRow;
3066                   for(thisRow = rows.first; thisRow; thisRow = thisRow.GetNextRow())
3067                      if((thisRow.selectedFlag == selected || thisRow.selectedFlag == tempSelected) || 
3068                         thisRow == row)
3069                         break;
3070                   if(thisRow != row)
3071                      rowIndex++;
3072                }
3073                if(this.dropIndex != rowIndex)
3074                {
3075                   this.dropIndex = rowIndex;
3076                   this.editRow = null;
3077                   Update(null);
3078                   this.movedRow = true;
3079                }
3080             }
3081             else if((style.freeSelect  || this.dragging) && ((realX>= 0 && realY >= 0 && realX< clientSize.w && realY < clientSize.h) || this.rolledOver))
3082             {
3083                if(!(style.multiSelect))
3084                {
3085                   if(currentRow)currentRow.selectedFlag = unselected;
3086                   if(row)row.selectedFlag = selected;
3087                }
3088                currentRow = row;
3089
3090                if(style.multiSelect)
3091                {
3092                   DataRow selRow;
3093
3094                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3095                   {
3096                      if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3097                      else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3098                   }
3099
3100                   if(rowIndex >= clickedRow.index)
3101                   {
3102                      for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3103                      {
3104                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3105                         if(selRow == row)
3106                            break;
3107                      }
3108                   }
3109                   else
3110                   {
3111                      for(selRow = row; selRow; selRow = selRow.GetNextRow())
3112                      {
3113                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3114                         if(selRow == clickedRow)
3115                            break;
3116                      }
3117                   }
3118                }
3119                Update(null);
3120                if(style.freeSelect)
3121                   NotifyHighlight(master, this, currentRow, mods);
3122                else
3123                {
3124                   insideNotifySelect = true;
3125                   NotifySelect(master, this, currentRow, mods);
3126                   insideNotifySelect = false;
3127                }
3128
3129                if(style.alwaysEdit && currentRow)
3130                   currentRow.Edit(currentField);
3131             }
3132          }
3133       }
3134       return true;
3135    }
3136
3137    bool OnMouseOver(int x, int y, Modifiers mods)
3138    {
3139       if(this.dragging)
3140          this.rolledOver = true;
3141       return true;
3142    }
3143
3144    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
3145    {
3146       // TOCHECK: WAS THIS MISSING ? CHECK FOR SLOWDOWN
3147       if(!active) Update(null);
3148
3149       if(!active && (!swap || !swap.isModal))
3150       {
3151          // true: destroy edit box
3152          HideEditBox(true, true, false);
3153       }
3154       else if(!swap || !swap.isModal)
3155       {
3156          // Bring back edit box
3157          if(currentRow && style.alwaysEdit)
3158          {
3159             currentRow.Edit(currentField ? currentField : null);
3160          }
3161          Update(null);
3162       }
3163       return true; //NotifyActivate(master, this, active, swap, 0);
3164    }
3165
3166
3167    bool OnButtonDown(int x, int y, Modifiers mods, bool right)
3168    {
3169       bool result = true;
3170       // Check to see if we're dragging the vertical divider
3171       if(style.alwaysEdit && style.resizable && !right)
3172       {
3173          int vx = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
3174          DataField field;
3175
3176          if(style.collapse && !(style.treeBranch))
3177             vx += 15;
3178
3179          for(field = fields.first; field; field = field.next)
3180          {
3181             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3182                clientSize.w - field.x : (field.width + EXTRA_SPACE);
3183
3184             if(field.prev)
3185             {
3186                if(Abs(x - vx) < 2)
3187                {
3188                   resizingField = field.prev;
3189                   this.resizeX = x;
3190                   this.startWidth = resizingField.width;
3191                   Capture();
3192                   SetMouseRangeToClient();
3193                   break;
3194                }
3195             }
3196             vx += width + EXTRA_SPACE;
3197          }
3198       }
3199
3200       if(!(style.freeSelect))
3201       {
3202          int rowY = (style.header) ? rowHeight : 0;
3203          DataRow row = null;
3204          int rowIndex = firstRowShown ? firstRowShown.index : -1;
3205          DataRow previousRow = currentRow;
3206          DataRow newCurrentRow = null;
3207          DataField newCurrentField = null;
3208          bool moveMultiple = false;
3209          int numSelected = 0;
3210          int rowStart = -scroll.x;
3211
3212          if(style.multiSelect)
3213          {
3214             if(!right)
3215             {
3216                DataRow row;
3217                if(!(mods.shift))
3218                {
3219                   for(row = rows.first; row; row = row.GetNextRow())
3220                   {
3221                      if(row.selectedFlag == tempSelected)
3222                         row.selectedFlag = selected;
3223                      else if(row.selectedFlag == tempUnselected)
3224                         row.selectedFlag = unselected;
3225                      if(row.selectedFlag == selected)
3226                         numSelected++;
3227                   }
3228                }
3229             }
3230          }
3231
3232          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3233          {
3234             rowY += rowHeight;
3235             if(rowY > y || (style.multiSelect && !row.GetNextRow())) 
3236             {
3237                int plusIndent = 0;
3238                if(style.treeBranch)
3239                {
3240                   DataRow parent;
3241                   for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3242                      if(!parent.header)
3243                         plusIndent += 20;
3244                   plusIndent += 4;
3245                }
3246
3247                /*    THIS WAS TOO STRICT:
3248                if(style.collapse && row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) &&
3249                   (x >= rowStart + 3 + plusIndent && y >= rowY - rowHeight + PLUSY && x <= rowStart + 11 + plusIndent && y <= rowY - rowHeight + PLUSY + 8))
3250                */
3251                if(style.collapse && 
3252                   (x >= rowStart && y >= rowY - rowHeight && x <= rowStart + 18 + plusIndent && y <= rowY + rowHeight-1))
3253                {
3254                   if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) && x >= plusIndent)
3255                      row.collapsed = !row.collapsed;
3256                   return false;
3257                }
3258                else
3259                {
3260                   if(rowY > y)
3261                   {
3262                      newCurrentRow = row;
3263                   }
3264                   if(style.multiSelect)
3265                   {
3266                      // Deselect everything if user didn't clicked on a row
3267                      if(y >= rowY)
3268                      {
3269                         DataRow selRow;
3270                         for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3271                            selRow.selectedFlag = unselected;
3272                         clickedRow = row;
3273                         //this.clickedRowIndex = rowIndex;
3274                      }
3275                      else if(style.moveRows && !(mods.shift) && 
3276                         (row.selectedFlag == selected || row.selectedFlag == tempSelected) &&
3277                         !right && !(mods.isActivate))
3278                         moveMultiple = true;
3279                      else
3280                      {
3281                         DataRow selRow;
3282                         if(right)
3283                         {
3284                            if(row.selectedFlag == tempUnselected || row.selectedFlag == unselected)
3285                            {
3286                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3287                                  selRow.selectedFlag = unselected;
3288                               row.selectedFlag = selected;
3289                            }
3290                            clickedRow = row;
3291                            //this.clickedRowIndex = rowIndex;
3292                         }
3293                         else
3294                         {
3295                            if(!(mods.ctrl))
3296                            {
3297                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3298                                  selRow.selectedFlag = unselected;
3299                            }
3300                            else
3301                            {
3302                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3303                               {
3304                                  if(selRow != clickedRow)
3305                                  {
3306                                     if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3307                                     else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3308                                  }
3309                               }
3310                            }
3311
3312                            if(mods.shift)
3313                            {
3314                               if(rowIndex >= clickedRow.index)
3315                               {
3316                                  for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3317                                  {
3318                                     if(mods.ctrl)
3319                                     {
3320                                        if(selRow != clickedRow)
3321                                        {
3322                                           if(selRow.selectedFlag) 
3323                                              selRow.selectedFlag = tempUnselected;
3324                                           else
3325                                              selRow.selectedFlag = tempSelected;
3326                                        }
3327                                     }
3328                                     else
3329                                        selRow.selectedFlag = selected;
3330                                     if(selRow == row)
3331                                        break;
3332                                  }
3333                               }
3334                               else
3335                               {
3336                                  for(selRow = row; selRow; selRow = selRow.GetNextRow())
3337                                  {
3338                                     if(mods.ctrl)
3339                                     {
3340                                        if(selRow != clickedRow)
3341                                        {
3342                                           if(selRow.selectedFlag)
3343                                              selRow.selectedFlag = tempUnselected;
3344                                           else
3345                                              selRow.selectedFlag = tempSelected;
3346                                        }
3347                                     }
3348                                     else
3349                                        selRow.selectedFlag = selected;
3350                                     if(selRow == clickedRow)
3351                                        break;
3352                                  }
3353                               }
3354                            }
3355                            else
3356                            {
3357                               if(mods.ctrl)
3358                               {
3359                                  if(row.selectedFlag)
3360                                     row.selectedFlag = tempUnselected;
3361                                  else row.selectedFlag = tempSelected;
3362                               }
3363                               else
3364                                  row.selectedFlag = tempSelected;
3365                               clickedRow = row;
3366                               //this.clickedRowIndex = rowIndex;
3367                            }
3368                         }
3369                      }
3370                   }
3371                }
3372                break;
3373             }
3374          }
3375
3376          // true: destroy edit box
3377          if(newCurrentRow)
3378          {
3379             incref newCurrentRow;
3380          }
3381
3382          if(currentRow != newCurrentRow)
3383             HideEditBox(true, true, false);
3384
3385          if(newCurrentRow)
3386          {
3387             if(newCurrentRow._refCount <= 1)
3388                delete newCurrentRow;
3389             else
3390                newCurrentRow._refCount--;
3391          }
3392
3393          if(newCurrentRow)
3394          {
3395             if(!(style.multiSelect))
3396             {
3397                if(currentRow) currentRow.selectedFlag = unselected;
3398                if(newCurrentRow) newCurrentRow.selectedFlag = selected;
3399             }
3400          }
3401
3402          if(currentRow != newCurrentRow)
3403          {
3404             /*
3405             // true: destroy edit box
3406             if(newCurrentRow)
3407             {
3408                //incref newCurrentRow;
3409                incref newCurrentRow;
3410             }
3411
3412             HideEditBox(true, true, false);
3413             */
3414
3415             if(newCurrentRow)
3416             {
3417                int headerSize = ((style.header) ? rowHeight : 0);
3418                int height = clientSize.h + 1 - headerSize;
3419
3420                /*if(newCurrentRow._refCount <= 1)
3421                   delete newCurrentRow;
3422                else
3423                {
3424                   newCurrentRow._refCount--;
3425                   //newCurrentRow._refCount--;
3426                }
3427                */
3428                currentRow = newCurrentRow;
3429
3430                if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
3431                   SetScrollPosition(scroll.x,
3432                      currentRow.index * rowHeight - height + rowHeight);
3433                else if(!currentRow || currentRow.index * rowHeight < scroll.y)
3434                {
3435                   int line = currentRow ? currentRow.index * rowHeight : 0;
3436                   //SNAPUP(line, rowHeight);
3437                   SetScrollPosition(scroll.x, line);
3438                }
3439
3440                // GO THROUGH SetCurrentRow eventually?
3441                // SetCurrentRow(newCurrentRow, true);
3442             }
3443          }
3444
3445          if(style.freeSelect)
3446             NotifyHighlight(master, this, currentRow, mods);
3447          else if((moveMultiple || (!(style.multiSelect) && previousRow == currentRow)) && 
3448             newCurrentRow && !(mods.shift))
3449          {
3450             if(!right)
3451             {
3452                if(!(mods.isActivate))
3453                {
3454                   if(style.moveRows)
3455                   {
3456                      this.dragRow = currentRow;
3457                      this.dropIndex = -1;
3458                      this.movedRow = false;
3459                   }
3460                   if(editData && editData.visible && style.alwaysEdit)
3461                   {
3462                      DataField field;
3463                      int sx = -scroll.x;
3464                      int indent = 0;
3465                      DataRow parent;
3466
3467                      if(style.collapse && !style.treeBranch)
3468                         sx += 15;
3469
3470                      for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3471                         if(!parent.header)
3472                            indent += (style.treeBranch) ? 20 : 15;
3473                      sx += indent;
3474
3475                      for(field = fields.first; field; field = field.next)
3476                      {
3477                         int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3478                            clientSize.w - field.x : (field.width + EXTRA_SPACE);
3479
3480                         if(!field.prev) width -= indent;
3481                         if(x >= sx && x < sx + width)
3482                            break;                     
3483                         sx += width;
3484                      }
3485                      if(field == currentField)
3486                         editData.Deactivate();
3487                      else
3488                      {
3489                         currentRow.Edit(field);                     
3490                         editData.Activate();
3491                      }
3492                   }
3493                   else if(!(mods.ctrl) && numSelected <= 1)
3494                      this.editRow = currentRow;
3495
3496                   if(style.noDragging && newCurrentRow)
3497                     NotifyReclick(master, this, newCurrentRow, mods);
3498                }
3499                else
3500                {
3501                   // If the user clicked exactly on the edited field,
3502                   // activate it
3503                   if(editData && editData.visible && newCurrentRow)
3504                   {
3505                      DataField field, whichField;
3506                      int sx = -scroll.x;
3507                      int indent = 0;
3508
3509                      if(style.collapse && !(style.treeBranch))
3510                         sx += 15;
3511                   
3512                      whichField = currentField;
3513
3514                      {
3515                         DataRow parent;
3516                         for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3517                            if(!parent.header)
3518                               indent += (style.treeBranch) ? 20 : 15;
3519                         sx += indent;
3520                      }
3521
3522                      for(field = fields.first; field; field = field.next)
3523                      {
3524                         int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3525                            clientSize.w - field.x : (field.width + EXTRA_SPACE);
3526                         if(!field.prev) width -= indent;
3527                         if(x >= sx && x < sx + width && newCurrentRow)
3528                            break;
3529                         sx += width;
3530                      }
3531
3532                      if(field) //x >= sx && x < sx + width && newCurrentRow)
3533                      {
3534                         if(field == currentField)
3535                            editData.Activate();
3536                         /*else
3537                            newCurrentRow.Edit(currentField);*/
3538                      }
3539                      else if(style.noDragging && newCurrentRow)
3540                        NotifyReclick(master, this, newCurrentRow, mods);
3541                   }
3542                   else if(style.noDragging && newCurrentRow)
3543                     NotifyReclick(master, this, newCurrentRow, mods);
3544                }
3545             }
3546          }
3547          else
3548          {
3549             result = NotifySelect(master, this, 
3550                currentRow ? currentRow : null, mods);
3551             if(result && style.alwaysEdit && currentRow)
3552             {
3553                if(newCurrentRow)
3554                {
3555                   DataField field = null;
3556                   int sx = -scroll.x;
3557                   int indent = 0;
3558                   DataRow parent;
3559
3560                   if(style.collapse && !style.treeBranch)
3561                      sx += 15;
3562
3563                   for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3564                      if(!parent.header)
3565                         indent += (style.treeBranch) ? 20 : 15;
3566                   sx += indent;
3567
3568                   for(field = fields.first; field; field = field.next)
3569                   {
3570                      int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3571                         clientSize.w - field.x : (field.width + EXTRA_SPACE);
3572
3573                      if(!field.prev) width -= indent;
3574                      if(x >= sx && x < sx + width)
3575                      {
3576                         currentField = field;
3577                         break;
3578                      }
3579                      sx += width;
3580                   }
3581                }
3582                currentRow.Edit(currentField);
3583
3584                // If the user clicked exactly on the edited field,
3585                // activate it
3586                if(editData && editData.visible && newCurrentRow)
3587                {
3588                   if(currentField)
3589                   {
3590                      editData.Activate();
3591                   }
3592                   else if(style.noDragging && newCurrentRow)
3593                      NotifyReclick(master, this, newCurrentRow, mods);
3594                }
3595             }
3596             else if(style.noDragging && newCurrentRow)
3597               NotifyReclick(master, this, newCurrentRow, mods);
3598          }
3599       }
3600       /*
3601          For drop box to capture...
3602       else
3603       {
3604          if(x < 0 || y < 0 || x >= clientSize.w || y >= clientSize.h)
3605          {
3606             bool goOn = true;
3607             master.parent.Activate();
3608             Update(null);
3609             ReleaseCapture();
3610             return true;
3611          }
3612       }
3613       */
3614       if(result)
3615       {
3616          if(!style.noDragging)
3617          {
3618             this.dragging = true;
3619             Capture();
3620          }
3621          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
3622             this.rolledOver = true;
3623          Update(null);
3624       }
3625       else
3626       {
3627          this.dragging = false;
3628          OnLeftButtonUp(x, y, mods);
3629       }
3630       return result;
3631    }
3632
3633    bool OnLeftButtonDown(int x, int y, Modifiers mods)
3634    {
3635       return OnButtonDown(x,y, mods, false);
3636    }
3637
3638    bool OnLeftButtonUp(int x, int y, Modifiers mods)
3639    {
3640       if(resizingField && style.alwaysEdit)
3641       {
3642          Window::FreeMouseRange();
3643          ReleaseCapture();
3644          resizingField = null;
3645       }
3646
3647       if(dragRow || editRow)
3648       {
3649          DataRow row, switchRow = rows.last;
3650          int rowY = (style.header) ? rowHeight : 0;
3651          for(row = firstRowShown; row; row = row.GetNextRow())
3652          {
3653             rowY += rowHeight;
3654             if(rowY > y) 
3655             {
3656                switchRow = row;
3657                break;
3658             }
3659          }
3660          if(this.editRow == switchRow && y >= 0)
3661          {
3662             // Find which field
3663             DataField field;
3664             int fieldX = 0;
3665             for(field = fields.first; field; field = field.next)
3666             {
3667                int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3668                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
3669                fieldX += width;
3670
3671                if(fieldX > x + scroll.x) 
3672                   break;
3673             }
3674             
3675             if(field && field.editable)
3676             {
3677                // true: destroy edit box
3678                HideEditBox(true, true, false);
3679                PopupEditBox(field, false);
3680             }
3681             else if(!style.noDragging)
3682                NotifyReclick(master, this, currentRow, mods);
3683          }
3684          else if(style.moveRows && switchRow)
3685          {
3686             if(this.dragRow == switchRow && this.movedRow == false)
3687             {
3688                DataRow row = this.dragRow;
3689                DataRow selRow;
3690                if(!(mods.ctrl))
3691                {
3692                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3693                      selRow.selectedFlag = unselected;
3694                }
3695                else
3696                {
3697                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3698                   {
3699                      if(selRow != clickedRow)
3700                      {
3701                         if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3702                         else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3703                      }
3704                   }
3705                }
3706                if(mods.ctrl)
3707                {
3708                   if(row.selectedFlag)
3709                      row.selectedFlag = tempUnselected;
3710                   else row.selectedFlag = tempSelected;
3711                }
3712                else
3713                   row.selectedFlag = tempSelected;
3714                clickedRow = row;
3715             }
3716             else
3717             { 
3718                if(style.multiSelect)
3719                {
3720                   if(!switchRow.selectedFlag)
3721                   {
3722                      bool foundSwitch = false;
3723                      bool after = false;
3724                      DataRow next;
3725                      DataRow afterRow = switchRow.prev;
3726                      for(row = rows.first; row; row = next)
3727                      {
3728                         next = row.GetNextRow();
3729                         if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3730                         {
3731                            if(!foundSwitch && !after)
3732                            {
3733                               after = true;
3734                               afterRow = switchRow;
3735                            }
3736                            if(!after || !(row.selectedFlag == selected || row.selectedFlag == tempSelected) ||
3737                               !foundSwitch)
3738                            {
3739                               row.Move(afterRow);
3740                               afterRow = row;
3741                            }
3742                         }
3743                         else if(row == switchRow)
3744                            foundSwitch = true;
3745                      }
3746                   }
3747                }
3748                else
3749                {
3750                   for(row = rows.first; row; row = row.GetNextRow())
3751                   {
3752                      if(row == switchRow || row == this.dragRow)
3753                         break;
3754                   }
3755
3756                   // Switch row first: move before
3757                   if(row == switchRow)
3758                   {
3759                      if(NotifyMove(master, this, switchRow.prev, mods))
3760                         dragRow.Move(switchRow.prev);
3761                   }
3762                   // Dragged row first: move after
3763                   else
3764                   {
3765                      if(NotifyMove(master, this, switchRow, mods))
3766                         dragRow.Move(switchRow);
3767                   }
3768                }
3769             }
3770          }
3771          dragRow = null;
3772          editRow = null;
3773          movedRow = false;
3774          dropIndex = -1;
3775          Update(null);
3776       }
3777
3778       timer.Stop();
3779       if(this.dragging || style.freeSelect)
3780       {
3781          if(this.dragging)
3782          {
3783             this.rolledOver = this.dragging = false;
3784          }
3785          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h && currentRow && style.freeSelect)
3786          {
3787             bool result;
3788             ReleaseCapture();
3789             result = NotifySelect(master, this, currentRow, mods);
3790             if(style.alwaysEdit)
3791                currentRow.Edit(currentField);
3792             return result;
3793
3794          }
3795          // if(!(style.freeSelect))
3796          ReleaseCapture();
3797       }
3798       return true;
3799    }
3800
3801    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
3802    {
3803       int rowStart = -scroll.x;
3804       DataRow row;
3805       int rowY = (style.header) ? rowHeight : 0;
3806       int plusIndent = 0;
3807
3808       OnLeftButtonUp(x,y,mods);
3809       if(style.alwaysEdit)
3810       {
3811          if(!(style.collapse) || x > 15)
3812             if(editData && editData.visible)
3813             {
3814                editData.Activate();
3815                NotifyDoubleClick(master, this, x, y, mods);
3816                return false;
3817             }
3818       }
3819       for(row = firstRowShown; row; row = row.GetNextRow())
3820       {
3821          rowY += rowHeight;
3822          if(rowY > y || (style.multiSelect && !row.GetNextRow())) 
3823          {
3824             if(style.treeBranch)
3825             {
3826                DataRow parent;
3827                for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3828                   if(!parent.header)
3829                      plusIndent += 20;
3830                plusIndent += 4;
3831             }
3832             break;
3833          }
3834       }
3835
3836       if((row && style.collapse && (x >= rowStart && x <= rowStart + 18 + plusIndent)) ||
3837          NotifyDoubleClick(master, this, x, y, mods))
3838       {
3839          if(style.collapse)
3840          {
3841             if(row && row.subRows.first)
3842             {
3843                row.collapsed = !row.collapsed;
3844                return false;
3845             }
3846          }
3847          // We need to return true here so that OnLeftButtonDown can popup the DataBox Editors
3848          return true;
3849       }
3850       return false;
3851    }
3852
3853    bool OnRightButtonDown(int x, int y, Modifiers mods)
3854    {
3855       return OnButtonDown(x,y, mods, true);
3856    }
3857
3858    bool OnRightButtonUp(int x, int y, Modifiers mods)
3859    {
3860       OnLeftButtonUp(x,y,mods);
3861       return NotifyRightClick(master, this, x, y, mods);
3862    }
3863
3864    bool GoToLetter(unichar ch, bool keyHit)
3865    {
3866       bool result = false;
3867       DataField field;
3868       bool checkNextField = true;
3869       int len = keyHit ? 0 : strlen(typedString);
3870
3871       typedString = renew typedString char[len + 2];
3872       typedString[len++] = (char)tolower(ch);         // TODO: FIX UNICODE
3873       typedString[len] = '\0';
3874
3875       for(field = fields.first; field; field = field.next)
3876       {
3877          DataRow startRow = currentRow ? currentRow : rows.first;
3878
3879          if(startRow && field.dataType && field.dataType._vTbl[__ecereVMethodID_class_OnGetString] && ch)
3880          {
3881             DataRow row, next;
3882             bool looped = false;
3883             if(len == 1 && currentRow)
3884                startRow = (next = startRow.GetNextRow(), (next ? next : rows.first));
3885         
3886             for(row = startRow; row != startRow || !looped; next = row.GetNextRow(), row = next ? next : rows.first)
3887             {
3888                void * data = row.GetData(field);
3889                char tempString[1024] = "";
3890                bool needClass = false;
3891                char * string = data ? (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, data, tempString, null, &needClass) : null;
3892
3893                if(string && string[0])
3894                   checkNextField = false;
3895                if(string && string[0] && !strnicmp(string, typedString, len))
3896                {
3897                   if(style.multiSelect)
3898                   {
3899                      DataRow selRow;
3900                      bool foundRow = false;
3901
3902                      //this.clickedRowIndex = 0;
3903                      clickedRow = row;
3904                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3905                      {
3906                         if(selRow == row) foundRow = true;
3907                         selRow.selectedFlag = unselected;
3908                         //if(!foundRow) this.clickedRowIndex++;
3909                      }
3910                      row.selectedFlag = selected;
3911                   }
3912                   SetCurrentRow(row, true);
3913                   result = true;
3914                   break;
3915                }
3916                looped = true;
3917             }
3918             typingTimer.Stop();
3919             if(this.typingTimeOut && !keyHit)
3920                typingTimer.Start();
3921             if(!result || !this.typingTimeOut || keyHit)
3922                typedString[len-1] = '\0';
3923          }
3924          if(!checkNextField || result) break;
3925       }
3926       return result;
3927    }
3928
3929    bool OnKeyDown(Key key, unichar ch)
3930    {
3931       DataField field;
3932
3933       if(key == enter || key == keyPadEnter)
3934       {
3935          if(editData && editData.visible && editData.active)
3936          {
3937             HideEditBox(true, false, false);
3938             return false;
3939          }
3940       }
3941       else if(key == escape)
3942       {
3943          if(resizingField || this.movingFields || (editData && editData.visible) || this.dragRow)
3944          {
3945             if(editData && editData.visible && style.alwaysEdit && !editData.active)
3946                return true;
3947             // false: dont destroy edit box
3948             HideEditBox(false, false, false);
3949             if(resizingField)
3950             {
3951                resizingField.width = this.startWidth;
3952                AdaptToFieldWidth(resizingField, true);
3953                resizingField = null;
3954                ReleaseCapture();
3955             }
3956             this.dragRow = null;
3957             if(this.dragging)
3958             {
3959                this.dragging = false;
3960                ReleaseCapture();
3961             }
3962
3963             this.movingFields = false;
3964             draggingField = null;
3965             Update(null);
3966             return false;
3967          }      
3968       }
3969
3970       if(!currentField || !currentField.editable)            
3971          for(field = fields.first; field; field = field.next)
3972          {
3973             if(field.editable)
3974             {
3975                currentField = field;
3976                break;
3977             }
3978          }
3979       if(key == f2 && currentField && currentField.editable)
3980       {
3981          PopupEditBox(currentField, false);
3982          if(editData && editData.visible)
3983          {
3984             if(style.alwaysEdit)
3985                editData.Activate();
3986             return false;
3987          }
3988       }
3989  
3990       if(!NotifyKeyDown(master, this, currentRow, key, ch))
3991          return false;
3992
3993       // Editable fields...
3994       if(currentField)
3995       {
3996          if(style.alwaysEdit && editData && editData.visible)
3997             return editData.OnKeyDown(key, ch);
3998          return true;   // We want to pick up the OnKeyHit to replace contents, but skip GoToLetter
3999       }
4000       
4001       if(ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, false))
4002       {
4003          /*if(inactive && window.state != Hidden)
4004             NotifyHighlight(master, this, currentRow, 0);
4005          else
4006          {
4007             NotifySelect(master, this, currentRow, 0);
4008          }*/
4009          return false;
4010       }
4011       return true;
4012    }
4013
4014    bool OnKeyHit(Key key, unichar ch)
4015    {
4016       if(key.code == up && key.alt == true && key.ctrl == false && key.shift == false)
4017          return true;
4018
4019       if(!ch && !key.alt && !key.ctrl)
4020       {
4021          key.code = (SmartKey)key.code;
4022       }
4023       if(ch >= 32 && !key.alt && !key.ctrl && ch != 128)
4024       {
4025          DataField field;
4026          if(!currentField || !currentField.editable)
4027             for(field = fields.first; field; field = field.next)
4028             {
4029                if(field.editable)
4030                {
4031                   currentField = field;
4032                   break;
4033                }
4034             }
4035          if(currentField && currentField.editable)
4036          {
4037             if((!editData || !editData.visible) || !editData.active)
4038             {
4039                PopupEditBox(currentField, false);
4040                if(editData && editData.visible)
4041                {
4042                   editData.Activate();
4043                   editData.OnKeyHit(key, ch);
4044                }
4045             }
4046             return false;
4047          }
4048       }
4049
4050       if(!(style.multiSelect) && (key.ctrl))
4051          return true;
4052
4053       if(editData && editData.visible && ch && !key.alt && !key.ctrl && editData.active)
4054          return false;
4055
4056       switch(key.code)
4057       {
4058          case left:
4059             if(style.alwaysEdit)
4060             {
4061                if(currentField)
4062                {
4063                   DataField field;
4064                   for(field = currentField.prev; field; field = field.prev)
4065                   {
4066                      if(field.editable)
4067                      {
4068                         currentField = field;
4069                         HideEditBox(true, true, false);
4070                         PopupEditBox(currentField, false);
4071                         return false;
4072                      }                     
4073                   }
4074                }
4075             }
4076             if(style.collapse && currentRow /*&& !currentField*/)  // THIS PREVENTED COLLAPSING THE PROPERTY SHEET
4077             {
4078                if(currentRow.subRows.first && !currentRow.collapsed)
4079                {
4080                   currentRow.collapsed = true;
4081                }
4082                else if(currentRow.parent)
4083                   SetCurrentRow(currentRow.parent, true);
4084                return false;
4085             }
4086             break;
4087          case right:
4088             if(style.collapse && currentRow && currentRow.subRows.first)
4089             {
4090                if(currentRow.collapsed)
4091                   currentRow.collapsed = false;
4092                else
4093                   SetCurrentRow(currentRow.subRows.first, true);
4094                return false;
4095             }
4096             else if(style.alwaysEdit)
4097             {
4098                if(currentField)
4099                {
4100                   DataField field;
4101                   for(field = currentField.next; field; field = field.next)
4102                   {
4103                      if(field.editable)
4104                      {
4105                         currentField = field;
4106                         HideEditBox(true, true, false);
4107                         PopupEditBox(currentField, false);
4108                         break;
4109                      }                     
4110                   }
4111                }
4112             }
4113             break;
4114          case down: case up:
4115          case pageDown: case pageUp:
4116          case end: case home:
4117          {
4118             int headerSize = ((style.header) ? rowHeight : 0);
4119             int height = clientSize.h + 1 - headerSize;
4120             DataRow oldRow;
4121
4122             // true: destroy edit box
4123             // !!! TESTING true HERE !!!
4124             HideEditBox(true, true, false);
4125             // HideEditBox(false, true, false);
4126             
4127             oldRow = currentRow;
4128
4129             SNAPDOWN(height, rowHeight);
4130             if((!currentRow || key.code == home) && key.code != end)
4131             {
4132                currentRow = rows.first;
4133             }
4134             else
4135             {
4136                DataRow next;
4137                switch(key.code)
4138                {
4139                   case down:
4140                      if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4141                         next = currentRow;
4142                      else
4143                         next = currentRow.GetNextRow();
4144                      if(next)
4145                      {
4146                         currentRow = next;
4147                      }
4148                      break;
4149                   case up:
4150                      if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4151                         next = currentRow;
4152                      else
4153                         next = currentRow.GetPrevRow();
4154
4155                      if(next)
4156                      {
4157                         currentRow = next;
4158                      }
4159                      break;
4160                   case end:
4161                      // TODO: Find very last row
4162                      for(currentRow = rows.last; currentRow && !currentRow.collapsed && currentRow.subRows.last;)
4163                         currentRow = currentRow.subRows.last;
4164                      break;
4165                   case pageUp:
4166                   {
4167                      int c;
4168                      for(c = 0;
4169                      currentRow && (next = currentRow.GetPrevRow()) && c < height / rowHeight;
4170                          c++, currentRow = next);
4171                      break;
4172                   }
4173                   case pageDown:
4174                   {
4175                      int c;
4176                      for(c = 0;
4177                          currentRow && (next = currentRow.GetNextRow()) && c < height / rowHeight;
4178                          c++, currentRow = next);
4179                      break;
4180                   }
4181                }
4182             }
4183             if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
4184                SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
4185             else if(!currentRow || currentRow.index * rowHeight < scroll.y)
4186                SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
4187
4188             if(style.multiSelect)
4189             {
4190                DataRow selRow;
4191
4192                if(!(key.shift) && (key.ctrl))
4193                {
4194                   DataRow row;
4195                   for(row = rows.first; row; row = row.GetNextRow())
4196                   {
4197                      if(row.selectedFlag == tempSelected)
4198                         row.selectedFlag = selected;
4199                      else if(row.selectedFlag == tempUnselected)
4200                         row.selectedFlag = unselected;
4201                   }
4202                }
4203
4204                if(!(key.ctrl))
4205                {
4206                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4207                      selRow.selectedFlag = unselected;
4208                }
4209                else
4210                {
4211                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4212                   {
4213                      if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
4214                      else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
4215                   }
4216                }
4217
4218                if(key.shift)
4219                {
4220                   if(currentRow.index >= clickedRow.index)
4221                   {
4222                      for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
4223                      {
4224                         if(key.ctrl)
4225                         {
4226                            if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4227                         }
4228                         else
4229                            selRow.selectedFlag = selected;
4230                         if(selRow == currentRow)
4231                            break;
4232                      }
4233                   }
4234                   else
4235                   {
4236                      for(selRow = currentRow; selRow; selRow = selRow.GetNextRow())
4237                      {
4238                         if(key.ctrl)
4239                         {
4240                            if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4241                         }
4242                         else
4243                            selRow.selectedFlag = selected;
4244                         if(selRow == clickedRow)
4245                            break;
4246                      }
4247                   }
4248                }
4249                else
4250                {
4251                   if(!(key.ctrl) && currentRow)
4252                   {
4253                      currentRow.selectedFlag = selected;
4254                   }
4255
4256                   clickedRow = currentRow;
4257                }
4258             }
4259             else
4260             {
4261                if(oldRow) oldRow.selectedFlag = unselected;
4262                if(currentRow) currentRow.selectedFlag = selected;
4263             }
4264
4265             if(currentRow)
4266             {
4267                if(style.freeSelect)
4268                   NotifyHighlight(master, this, currentRow, 0);
4269                else
4270                   NotifySelect(master, this, currentRow, 0);
4271
4272                if(style.alwaysEdit && currentRow)
4273                   currentRow.Edit(currentField /*null*/);
4274             }
4275             Update(null);
4276             return false;
4277          }
4278          case space:
4279          {
4280             if(style.multiSelect && currentRow)
4281             {
4282                if(currentRow.selectedFlag)
4283                {
4284                   if(key.ctrl)
4285                      currentRow.selectedFlag = unselected;
4286                }
4287                else
4288                   currentRow.selectedFlag = selected;
4289                Update(null);
4290
4291                if(style.freeSelect)
4292                   NotifyHighlight(master, this, currentRow, 0);
4293                else
4294                   NotifySelect(master, this, currentRow, 0);
4295             }
4296             break;
4297          }
4298       }
4299  
4300       if(!NotifyKeyHit(master, this, currentRow, key, ch))
4301          return false;
4302
4303       if(ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, true))
4304       {
4305          /*if(inactive && window.state != Hidden)
4306             return NotifyHighlight(master, this, currentRow, 0);
4307          else
4308          {
4309             return NotifySelect(master, this, currentRow, 0);
4310          }*/
4311          return false;
4312       }
4313       return true;
4314    }
4315
4316
4317    void OnHScroll(ScrollBarAction action, int position, Key key)
4318    {
4319       Update(null);
4320    }
4321
4322    void OnVScroll(ScrollBarAction action, int position, Key key)
4323    {
4324       int y = 0;
4325       DataRow next;
4326
4327       for(firstRowShown = rows.first; firstRowShown; firstRowShown = next)
4328       {
4329          next = firstRowShown.GetNextRow();
4330          if(y >= position || !next) break;
4331
4332          y += rowHeight;
4333       }
4334       Update(null);
4335    }
4336
4337    OldList fields;
4338    OldList rows;
4339    int numFields;
4340    DataRow firstRowShown;
4341    DataRow clickedRow;
4342    DataRow currentRow;
4343    int width;
4344    DataField sortField;
4345    int rowCount;
4346    int rowHeight;
4347    int fontH;
4348    double typingTimeOut;
4349    char * typedString;
4350
4351    int mouseX, mouseY;
4352
4353    Timer timer
4354    {
4355       delay = 0.1;
4356       userData = this;
4357
4358       bool DelayExpired()
4359       {
4360          Modifiers mods { };
4361          if(guiApp.GetKeyState(shift)) mods.shift = true;
4362          if(guiApp.GetKeyState(alt)) mods.alt = true;
4363          if(guiApp.GetKeyState(control)) mods.ctrl = true;
4364          OnMouseMove(MAXINT, MAXINT, mods);
4365
4366          return true;
4367       }
4368    };
4369
4370    Timer typingTimer
4371    {
4372       delay = 0.5; // typingTimeOut
4373       userData = this;
4374       
4375       bool DelayExpired()
4376       {
4377          typedString[0] = '\0';
4378
4379          // The next line was commented... Why? When commented typing words stops working ( only first character jumps )
4380          typingTimer.Stop();
4381          return true;
4382       }
4383    };
4384
4385    bool dragging, rolledOver;
4386    int numSelections;
4387    Button endBevel;
4388
4389    // For moving rows
4390    DataRow dragRow;
4391    int dropIndex;
4392    bool movedRow;
4393
4394    // For editing fields
4395    DataBox editData;
4396    DataField currentField;
4397    DataRow editRow;
4398
4399    // For moving fields
4400    DataField draggingField, dropField;
4401    bool movingFields;
4402
4403    // For resizing fields
4404    DataField resizingField;
4405    int resizeX, oldX, startWidth;
4406
4407    ListBoxBits style;
4408    FontResource boldFont;
4409    int maxShown;
4410
4411    // Only used for OnMouseMove so far, for avoiding problems with consequential mouse moves
4412    bool insideNotifySelect;
4413    Color selectionColor, selectionText, stippleColor;
4414    stippleColor = 0xFFFFFF80;
4415 };