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