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