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