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