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