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