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