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