ecere/gui/ListBox: AutoSize tweak to consider header width
[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 int 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(int 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(int 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
764          va_start(args, format);
765          vsprintf(string, format, args);
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    int 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 = (uint)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          vsprintf(string, format ? format : "", args);
1456          va_end(args);
1457
1458          row = AddRow();
1459          row.SetData(fields.first, string);
1460          return row;
1461       }
1462       return null;
1463    }
1464
1465    DataRow AddString(char * string)
1466    {
1467       if(this)
1468       {
1469          DataRow row;
1470          row = AddRow();
1471          row.SetData(fields.first, string);
1472          return row;
1473       }
1474       return null;
1475    }
1476
1477    void SelectRow(DataRow row)
1478    {
1479       SetCurrentRow(row, true);
1480    }
1481
1482    void DeleteRow(DataRow row)
1483    {
1484       if(!row) row = currentRow;
1485       if(row)
1486       {
1487          DataRow sub, next, search;
1488          // Trying to move this here (Messed up deleting watches)
1489          //HideEditBox(false, false, true);
1490
1491          // Delete Sub Rows
1492          for(sub = row.subRows.first; sub; sub = next)
1493          {
1494             next = sub.next;
1495             DeleteRow(sub);
1496          }
1497
1498          if(row.parent.IsExpanded())
1499          {
1500             for(search = row.GetNextRow(); search; search = search.GetNextRow())
1501                search.index--;         
1502             this.rowCount--;
1503          }
1504
1505          HideEditBox(false, false, true);
1506
1507          if(row == clickedRow) 
1508          {
1509             clickedRow = row.GetNextRow();
1510             if(!clickedRow)
1511                clickedRow = row.GetPrevRow();
1512          }
1513
1514          if(row == currentRow) 
1515          {
1516             DataRow newCurrentRow = row.GetNextRow();
1517             if(!newCurrentRow)
1518                newCurrentRow = row.GetPrevRow();
1519             SetCurrentRow(newCurrentRow, true);
1520          }
1521
1522          if(row == firstRowShown) 
1523          {
1524             firstRowShown = row.GetPrevRow();
1525             if(!firstRowShown)
1526                firstRowShown = row.GetNextRow();
1527          }
1528
1529          (row.parent ? row.parent.subRows: rows).Remove(row);
1530          delete row;
1531
1532          //HideEditBox(false, false, true);
1533
1534          SetScrollArea(
1535             this.width,
1536             (this.rowCount * rowHeight) + 
1537             ((style.header) ? rowHeight : 0) -
1538             ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1539
1540          modifiedDocument = true;
1541
1542          Update(null);
1543 #ifdef _DEBUG
1544          CheckConsistency();
1545 #endif
1546       }
1547    }
1548
1549    DataRow FindRow(int tag)
1550    {
1551       if(this)
1552       {
1553          DataRow row = null;
1554          for(row = rows.first; row; row = row.next)
1555          {
1556             if(!row.noneRow && row.tag == tag)
1557                break;
1558          }
1559          return row;
1560       }
1561       return null;
1562    }
1563
1564    DataRow FindString(char * searchedString)
1565    {
1566       DataField field;
1567       bool checkNextField = true;
1568       int len = searchedString ? strlen(searchedString) : 0;
1569
1570       for(field = fields.first; field; field = field.next)
1571       {
1572          if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1573          {
1574             DataRow row;
1575             for(row = rows.first; row; row = row.GetNextRow())
1576             {
1577                if(!row.noneRow)
1578                {
1579                   void * data = row.GetData(field);
1580                   char tempString[1024] = "";
1581                   bool needClass = false;
1582                   char * string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, data, tempString, null, &needClass);
1583
1584                   if(string && string[0])
1585                      checkNextField = false;
1586                   if(string && string[0] && !strcmp(string, searchedString))
1587                      return row;
1588                }
1589             }
1590          }
1591          if(!checkNextField) break;
1592       }
1593       return null;
1594    }
1595
1596    DataRow FindSubString(char * subString)
1597    {
1598       DataField field;
1599       bool checkNextField = true;
1600       int len = subString ? strlen(subString) : 0;
1601
1602       if(len)
1603       {
1604          for(field = fields.first; field; field = field.next)
1605          {
1606             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1607             {
1608                DataRow row;
1609                for(row = rows.first; row; row = row.GetNextRow())
1610                {
1611                   if(!row.noneRow)
1612                   {
1613                      void * data = row.GetData(field);
1614                      char tempString[1024] = "";
1615                      bool needClass = false;
1616                      char * string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, data, tempString, null, &needClass);
1617
1618                      if(string && string[0])
1619                         checkNextField = false;
1620                      if(string && string[0] && !strncmp(string, subString, len))
1621                         return row;
1622                   }
1623                }
1624             }
1625             if(!checkNextField) break;
1626          }
1627       }
1628       return null;
1629    }
1630
1631    DataRow FindSubStringi(char * subString)
1632    {
1633       DataField field;
1634       bool checkNextField = true;
1635       int len = subString ? strlen(subString) : 0;
1636       DataRow result = null;
1637       char * bestResult = null;
1638       int bestLen = 0;
1639
1640       if(len)
1641       {
1642          for(field = fields.first; field; field = field.next)
1643          {
1644             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1645             {
1646                DataRow row;
1647                for(row = rows.first; row; row = row.GetNextRow())
1648                {
1649                   if(!row.noneRow)
1650                   {
1651                      void * data = row.GetData(field);
1652                      char tempString[1024] = "";
1653                      bool needClass = false;
1654                      char * string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, data, tempString, null, &needClass);
1655
1656                      if(string && string[0])
1657                         checkNextField = false;
1658                      if(string && string[0])
1659                      {
1660                         int stringLen = strlen(string);
1661
1662                         if(!strnicmp(string, subString, Min(len, stringLen)))
1663                         {
1664                            if(bestLen < Min(len, stringLen))
1665                               bestResult = 0;
1666                            if(!bestResult || strcmpi(string, bestResult) < 0)
1667                            {
1668                               bestLen = Min(len, stringLen);
1669                               bestResult = string;
1670                               result = row;
1671                            }
1672                         }
1673                      }
1674                   }
1675                }
1676             }
1677             if(!checkNextField) break;
1678          }
1679       }
1680       return result;
1681    }
1682
1683    DataRow FindSubStringAfter(DataRow after, char * subString)
1684    {
1685       DataField field;
1686       bool checkNextField = true;
1687       int len = subString ? strlen(subString) : 0;
1688
1689       if(len)
1690       {
1691          for(field = fields.first; field; field = field.next)
1692          {
1693             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1694             {
1695                DataRow row;
1696                for(row = after.GetNextRow(); row && row != after; row = row.GetNextRow(), (!row) ? (row = rows.first) : null)
1697                {
1698                   if(!row.noneRow)
1699                   {
1700                      void * data = row.GetData(field);
1701                      char tempString[1024] = "";
1702                      bool needClass = false;
1703                      char * string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, data, tempString, null, &needClass);
1704
1705                      if(string && string[0])
1706                         checkNextField = false;
1707                      if(string && string[0] && !strncmp(string, subString, len))
1708                         return row;
1709                   }
1710                }
1711             }
1712             if(!checkNextField) break;
1713          }
1714       }
1715       return null;
1716    }
1717
1718    DataRow FindSubRow(int tag)
1719    {
1720       if(this)
1721       {
1722          DataRow row = null;
1723       
1724          for(row = rows.first; row; row = row.next)
1725          {
1726             if(!row.noneRow && row.tag == tag)
1727                break;
1728             if(!row.noneRow && row.subRows.first)
1729             {
1730                DataRow subRow = row.FindSubRow(tag);
1731                if(subRow)
1732                   return subRow;
1733             }
1734          }
1735          return row;
1736       }
1737       return null;
1738    }
1739
1740    void Clear()
1741    {
1742       if(this)
1743       {
1744          Window master = this.master;
1745
1746          HideEditBox(false, true, false);
1747          editData.Destroy(0);
1748                
1749          firstRowShown = currentRow = null;
1750          ClearEx();
1751      
1752          if(master)
1753          {
1754             if(style.freeSelect)
1755                NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
1756             else
1757                NotifySelect(master, this, currentRow ? currentRow : null, 0);
1758          }
1759
1760          if(style.alwaysEdit && currentRow)
1761             currentRow.Edit(currentField);
1762
1763       
1764          this.rowCount = 0;
1765       
1766          SetScrollArea(
1767                this.width,
1768                (this.rowCount * rowHeight) + 
1769                ((style.header) ? rowHeight : 0) -
1770                ((rowHeight && !((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1771          Update(null);
1772       }
1773    }
1774
1775    void Sort(DataField field, int order)
1776    {
1777       if(this)
1778       {
1779          DataRow search;
1780          int headerSize = ((style.header) ? rowHeight : 0);
1781          int height = clientSize.h + 1 - headerSize;
1782
1783          if(!field) field = fields.first;
1784          sortField = field;
1785          field.sortOrder = order ? order : 1;
1786          rows.Sort(DataRow::Compare, field);
1787
1788          for(search = rows.first; search; search = search.next)
1789             search._SortSubRows(field, order);
1790
1791          {
1792             int index = 0;
1793             for(search = rows.first; search; search = search.GetNextRow())
1794                search.index = index++;
1795          }
1796
1797          if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
1798             SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
1799          else if(!currentRow || currentRow.index * rowHeight < scroll.y)
1800             SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
1801
1802          OnVScroll(0, scroll.y, 0);
1803
1804          // SetScrollPosition(0, scroll.y);
1805          // Update(null);
1806       }
1807    }
1808
1809    void StopEditing(bool save)
1810    {
1811       HideEditBox(save, false, true);
1812    }
1813
1814    void GetMultiSelection(OldList list)
1815    {
1816       list.Clear();
1817       if(this && style.multiSelect)
1818       {
1819          DataRow row;
1820
1821          for(row = rows.first; row; row = row.GetNextRow())
1822          {
1823             if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1824             {
1825                list.Add(OldLink { data = row });
1826             }
1827          }
1828       }
1829    }
1830
1831    // Convenience Current Row Methods
1832    void * SetData(DataField field, any_object data)
1833    {
1834       return currentRow.SetData(field, data);
1835    }
1836
1837    any_object GetData(DataField field)
1838    {
1839       return (void *)currentRow.GetData(field);
1840    }
1841
1842    int GetTag()
1843    {
1844       return currentRow ? currentRow.tag : 0;
1845    }
1846
1847 private:
1848    ListBox()
1849    {
1850       DataField defaultField { };
1851       rows.offset = (uint)&((DataRow)0).prev;
1852       fields.offset = (uint)&((DataField)0).prev;
1853       style.fullRowSelect = true;
1854       style.fillLastField = true;
1855       style.expandOnAdd = true;
1856       typingTimeOut = 0.5;
1857       rowHeight = 16;   // Stuff depending on creation and default property checking
1858       maxShown = 10;
1859
1860       defaultField.defaultField = true;
1861
1862       AddField(defaultField);
1863
1864       typedString = new char[1];
1865       typedString[0] = '\0';
1866       dropIndex = -1;
1867       return true;
1868    }
1869
1870    ~ListBox()
1871    {
1872       DataField field;
1873
1874       delete editData;
1875       delete typedString;
1876       delete endBevel;
1877
1878       ClearEx();
1879
1880       while((field = fields.first))
1881       {
1882          // fields.Remove(field);
1883          field.Free();
1884          delete field;
1885       }
1886    }
1887
1888    void ClearEx()
1889    {
1890       DataRow row;
1891       clickedRow = null;
1892       while((row = rows.first))
1893       {
1894          rows.Remove(row);
1895          delete row;
1896       }
1897    }
1898
1899    ListBoxCell GetCell(DataRow * row, DataField * field)
1900    {
1901       ListBoxCell cell = null;
1902       if(!*row) *row = currentRow;
1903       if(*row)
1904       {
1905          if(!*field) *field = this ? currentField : null;
1906          if(!*field && this) 
1907          {
1908             for(*field = fields.first; (*field).index != 0; *field = (*field).next);
1909          }
1910          if(*field)
1911          {
1912             uint index;
1913             if(field->listBox == this)
1914             {
1915                for(index = 0, cell = (*row).cells.first; cell && index != (*field).index; index++, cell = cell.next);
1916             }
1917          }
1918       }
1919       return cell;
1920    }
1921
1922    void HideEditBox(bool save, bool alwaysStopEdit, bool repositionOnly)
1923    {
1924       if(editData && editData.visible)
1925       {
1926          Class dataType = currentField.dataType;
1927          if(save) 
1928             editData.SaveData();
1929          
1930          editData.visible = false;
1931          NotifyEditDone(master, this, currentRow);
1932
1933          // ENSURE DATA BOX IS NOT VISIBLE
1934          editData.visible = false;
1935
1936          if(style.alwaysEdit && !alwaysStopEdit)
1937          {
1938             /*
1939             int height = rowHeight - (style.alwaysEdit ? 1 : 0);
1940             int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
1941             int x = currentField.x;
1942             int width = (!currentField.next && style.fillLastField && (!hasHorzScroll || clientSize.w - currentField.x > currentField.width + EXTRA_SPACE)) ? 
1943                   clientSize.w - currentField.x : (currentField.width + EXTRA_SPACE);
1944
1945             if(!style.alwaysEdit)
1946             {
1947                editData.position = { x, y };
1948                editData.size = { width, height };
1949             }
1950             else
1951             {
1952                editData.position = { x, y - editData.clientStart.y };
1953                editData.size = { width, height + editData.clientStart.y * 2 };
1954             }
1955             editData.visible = true;
1956             if(style.alwaysEdit)
1957                editData.Deactivate();
1958             */
1959             PopupEditBox(currentField, repositionOnly);
1960          }
1961          else
1962             printf("");
1963             
1964          /*else
1965             currentField = null;*/
1966       }
1967    }
1968
1969    void SetCurrentRow(DataRow row, bool notify)
1970    {
1971       if(currentRow != row || (currentRow && currentRow.selectedFlag == unselected))
1972       {
1973          int headerSize = ((style.header) ? rowHeight : 0);
1974          int height = clientSize.h + 1 - headerSize;
1975
1976          // true: destroy edit box
1977          HideEditBox(true, true, false);
1978
1979          if(!(style.multiSelect) && currentRow)
1980             currentRow.selectedFlag = unselected;
1981
1982          currentRow = row;
1983
1984          if(style.multiSelect)
1985          {
1986             DataRow selRow;
1987
1988             for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
1989                selRow.selectedFlag = unselected;
1990             if(currentRow)
1991                currentRow.selectedFlag = selected;
1992
1993             clickedRow = row;
1994          }
1995          else if(currentRow)
1996             currentRow.selectedFlag = selected;
1997
1998          if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
1999             SetScrollPosition(scroll.x,
2000                currentRow.index * rowHeight - height + rowHeight);
2001          else if(!currentRow || currentRow.index * rowHeight < scroll.y)
2002          {
2003             int line = currentRow ? currentRow.index * rowHeight : 0;
2004             //SNAPUP(line, rowHeight);
2005             SetScrollPosition(scroll.x, line);
2006          }
2007
2008          if(notify)
2009          {
2010             Window master = this.master;
2011             if(master)
2012             {
2013                if(style.freeSelect && visible)
2014                   NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
2015                else
2016                   NotifySelect(master, this, currentRow ? currentRow : null, 0);
2017                if(style.alwaysEdit && currentRow)
2018                   currentRow.Edit(currentField);
2019             }
2020          }
2021
2022          Update(null);
2023       }
2024    }
2025
2026    void PopupEditBox(DataField whichField, bool repositionOnly)
2027    {
2028       if((!editData || !editData.visible || currentField != whichField) && currentRow)
2029       {
2030          // true: destroy edit box
2031          HideEditBox(true, true, false);
2032          if(whichField)
2033          {
2034             int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2035             int x = 0;
2036             int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2037             int width;
2038             //void * data = currentRow.GetData(whichField);
2039             DataField field;
2040             DataRow row = null;
2041             ListBoxCell cell;
2042
2043             if(style.collapse && !(style.treeBranch))
2044                x += 15;
2045
2046             for(field = fields.first; field; field = field.next)
2047             {
2048                width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2049                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
2050                if(field == whichField) break;
2051                x += width;
2052             }
2053
2054             currentField = whichField;
2055             cell = GetCell(&row, &currentField);
2056
2057             if(!editData)
2058             {
2059                editData = DataBox
2060                {
2061                   this;
2062                   background = dataBoxBackground;
2063                   foreground = dataBoxForeground;
2064
2065                   bool NotifyChanged(bool closingDropDown)
2066                   {
2067                      DataRow row = null;
2068                      DataField field = null;
2069                      ListBoxCell cell = GetCell(&row, &field);
2070                      if(cell)
2071                      {
2072                         cell.isSet = true;
2073                         modifiedDocument = true;
2074                         Update(null);
2075                         NotifyChanged(master, this, currentRow);
2076                      }
2077                      return true;
2078                   }
2079
2080                   bool NotifyModified()
2081                   {
2082                      //DataRow row = null;
2083                      //DataField field = null;
2084                      //ListBoxCell cell = GetCell(&row, &field);
2085                      //cell.isSet = true;
2086                      modifiedDocument = true;
2087                      //Update(null);
2088                      NotifyModified(master, this, currentRow);
2089                      return true;
2090                   }
2091
2092                   bool OnKeyDown(Key key, unichar ch)
2093                   {
2094                      bool result = DataBox::OnKeyDown(key, ch);
2095                      if(visible && active)   // Added this check here, because we will not use enter/escape otherwise, and lose DataBox's result
2096                      {
2097                         if((SmartKey)key == enter || (SmartKey)key == escape) 
2098                            return true;
2099                      }
2100                      return result;
2101                   }
2102                };
2103                incref editData;
2104             }
2105             else
2106                editData.Destroy(0);
2107             editData.type = whichField.dataType;
2108             editData.fieldData = whichField.userData;
2109             editData.borderStyle = style.alwaysEdit ? 0 : deep;
2110             editData.data = cell ? cell.data : null;
2111
2112             if(!repositionOnly)
2113                // Might not really need this anymore...
2114                NotifyEditing(master, this, currentRow);
2115
2116             editData.Create();
2117             if(!style.alwaysEdit)
2118             {
2119                editData.position = { x, y - editData.clientStart.y };
2120                editData.size = { width, height + editData.clientStart.y * 2 };
2121             }
2122             else
2123             {
2124                editData.position = { x, y };
2125                editData.size = { width, height };
2126             }
2127             if(!repositionOnly)
2128                editData.Refresh();
2129             editData.visible = true;
2130
2131             if(style.alwaysEdit)
2132                editData.Deactivate();
2133
2134             //   MOVED THIS HIGHER FOR DATALIST EDITOR
2135             if(!repositionOnly)
2136                // Might not really need this anymore...
2137                NotifyEdited(master, this, currentRow);
2138          }
2139       }
2140    }
2141
2142    void OnRedraw(Surface surface)
2143    {
2144       DataRow row;
2145       int y = (style.header) ? rowHeight : 0;
2146       bool isActive = active;
2147       Font font = fontObject;
2148       Font boldFont = this.boldFont.font;
2149
2150
2151       // Draw gray grid
2152       if(style.alwaysEdit && style.fullRowSelect)
2153       {
2154          // Horizontal lines
2155          int y = (style.header) ? rowHeight : 0;
2156          int x = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
2157          int w = clientSize.w;
2158          int h = clientSize.h;
2159          DataRow row;
2160          DataField field;
2161
2162          // Fill out indent column
2163          if(style.collapse && !(style.treeBranch) && rows.first)
2164          {
2165             x += 15;
2166             surface.SetBackground(formColor);
2167             surface.Area(-scroll.x, 0, x, clientSize.h);
2168          }
2169
2170          surface.SetForeground(formColor);
2171          for(row = firstRowShown; row; row = row.GetNextRow())
2172          {
2173             y += rowHeight;
2174             surface.HLine(x + 1, w-1, y-1);
2175             if(y >= h)
2176                break;
2177          }
2178
2179          // Vertical lines
2180          for(field = fields.first; field; field = field.next)
2181          {
2182             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2183                clientSize.w - field.x : (field.width + EXTRA_SPACE);
2184             if(field.prev && y > 0)
2185                surface.VLine(0, y-1, x);
2186             x += width;
2187          }
2188       }
2189
2190       surface.foreground = this.foreground;
2191       surface.TextOpacity(false);
2192
2193       // Draw the tree branches
2194       if(style.treeBranch)
2195       {
2196          int y = -scroll.y + ((style.header) ? rowHeight : 0);
2197          surface.LineStipple(0x5555);
2198          surface.SetForeground(branchesColor);
2199          for(row = rows.first; row; row = row.GetNextRow() )
2200          {
2201             int x = -scroll.x + EXTRA_SPACE / 2-1;
2202             int rowStart = -scroll.x;
2203             int indent = 0;
2204             DataRow parent;
2205             int plusIndent;
2206
2207             for(parent = row.parent; parent; parent = parent.parent)
2208                if(!parent.header) indent += 20;
2209             if(style.rootCollapse) indent += 20;
2210
2211             plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2212
2213             x += indent;
2214
2215             // Vertical line
2216             if(row.subRows.first)
2217             {
2218                int numRows = 0;
2219                int y1 = y + PLUSY + 4;
2220                int y2;
2221                DataRow child;
2222             
2223                for(child = row.collapsed ? null : row.subRows.first; child && child != row; )
2224                {
2225                   numRows++;
2226                   if(child.subRows.first && !child.collapsed && child != row.subRows.last)
2227                      child = child.subRows.first;
2228                   else if(child.next)
2229                      child = child.next;
2230                   else
2231                   {
2232                      for(; child && child != row; child = child.parent)
2233                      {
2234                         if(child.next)
2235                         {
2236                            child = child.next;
2237                            break;
2238                         }
2239                      }
2240                   }
2241                }
2242                y2 = y + numRows * rowHeight + PLUSY + 4;
2243                surface.VLine(y1, y2, rowStart + plusIndent + 7 + 20);
2244             }
2245             surface.HLine(rowStart + plusIndent + 7, rowStart + indent, y + PLUSY + 4);
2246
2247             y += rowHeight;
2248             if(y >= clientSize.h)
2249                break;
2250          }
2251          // Root Vertical Lines
2252          if(style.rootCollapse && rows.first)
2253          {
2254             int numRows = 0;
2255             int y1, y2;
2256             DataRow child;
2257             y = -scroll.y + ((style.header) ? rowHeight : 0);
2258             y1 = y + PLUSY + 4;
2259             for(child = rows.first; child && child != rows.last; )
2260             {
2261                numRows++;
2262                if(child.subRows.first && !child.collapsed && child != rows.last)
2263                   child = child.subRows.first;
2264                else if(child.next)
2265                   child = child.next;
2266                else
2267                {
2268                   for(; child; child = child.parent)
2269                   {
2270                      if(child.next)
2271                      {
2272                         child = child.next;
2273                         break;
2274                      }
2275                   }
2276                }
2277             }
2278             y2 = y + numRows * rowHeight + PLUSY + 4;
2279             surface.VLine(y1, y2, -scroll.x + 11);
2280          }
2281          surface.LineStipple(0);
2282       }
2283
2284       for(row = firstRowShown; row; row = row.GetNextRow() )
2285       {
2286          int x = -scroll.x + EXTRA_SPACE / 2-1;
2287          DataField field;
2288          ListBoxCell cell;
2289          Color foreground = this.foreground /*black*/, background = this.background /*white*/;
2290          DataDisplayFlags dataDisplayFlags = 0;
2291          int rowStart = -scroll.x;
2292          int indent = 0;
2293          DataRow parent;
2294          Bitmap icon = row.icon ? row.icon.bitmap : null;
2295          int collapseRowStart;
2296          bool lastWasHeader = row.header;
2297
2298          for(parent = row.parent; parent; parent = parent.parent)
2299          {
2300             if(!parent.header || lastWasHeader)
2301             {
2302                if(style.treeBranch)
2303                   indent += 20;
2304                else
2305                   indent += 15;
2306             }
2307          }
2308          if(style.rootCollapse) indent += 20;
2309          x += indent;
2310
2311          dataDisplayFlags.fullRow = style.fullRowSelect;
2312          dataDisplayFlags.active = isActive;
2313          dataDisplayFlags.header = row.header;
2314
2315          surface.Clip(null);
2316          if(style.collapse)
2317          {
2318             collapseRowStart = rowStart;
2319
2320             if(!(style.treeBranch))
2321             {
2322                x += 15;
2323                rowStart += 15;
2324             }
2325          }
2326
2327          if(style.multiSelect)
2328          {
2329             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2330             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2331          }
2332          else
2333          {
2334             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2335             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2336             /*
2337             if(row == currentRow)
2338             {
2339                dataDisplayFlags.current = true;
2340                dataDisplayFlags.selectedFlag = true;
2341             }
2342             else if(!currentRow && row == firstRowShown)
2343             {
2344                dataDisplayFlags.current = true;
2345             }*/
2346          }
2347
2348          surface.TextOpacity(true);
2349
2350          background = this.background;
2351          foreground = this.foreground;
2352
2353          // Draw the current row background
2354          if(row.header)
2355          {
2356             Color colors[] = { formColor, azure, mistyRose, linen, floralWhite, lavender, lavenderBlush, lemonChiffon };
2357             int level = 0;
2358             DataRow p = row;
2359             while(p = p.parent) level++;
2360             background = colors[(level % (sizeof(colors)/sizeof(colors[0]))];
2361             surface.SetBackground(background);
2362             surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - 1);
2363             foreground = branchesColor;
2364          }
2365          else if(dataDisplayFlags.selected)
2366          {
2367             if(dataDisplayFlags.selected && (isActive || style.alwaysHL || (style.alwaysEdit && style.fullRowSelect)))
2368             {
2369                if(!isActive && style.alwaysEdit)
2370                   background = formColor;
2371                else
2372                   background = selectionColor ? selectionColor : SELECTION_COLOR;
2373                if(style.fullRowSelect)
2374                {
2375                   int offset = (style.alwaysEdit) ? 2 : 1;
2376                   surface.SetBackground(background);
2377                   surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - offset);
2378                }
2379                if(isActive || !(style.alwaysEdit))
2380                   foreground = selectionText ? selectionText : SELECTION_TEXT;
2381                else
2382                   foreground = branchesColor;
2383             }
2384          }
2385
2386          if(icon)
2387          {
2388             surface.Blit(icon, x + (20 - icon.width) /2,y + 2,0,0, icon.width, icon.height);
2389             x += 20;
2390          }
2391
2392          if(row.noneRow)
2393          {
2394             int width = clientSize.w;
2395             Box clip;
2396             dataDisplayFlags.firstField = true;
2397             clip.left = x - EXTRA_SPACE / 2+1;
2398             clip.top = y;
2399             clip.right = x + width - EXTRA_SPACE/2 - 0;
2400             clip.bottom = y + rowHeight - 1;
2401             surface.Clip(&clip);
2402
2403             surface.TextFont(font);
2404
2405             surface.SetForeground(foreground);
2406             surface.SetBackground(background);
2407
2408             class(String)._vTbl[__ecereVMethodID_class_OnDisplay](class(String), "(none)", surface, x, y - 1 + (rowHeight - fontH)/2, width - EXTRA_SPACE/2, null, Alignment::left, dataDisplayFlags);
2409          }
2410          else
2411          {
2412             if(!opacity) surface.TextOpacity(false);
2413             // Draw the rows
2414             for(field = fields.first; field; field = field.next)
2415             {
2416                uint index;
2417                int width = ((!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) || row.header) ?
2418                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
2419
2420                // Box clip = { x, y+1, x + field.width - EXTRA_SPACE - 1, y + rowHeight - 2 };
2421                Box clip;
2422
2423                //width -= EXTRA_SPACE;
2424
2425                if(!field.prev) width -= indent;
2426
2427
2428                dataDisplayFlags.firstField = field.prev ? false : true;
2429
2430                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2431                {
2432                   background = this.background;
2433                   foreground = this.foreground;
2434                }
2435
2436                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable && opacity)
2437                {
2438                   surface.Clip(null);
2439                   surface.SetBackground(background);
2440                   surface.Area(x-3, y, x+width-1, y + rowHeight-2);
2441                }
2442
2443                clip.left = x - EXTRA_SPACE / 2+1;
2444                clip.top = y;
2445                clip.right = x + width - EXTRA_SPACE/2 - 0;
2446                clip.bottom = y + rowHeight - 1;
2447                surface.Clip(&clip);
2448
2449                for(index = 0, cell = row.cells.first; cell && index != field.index; index++, cell = cell.next);
2450                // Should always be as many cells in the row as fields in the listbox
2451                if(cell && cell.isSet && field.dataType)
2452                {
2453                   if(row.header)
2454                      surface.TextFont(boldFont);
2455                   else
2456                      surface.TextFont(font);
2457
2458                   surface.SetForeground(foreground);
2459                   surface.SetBackground(background);
2460
2461                   if(field.dataType.type == noHeadClass || field.dataType.type == normalClass)
2462                      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);
2463                   else
2464                      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);
2465                }
2466
2467                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable)
2468                   background = formColor;
2469
2470                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2471                {
2472                   background = formColor;
2473                   foreground = this.background;
2474                }
2475
2476                x += width;// + EXTRA_SPACE;
2477
2478                if(row.header) break;
2479             }
2480          }
2481
2482          if(style.collapse)
2483          {
2484             int plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2485             surface.Clip(null);
2486             if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)))
2487             {
2488                surface.SetForeground(row.header ? headerCollapseForeground : this.foreground);
2489                surface.Rectangle(collapseRowStart + 3 + plusIndent, y + PLUSY, collapseRowStart + 11 + plusIndent, y + PLUSY + 8);
2490
2491                surface.SetBackground(row.header ? (formColor) : (this.background)); //white
2492                surface.Area(collapseRowStart + 4 + plusIndent, y + PLUSY + 1, collapseRowStart + 10 + plusIndent, y + PLUSY + 7);
2493
2494                surface.HLine(collapseRowStart + 5 + plusIndent, collapseRowStart + 9 + plusIndent, y+PLUSY+4);
2495                if(row.collapsed)
2496                   surface.VLine(y + PLUSY + 2, y + PLUSY + 6, collapseRowStart + 7 + plusIndent);
2497             }
2498
2499          }
2500
2501          // Draw the current row stipple
2502          if(style.fullRowSelect && !(style.alwaysEdit) && (dataDisplayFlags.current) && isActive)
2503          {
2504             surface.Clip(null);
2505             if(isActive)
2506             {
2507                surface.LineStipple(0x5555);
2508                if(dataDisplayFlags.selected)
2509                   surface.SetForeground(stippleColor);
2510                else
2511                   surface.SetForeground(this.foreground);
2512             }
2513             else
2514                surface.SetForeground(selectionColor ? selectionColor : SELECTION_COLOR);
2515             surface.Rectangle(0, y, clientSize.w-1, (y + rowHeight) - 1);
2516             surface.LineStipple(0);
2517          }
2518
2519          y += rowHeight;
2520          if(y >= clientSize.h)
2521             break;
2522       }
2523       if(firstRowShown) surface.Clip(null);
2524       if(this.dragRow && this.dropIndex != -1)
2525       {
2526          int dropIndex = this.dropIndex;
2527          int y;
2528
2529          if(!style.multiSelect && currentRow.index < this.dropIndex)
2530             dropIndex++;
2531          surface.DrawingChar(223);
2532
2533          y = style.header ? rowHeight : 0;
2534          y += dropIndex * rowHeight - scroll.y;
2535
2536          surface.SetForeground(Color { 85, 85, 255 });
2537          surface.HLine(0, clientSize.w-1, y);
2538          surface.HLine(0, clientSize.w-1, y + 1);
2539       }
2540    }
2541
2542    void OnDrawOverChildren(Surface surface)
2543    {
2544       if(draggingField && this.dropField)
2545       {
2546          int position = this.dropField.x;
2547          if(draggingField.x < position)
2548             position += this.dropField.width + EXTRA_SPACE;
2549
2550          surface.SetForeground(Color { 85, 85, 255 });
2551          surface.VLine(0, rowHeight - 1, position - scroll.x - 2);
2552          surface.VLine(0, rowHeight - 1, position - scroll.x);
2553       }
2554       if(sortField && !style.clearHeader && style.header)
2555       {
2556          DataField field = sortField;
2557          int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2558             clientSize.w - field.x : (field.width + EXTRA_SPACE);
2559          int tw = 0, th = 0;
2560          if(field.header)
2561             surface.TextExtent(field.header, strlen(field.header), &tw, &th);
2562          if(tw < width - EXTRA_SPACE)
2563          {
2564             bool up = field.sortOrder == 1;
2565             int x = 4, y = 4;
2566             Box clip = 
2567             { 
2568                field.x + 2 - scroll.x, 0, 
2569                field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight
2570             };
2571             surface.Clip(&clip);
2572             if(field.alignment == left || field.alignment == center)
2573             {
2574                if(field.alignment == center)
2575                   x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4;
2576                else
2577                   x = field.x + tw + EXTRA_SPACE + 4;
2578              
2579                x = Min(x, field.x + width - 4);
2580             }
2581             else if(field.alignment == right)
2582             {
2583                x = field.x + width - tw - 2*EXTRA_SPACE - 4;
2584                x = Max(x, field.x + 2);
2585             }
2586             x -= scroll.x;
2587
2588             if(guiApp.textMode)
2589             {
2590                // surface.SetForeground((wmenu.selectedFlag == item) ? white : black);
2591                // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1);
2592             }
2593             else
2594             {
2595                if(up)
2596                {
2597                   surface.SetForeground(Color { 128,128,128 } );
2598                   surface.DrawLine(x + 3, y, x, y + 5);
2599                   surface.PutPixel(x + 1, y + 5);
2600                   surface.PutPixel(x + 1, y + 3);
2601                   surface.PutPixel(x + 2, y + 1);
2602             
2603                   surface.SetForeground(white);
2604                   surface.DrawLine(x + 4, y, x + 7, y + 5);
2605                   surface.PutPixel(x + 6, y + 5);
2606                   surface.PutPixel(x + 6, y + 3);
2607                   surface.PutPixel(x + 5, y + 1);
2608
2609                   surface.DrawLine(x, y + 6, x + 7, y + 6);
2610                }
2611                else
2612                {
2613                   surface.SetForeground(Color { 128,128,128 });
2614                   surface.DrawLine(x + 3, y+6, x, y+1);
2615                   surface.PutPixel(x + 1, y+1);
2616                   surface.PutPixel(x + 1, y+3);
2617                   surface.PutPixel(x + 2, y+5);
2618             
2619                   surface.SetForeground(white);
2620                   surface.DrawLine(x + 4, y+6, x + 7, y+1);
2621                   surface.PutPixel(x + 6, y+1);
2622                   surface.PutPixel(x + 6, y+3);
2623                   surface.PutPixel(x + 5, y+5);
2624
2625                   surface.DrawLine(x, y, x + 7, y);
2626                }
2627             }
2628             surface.Clip(null);
2629          }
2630       }
2631
2632    }
2633
2634    void OnResize(int w, int h)
2635    {
2636       DataField field;
2637       bool showEndBevel = false;
2638       int x = 0;
2639       if(style.collapse && !style.treeBranch)
2640          x += 15;
2641       for(field = fields.first; field; field = field.next)
2642       {
2643          int width = field.width + EXTRA_SPACE;
2644          field.x = x;
2645          x += width;
2646       }
2647       width = x;
2648
2649       SetScrollArea(
2650          width,
2651          (rowCount * rowHeight) + 
2652          ((style.header) ? rowHeight : 0) -
2653          ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
2654
2655       for(field = fields.first; field; field = field.next)
2656       {
2657          int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
2658             clientSize.w - field.x : (field.width + EXTRA_SPACE);
2659          if(style.header && field.headButton)
2660          {
2661             showEndBevel = true;
2662             if(width > 0)
2663             {
2664                field.headButton.position = { field.x, 0 };
2665                field.headButton.size = { width, rowHeight };
2666                field.headButton.visible = true;
2667             }
2668             else
2669                field.headButton.visible = false;
2670          }
2671       }
2672
2673       if(!style.fillLastField && showEndBevel && endBevel)
2674       {
2675          endBevel.position = { x, 0 };
2676          endBevel.size = { clientSize.w + 2 - x, rowHeight };
2677          endBevel.visible = true;
2678       }
2679       else if(endBevel)
2680          endBevel.visible = false;
2681
2682       if(style.alwaysEdit && editData && editData.visible)
2683       {
2684          HideEditBox(true, false, true);
2685       }
2686    }
2687
2688    void AdaptToFieldWidth(DataField field, bool doScroll)
2689    {
2690       OnResize(clientSize.w, clientSize.h);
2691
2692       // Scroll appropriately
2693       if(doScroll)
2694       {
2695          if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2696                  field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2697          {
2698             SetScrollPosition(field.x + field.width + EXTRA_SPACE - clientSize.w, scroll.y);
2699          }
2700          else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2701                  field.x < scroll.x)
2702             SetScrollPosition(field.x, scroll.y);
2703       }
2704       Update(null);
2705    }
2706
2707    bool HeaderPushed(Button control, int x, int y, Modifiers mods)
2708    {
2709       DataField field = (DataField)control.id;
2710       // false: dont destroy edit box
2711       HideEditBox(true, false, true);
2712       if(style.resizable && ((!field && x < RESIZE_BORDER && fields.last) ||
2713          (field && x < RESIZE_BORDER && field.prev) || 
2714          (field && x >= control.clientSize.w - RESIZE_BORDER)))
2715       {
2716          if(!field)
2717             field = fields.last;
2718          else if(x < RESIZE_BORDER && field.prev)
2719             field = field.prev;
2720
2721          if(field.fixed) return false;
2722          resizingField = field;
2723          this.resizeX = x + control.position.x;
2724          this.startWidth = field.width;
2725          this.oldX = x - scroll.x;
2726          return false;
2727       }
2728       else if(field)
2729       {
2730          if(field.fixed) return false;
2731          draggingField = field;
2732          if(style.moveFields)
2733             field.headButton.stayDown = true;
2734          else if(!style.sortable)
2735             return false;
2736       }
2737       else
2738          return false;
2739       return true;
2740    }
2741
2742    bool HeaderMouseMove(Button control, int x, int y, Modifiers mods)
2743    {
2744       if(resizingField)
2745       {
2746          // Resize left
2747          DataField field = resizingField;
2748
2749          x += control.position.x;
2750
2751          // Tweak to prevent shrinking field if we're actually moving right
2752          if(x - scroll.x > this.oldX && 
2753             this.startWidth + x - this.resizeX < field.width)
2754          {
2755             this.oldX = x - scroll.x;
2756             return true;
2757          }
2758          this.oldX = x - scroll.x;
2759
2760          field.width = this.startWidth + x - this.resizeX;
2761          field.width = Max(field.width, - EXTRA_SPACE);
2762
2763          AdaptToFieldWidth(field, true);
2764       }
2765       else if(draggingField)
2766       {
2767          x += control.position.x;
2768          if(style.moveFields)
2769          {
2770             DataField field = fields.last;
2771             int fieldX = 0;
2772             for(field = fields.first; field; field = field.next)
2773             {
2774                fieldX += ((field.width || style.resizable) ? 
2775                   field.width : clientSize.w) + EXTRA_SPACE;
2776                if(fieldX > x) 
2777                   break;
2778             }
2779             if(draggingField == field)
2780             {
2781                // Reset scroll position
2782                if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2783                        field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2784                {
2785                   SetScrollPosition(
2786                      field.x + field.width + EXTRA_SPACE - clientSize.w, 
2787                      scroll.y);
2788                }
2789                else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2790                        field.x < scroll.x)
2791                   SetScrollPosition(field.x, scroll.y);
2792                field = null;
2793             }
2794             if(this.dropField != field)
2795             {
2796                this.dropField = field;
2797                if(field)
2798                {
2799                   int position = field.x;
2800                   // Moving right
2801                   if(draggingField.x < position)
2802                   {
2803                      position += field.width + EXTRA_SPACE - clientSize.w;
2804                      if(position > scroll.x)
2805                         SetScrollPosition(position, scroll.y);
2806                   }
2807                   // Moving Left
2808                   else
2809                   {
2810                      if(position < scroll.x)
2811                         SetScrollPosition(position, scroll.y);
2812                   }
2813
2814                   this.movingFields = true;
2815                }
2816                Update(null);
2817             }
2818          }
2819       }
2820       else if(style.resizable)
2821       {
2822          DataField field = (DataField)control.id;
2823          if(field)
2824          {
2825             if(x < RESIZE_BORDER && field.prev)
2826             {
2827                if(!field.prev.fixed)
2828                   control.cursor = guiApp.GetCursor(sizeWE);
2829             }
2830             else if(x >= control.clientSize.w - RESIZE_BORDER)
2831                control.cursor = guiApp.GetCursor(sizeWE);
2832             else
2833                control.cursor = null;
2834          }
2835          else
2836          {
2837             if(x < RESIZE_BORDER && fields.last)
2838                control.cursor = guiApp.GetCursor(sizeWE);
2839             else
2840                control.cursor = null;
2841          }
2842       }
2843       return true;
2844    }
2845
2846    bool HeaderReleased(Button control, int x, int y, Modifiers mods)
2847    {
2848       if(resizingField)
2849       {
2850          NotifyResized(master, this, resizingField, mods);
2851          resizingField = null;
2852       }
2853
2854       if(draggingField)
2855       {
2856          bool result = true;
2857          
2858          if(style.moveFields)
2859          {
2860             if(dropField)
2861             {
2862                // Find which field
2863                DataField switchField = fields.last;
2864                DataField field;
2865                int fieldX = 0;
2866
2867                x += draggingField.x;
2868                for(field = fields.first; field; field = field.next)
2869                {
2870                   fieldX += ((field.width || style.resizable) ? 
2871                      field.width : clientSize.w) + EXTRA_SPACE;
2872                   if(fieldX > x) 
2873                   {
2874                      switchField = field;
2875                      break;
2876                   }
2877                }
2878                if(switchField && draggingField != switchField && this.dropField)
2879                {
2880                   for(field = fields.first; field; field = field.next)
2881                   {
2882                      if(field == switchField || field == draggingField)
2883                         break;
2884                   }
2885
2886                   // Switch field first: move before
2887                   if(field == switchField)
2888                      draggingField.Move(switchField.prev);
2889                   // Dragged field first: move after
2890                   else
2891                      draggingField.Move(switchField);
2892
2893                   NotifyMovedField(master, this, draggingField, mods);
2894                }
2895                draggingField.headButton.stayDown = false;
2896             }
2897             if(movingFields)
2898                result = false;
2899             dropField = null;
2900             movingFields = false;
2901          }
2902          draggingField = null;
2903          return result;
2904       }
2905       return true;
2906    }
2907
2908    bool HeaderClicked(Button control, int x, int y, Modifiers mods)
2909    {
2910       if(style.header && !this.dropField && style.sortable)
2911       {
2912          DataField field = (DataField)control.id;
2913          if(sortField == field)
2914             field.sortOrder *= -1;
2915          else
2916             sortField = field;
2917          if(field)
2918          {
2919             Sort(sortField, field.sortOrder);
2920             NotifySort(master, this, field, mods);
2921          }
2922       }
2923       return true;
2924    }
2925
2926    bool HeaderDoubleClicked(Button control, int x, int y, Modifiers mods)
2927    {
2928       if(style.resizable)
2929       {
2930          DataField field = (DataField)control.id;
2931          if(field)
2932          {
2933             if(x < RESIZE_BORDER && field.prev)
2934                field = field.prev;
2935             else if(x >= control.clientSize.w - RESIZE_BORDER);
2936             else
2937                field = null;
2938          }
2939          else
2940          {
2941             if(x < RESIZE_BORDER && fields.last)
2942                field = fields.last;
2943             else
2944                field = null;
2945          }
2946          if(field)
2947             field.AutoSize();
2948       }
2949       return false;
2950    }
2951
2952    watch(visible)
2953    {
2954       if(style.freeSelect)
2955       {
2956          if(!visible)
2957          {
2958             ReleaseCapture();
2959             this.rolledOver = this.dragging = false;
2960          }
2961          /*else
2962             Capture();*/
2963       }
2964    };
2965
2966    bool OnLoadGraphics()
2967    {
2968       display.FontExtent(fontObject, "W", 1, null, &fontH); 
2969       if(!style.heightSet)
2970       {
2971          rowHeight = Max(fontH + 2, 16) + (style.alwaysEdit ? 1 : 0);
2972          SetScrollLineStep(8, rowHeight);
2973       }
2974       return true;
2975    }
2976
2977    void OnApplyGraphics()
2978    {
2979       SetScrollLineStep(8, rowHeight);
2980       if(style.header)
2981       {
2982          DataField field;
2983          for(field = fields.first; field; field = field.next)
2984          {
2985             if(field.headButton)
2986             {
2987                field.headButton.bevel = (!guiApp.textMode && !style.clearHeader);
2988                if(guiApp.textMode)
2989                   field.headButton.background = Color { 0, 170, 0 };
2990             }
2991          }
2992       }
2993       OnResize(clientSize.w, clientSize.h);
2994    }
2995
2996    bool OnResizing(int *w, int *h)
2997    {
2998       if(rows.first)
2999       {
3000          if(!initSize.w && (!anchor.left.type || !anchor.right.type) /**w*/)
3001          {
3002             // Use widest item
3003             DataRow row;
3004             int maxWidth = 0;
3005             Font font = fontObject;
3006             Font boldFont = this.boldFont.font;
3007             Display display = this.display;
3008             
3009             for(row = rows.first; row; row = row.GetNextRow())
3010             {
3011                Bitmap icon = row.icon ? row.icon.bitmap : null;
3012                int x = -scroll.x + EXTRA_SPACE / 2-1;
3013                DataField field;
3014                int indent = 0;
3015                DataRow parent;
3016                for(parent = row.parent; parent; parent = parent.parent)
3017                {
3018                   if(!parent.header)
3019                   {
3020                      if(style.treeBranch)
3021                         indent += 20;
3022                      else
3023                         indent += 15;
3024                   }
3025                }
3026                if(style.rootCollapse) indent += 20;
3027                x += indent;
3028                if(style.collapse && !(style.treeBranch)) x += 15;
3029                if(icon)
3030                   x += 20;
3031
3032                // Compute the rows size
3033                for(field = fields.first; field; field = field.next)
3034                {
3035                   if(((style.resizable && (!(style.alwaysEdit) || field.next)) || field.width) && !row.header)
3036                      x += field.width - (field.prev ? 0 : indent);
3037                   else
3038                   {
3039                      ListBoxCell cell;
3040                      uint index;
3041                      for(index = 0, cell = row.cells.first; index != field.index; index++, cell = cell.next);
3042
3043                      // Should always be as many cells in the row as fields in the listbox
3044                      if(cell && cell.isSet && field.dataType)
3045                      {
3046                         static char tempString[4096];
3047                         char * string;
3048                         int tw, th;
3049                         if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
3050                            string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, cell.data[0], tempString, field.userData, null);
3051                         else
3052                            string = (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, cell.data, tempString, field.userData, null);
3053                         /* GCC-4.4 Bug!
3054                         if(!string) string = "";
3055                         display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3056                         */
3057                         if(string)
3058                            display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3059                         else
3060                            display.FontExtent(row.header ? boldFont : font, "", 0, &tw, &th);
3061                         x += tw;
3062                      }
3063                      if(row.header) break;
3064                   }
3065                   x += EXTRA_SPACE;
3066                }
3067                maxWidth = Max(maxWidth, x);
3068             }
3069             *w = maxWidth;
3070          }
3071          if(!*h)
3072          {
3073             *h = Min(this.maxShown, this.rowCount) * rowHeight;
3074          }
3075       }
3076       else
3077       {
3078          if(!*w) *w = rowHeight * 5;
3079          if(!*h) *h = rowHeight * 5;
3080       }
3081       return true;
3082    }
3083
3084    watch(font)
3085    {
3086       FontResource font = this.font;
3087       FontResource boldFont
3088       {
3089          faceName = font.faceName, size = font.size, bold = true
3090       };
3091       AddResource(boldFont);
3092       RemoveResource(this.boldFont);
3093       this.boldFont = boldFont;
3094
3095       OnLoadGraphics();
3096
3097       SetInitSize(initSize);
3098    };
3099
3100    bool OnMouseMove(int x, int y, Modifiers mods)
3101    {
3102       bool isTimer = false;
3103       int realX = x, realY = y;
3104
3105       if(insideNotifySelect) return true;
3106
3107       if(style.alwaysEdit && style.resizable &&
3108          resizingField && !(mods.isSideEffect))
3109       {
3110         // Resize left
3111          DataField field = resizingField;
3112          field.width = this.startWidth + x - this.resizeX;
3113          field.width = Max(field.width, - EXTRA_SPACE);
3114
3115          AdaptToFieldWidth(field, true);
3116       }
3117
3118       cursor = null;
3119       if(style.alwaysEdit && style.resizable)
3120       {
3121          int vx = -scroll.x - 1;
3122          DataField field;
3123
3124          if(style.collapse && !(style.treeBranch))
3125             vx += 15;
3126
3127          for(field = fields.first; field; field = field.next)
3128          {
3129             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3130                clientSize.w - field.x : (field.width + EXTRA_SPACE);
3131
3132             if(field.prev)
3133             {
3134                if(Abs(x - vx) < 2)
3135                {
3136                   cursor = guiApp.GetCursor(sizeWE);
3137                   break;
3138                }
3139             }
3140             vx += width + EXTRA_SPACE;
3141          }
3142       }
3143
3144       if((editData && editData.visible) || (style.alwaysEdit))
3145          return true;
3146       
3147       if(x == MAXINT && y == MAXINT)
3148       {
3149          x = this.mouseX;
3150          y = this.mouseY;
3151          isTimer = true;
3152       }
3153
3154       // ADDED THIS CHECK FOR FieldDropBox LEAKS
3155       if(/*!mods.isSideEffect && */(this.rolledOver || !this.dragging))
3156       {
3157          int rowY = (style.header) ? rowHeight : 0;
3158          DataRow row = null;
3159          int rowIndex;
3160
3161          mouseX = x;
3162          mouseY = y;
3163
3164          if(this.dragging &&
3165             ((vertScroll && vertScroll.visible && 
3166              (y < 0 || y >= clientSize.h)) ||
3167              (horzScroll && horzScroll.visible && 
3168              (x < 0 || x >= clientSize.w))))
3169          {
3170             timer.Start();
3171             if(isTimer)
3172             {
3173                if(vertScroll && vertScroll.visible && 
3174                   (y < 0 || y >= clientSize.h))
3175                   vertScroll.Action((y<0)?up:down, 0, 0);
3176                if(horzScroll && horzScroll.visible && 
3177                   (x < 0 || x >= clientSize.w))
3178                   horzScroll.Action((x<0)?up:down, 0, 0);
3179             }
3180          }
3181          else
3182             timer.Stop();
3183
3184          // This must be done after the scrolling took place
3185          rowIndex = firstRowShown ? firstRowShown.index : -1;
3186          y = Max(y, 0);
3187          y = Min(y, clientSize.h-1);
3188          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3189          {
3190             rowY += rowHeight;
3191             if(rowY > y)
3192             {
3193                break;
3194             }
3195          }
3196
3197          if(row && currentRow != row)
3198          {
3199             if(this.dragRow && style.moveRows)
3200             {
3201                if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3202                   rowIndex = -1;
3203                else if(style.multiSelect)
3204                {
3205                   DataRow thisRow;
3206                   for(thisRow = rows.first; thisRow; thisRow = thisRow.GetNextRow())
3207                      if((thisRow.selectedFlag == selected || thisRow.selectedFlag == tempSelected) || 
3208                         thisRow == row)
3209                         break;
3210                   if(thisRow != row)
3211                      rowIndex++;
3212                }
3213                if(this.dropIndex != rowIndex)
3214                {
3215                   this.dropIndex = rowIndex;
3216                   this.editRow = null;
3217                   Update(null);
3218                   this.movedRow = true;
3219                }
3220             }
3221             else if((style.freeSelect  || this.dragging) && ((realX>= 0 && realY >= 0 && realX< clientSize.w && realY < clientSize.h) || this.rolledOver))
3222             {
3223                if(!(style.multiSelect))
3224                {
3225                   if(currentRow)currentRow.selectedFlag = unselected;
3226                   if(row)row.selectedFlag = selected;
3227                }
3228                currentRow = row;
3229
3230                if(style.multiSelect)
3231                {
3232                   DataRow selRow;
3233
3234                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3235                   {
3236                      if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3237                      else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3238                   }
3239
3240                   if(rowIndex >= clickedRow.index)
3241                   {
3242                      for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3243                      {
3244                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3245                         if(selRow == row)
3246                            break;
3247                      }
3248                   }
3249                   else
3250                   {
3251                      for(selRow = row; selRow; selRow = selRow.GetNextRow())
3252                      {
3253                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3254                         if(selRow == clickedRow)
3255                            break;
3256                      }
3257                   }
3258                }
3259                Update(null);
3260                if(style.freeSelect)
3261                   NotifyHighlight(master, this, currentRow, mods);
3262                else
3263                {
3264                   insideNotifySelect = true;
3265                   NotifySelect(master, this, currentRow, mods);
3266                   insideNotifySelect = false;
3267                }
3268
3269                if(style.alwaysEdit && currentRow)
3270                   currentRow.Edit(currentField);
3271             }
3272          }
3273       }
3274       return true;
3275    }
3276
3277    bool OnMouseOver(int x, int y, Modifiers mods)
3278    {
3279       if(this.dragging)
3280          this.rolledOver = true;
3281       return true;
3282    }
3283
3284    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
3285    {
3286       // TOCHECK: WAS THIS MISSING ? CHECK FOR SLOWDOWN
3287       if(!active) Update(null);
3288
3289       if(!active && (!swap || !swap.isModal))
3290       {
3291          // true: destroy edit box
3292          HideEditBox(true, true, false);
3293       }
3294       else if(!swap || !swap.isModal)
3295       {
3296          // Bring back edit box
3297          if(currentRow && style.alwaysEdit)
3298          {
3299             currentRow.Edit(currentField ? currentField : null);
3300          }
3301          Update(null);
3302       }
3303       return true; //NotifyActivate(master, this, active, swap, 0);
3304    }
3305
3306
3307    bool OnButtonDown(int x, int y, Modifiers mods, bool right)
3308    {
3309       bool result = true;
3310       // Check to see if we're dragging the vertical divider
3311       if(style.alwaysEdit && style.resizable && !right)
3312       {
3313          int vx = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
3314          DataField field;
3315
3316          if(style.collapse && !(style.treeBranch))
3317             vx += 15;
3318
3319          for(field = fields.first; field; field = field.next)
3320          {
3321             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3322                clientSize.w - field.x : (field.width + EXTRA_SPACE);
3323
3324             if(field.prev)
3325             {
3326                if(Abs(x - vx) < 2)
3327                {
3328                   resizingField = field.prev;
3329                   this.resizeX = x;
3330                   this.startWidth = resizingField.width;
3331                   Capture();
3332                   SetMouseRangeToClient();
3333                   break;
3334                }
3335             }
3336             vx += width + EXTRA_SPACE;
3337          }
3338       }
3339
3340       if(!(style.freeSelect))
3341       {
3342          int rowY = (style.header) ? rowHeight : 0;
3343          DataRow row = null;
3344          int rowIndex = firstRowShown ? firstRowShown.index : -1;
3345          DataRow previousRow = currentRow;
3346          DataRow newCurrentRow = null;
3347          DataField newCurrentField = null;
3348          bool moveMultiple = false;
3349          int numSelected = 0;
3350          int rowStart = -scroll.x;
3351
3352          if(style.multiSelect)
3353          {
3354             if(!right)
3355             {
3356                DataRow row;
3357                if(!(mods.shift))
3358                {
3359                   for(row = rows.first; row; row = row.GetNextRow())
3360                   {
3361                      if(row.selectedFlag == tempSelected)
3362                         row.selectedFlag = selected;
3363                      else if(row.selectedFlag == tempUnselected)
3364                         row.selectedFlag = unselected;
3365                      if(row.selectedFlag == selected)
3366                         numSelected++;
3367                   }
3368                }
3369             }
3370          }
3371
3372          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3373          {
3374             rowY += rowHeight;
3375             if(rowY > y || (style.multiSelect && !row.GetNextRow())) 
3376             {
3377                int plusIndent = 0;
3378                if(style.treeBranch)
3379                {
3380                   DataRow parent;
3381                   for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3382                      if(!parent.header)
3383                         plusIndent += 20;
3384                   plusIndent += 4;
3385                }
3386
3387                /*    THIS WAS TOO STRICT:
3388                if(style.collapse && row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) &&
3389                   (x >= rowStart + 3 + plusIndent && y >= rowY - rowHeight + PLUSY && x <= rowStart + 11 + plusIndent && y <= rowY - rowHeight + PLUSY + 8))
3390                */
3391                if(style.collapse && 
3392                   (x >= rowStart && y >= rowY - rowHeight && x <= rowStart + 18 + plusIndent && y <= rowY + rowHeight-1))
3393                {
3394                   if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) && x >= plusIndent)
3395                      row.collapsed = !row.collapsed;
3396                   return false;
3397                }
3398                else
3399                {
3400                   if(rowY > y)
3401                   {
3402                      newCurrentRow = row;
3403                   }
3404                   if(style.multiSelect)
3405                   {
3406                      // Deselect everything if user didn't clicked on a row
3407                      if(y >= rowY)
3408                      {
3409                         DataRow selRow;
3410                         for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3411                            selRow.selectedFlag = unselected;
3412                         clickedRow = row;
3413                         //this.clickedRowIndex = rowIndex;
3414                      }
3415                      else if(style.moveRows && !(mods.shift) && 
3416                         (row.selectedFlag == selected || row.selectedFlag == tempSelected) &&
3417                         !right && !(mods.isActivate))
3418                         moveMultiple = true;
3419                      else
3420                      {
3421                         DataRow selRow;
3422                         if(right)
3423                         {
3424                            if(row.selectedFlag == tempUnselected || row.selectedFlag == unselected)
3425                            {
3426                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3427                                  selRow.selectedFlag = unselected;
3428                               row.selectedFlag = selected;
3429                            }
3430                            clickedRow = row;
3431                            //this.clickedRowIndex = rowIndex;
3432                         }
3433                         else
3434                         {
3435                            if(!(mods.ctrl))
3436                            {
3437                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3438                                  selRow.selectedFlag = unselected;
3439                            }
3440                            else
3441                            {
3442                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3443                               {
3444                                  if(selRow != clickedRow)
3445                                  {
3446                                     if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3447                                     else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3448                                  }
3449                               }
3450                            }
3451
3452                            if(mods.shift)
3453                            {
3454                               if(rowIndex >= clickedRow.index)
3455                               {
3456                                  for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3457                                  {
3458                                     if(mods.ctrl)
3459                                     {
3460                                        if(selRow != clickedRow)
3461                                        {
3462                                           if(selRow.selectedFlag) 
3463                                              selRow.selectedFlag = tempUnselected;
3464                                           else
3465                                              selRow.selectedFlag = tempSelected;
3466                                        }
3467                                     }
3468                                     else
3469                                        selRow.selectedFlag = selected;
3470                                     if(selRow == row)
3471                                        break;
3472                                  }
3473                               }
3474                               else
3475                               {
3476                                  for(selRow = row; selRow; selRow = selRow.GetNextRow())
3477                                  {
3478                                     if(mods.ctrl)
3479                                     {
3480                                        if(selRow != clickedRow)
3481                                        {
3482                                           if(selRow.selectedFlag)
3483                                              selRow.selectedFlag = tempUnselected;
3484                                           else
3485                                              selRow.selectedFlag = tempSelected;
3486                                        }
3487                                     }
3488                                     else
3489                                        selRow.selectedFlag = selected;
3490                                     if(selRow == clickedRow)
3491                                        break;
3492                                  }
3493                               }
3494                            }
3495                            else
3496                            {
3497                               if(mods.ctrl)
3498                               {
3499                                  if(row.selectedFlag)
3500                                     row.selectedFlag = tempUnselected;
3501                                  else row.selectedFlag = tempSelected;
3502                               }
3503                               else
3504                                  row.selectedFlag = tempSelected;
3505                               clickedRow = row;
3506                               //this.clickedRowIndex = rowIndex;
3507                            }
3508                         }
3509                      }
3510                   }
3511                }
3512                break;
3513             }
3514          }
3515
3516          // true: destroy edit box
3517          if(newCurrentRow)
3518          {
3519             incref newCurrentRow;
3520          }
3521
3522          if(currentRow != newCurrentRow)
3523             HideEditBox(true, true, false);
3524
3525          if(newCurrentRow)
3526          {
3527             if(newCurrentRow._refCount <= 1)
3528                delete newCurrentRow;
3529             else
3530                newCurrentRow._refCount--;
3531          }
3532
3533          if(newCurrentRow)
3534          {
3535             if(!(style.multiSelect))
3536             {
3537                if(currentRow) currentRow.selectedFlag = unselected;
3538                if(newCurrentRow) newCurrentRow.selectedFlag = selected;
3539             }
3540          }
3541
3542          if(currentRow != newCurrentRow)
3543          {
3544             /*
3545             // true: destroy edit box
3546             if(newCurrentRow)
3547             {
3548                //incref newCurrentRow;
3549                incref newCurrentRow;
3550             }
3551
3552             HideEditBox(true, true, false);
3553             */
3554
3555             if(newCurrentRow)
3556             {
3557                int headerSize = ((style.header) ? rowHeight : 0);
3558                int height = clientSize.h + 1 - headerSize;
3559
3560                /*if(newCurrentRow._refCount <= 1)
3561                   delete newCurrentRow;
3562                else
3563                {
3564                   newCurrentRow._refCount--;
3565                   //newCurrentRow._refCount--;
3566                }
3567                */
3568                currentRow = newCurrentRow;
3569
3570                if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
3571                   SetScrollPosition(scroll.x,
3572                      currentRow.index * rowHeight - height + rowHeight);
3573                else if(!currentRow || currentRow.index * rowHeight < scroll.y)
3574                {
3575                   int line = currentRow ? currentRow.index * rowHeight : 0;
3576                   //SNAPUP(line, rowHeight);
3577                   SetScrollPosition(scroll.x, line);
3578                }
3579
3580                // GO THROUGH SetCurrentRow eventually?
3581                // SetCurrentRow(newCurrentRow, true);
3582             }
3583          }
3584
3585          if(style.freeSelect)
3586             NotifyHighlight(master, this, currentRow, mods);
3587          else if((moveMultiple || (!(style.multiSelect) && previousRow == currentRow)) && 
3588             newCurrentRow && !(mods.shift))
3589          {
3590             if(!right)
3591             {
3592                if(!(mods.isActivate))
3593                {
3594                   if(style.moveRows)
3595                   {
3596                      this.dragRow = currentRow;
3597                      this.dropIndex = -1;
3598                      this.movedRow = false;
3599                   }
3600                   if(editData && editData.visible && style.alwaysEdit)
3601                   {
3602                      DataField field;
3603                      int sx = -scroll.x;
3604                      int indent = 0;
3605                      DataRow parent;
3606
3607                      if(style.collapse && !style.treeBranch)
3608                         sx += 15;
3609
3610                      for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3611                         if(!parent.header)
3612                            indent += (style.treeBranch) ? 20 : 15;
3613                      sx += indent;
3614
3615                      for(field = fields.first; field; field = field.next)
3616                      {
3617                         int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3618                            clientSize.w - field.x : (field.width + EXTRA_SPACE);
3619
3620                         if(!field.prev) width -= indent;
3621                         if(x >= sx && x < sx + width)
3622                            break;                     
3623                         sx += width;
3624                      }
3625                      if(field == currentField)
3626                         editData.Deactivate();
3627                      else
3628                      {
3629                         currentRow.Edit(field);                     
3630                         editData.Activate();
3631                      }
3632                   }
3633                   else if(!(mods.ctrl) && numSelected <= 1)
3634                      this.editRow = currentRow;
3635
3636                   if(style.noDragging && newCurrentRow)
3637                     NotifyReclick(master, this, newCurrentRow, mods);
3638                }
3639                else
3640                {
3641                   // If the user clicked exactly on the edited field,
3642                   // activate it
3643                   if(editData && editData.visible && newCurrentRow)
3644                   {
3645                      DataField field, whichField;
3646                      int sx = -scroll.x;
3647                      int indent = 0;
3648
3649                      if(style.collapse && !(style.treeBranch))
3650                         sx += 15;
3651                   
3652                      whichField = currentField;
3653
3654                      {
3655                         DataRow parent;
3656                         for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3657                            if(!parent.header)
3658                               indent += (style.treeBranch) ? 20 : 15;
3659                         sx += indent;
3660                      }
3661
3662                      for(field = fields.first; field; field = field.next)
3663                      {
3664                         int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3665                            clientSize.w - field.x : (field.width + EXTRA_SPACE);
3666                         if(!field.prev) width -= indent;
3667                         if(x >= sx && x < sx + width && newCurrentRow)
3668                            break;
3669                         sx += width;
3670                      }
3671
3672                      if(field) //x >= sx && x < sx + width && newCurrentRow)
3673                      {
3674                         if(field == currentField)
3675                            editData.Activate();
3676                         /*else
3677                            newCurrentRow.Edit(currentField);*/
3678                      }
3679                      else if(style.noDragging && newCurrentRow)
3680                        NotifyReclick(master, this, newCurrentRow, mods);
3681                   }
3682                   else if(style.noDragging && newCurrentRow)
3683                     NotifyReclick(master, this, newCurrentRow, mods);
3684                }
3685             }
3686          }
3687          else
3688          {
3689             result = NotifySelect(master, this, 
3690                currentRow ? currentRow : null, mods);
3691             if(result && style.alwaysEdit && currentRow)
3692             {
3693                if(newCurrentRow)
3694                {
3695                   DataField field = null;
3696                   int sx = -scroll.x;
3697                   int indent = 0;
3698                   DataRow parent;
3699
3700                   if(style.collapse && !style.treeBranch)
3701                      sx += 15;
3702
3703                   for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3704                      if(!parent.header)
3705                         indent += (style.treeBranch) ? 20 : 15;
3706                   sx += indent;
3707
3708                   for(field = fields.first; field; field = field.next)
3709                   {
3710                      int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3711                         clientSize.w - field.x : (field.width + EXTRA_SPACE);
3712
3713                      if(!field.prev) width -= indent;
3714                      if(x >= sx && x < sx + width)
3715                      {
3716                         currentField = field;
3717                         break;
3718                      }
3719                      sx += width;
3720                   }
3721                }
3722                currentRow.Edit(currentField);
3723
3724                // If the user clicked exactly on the edited field,
3725                // activate it
3726                if(editData && editData.visible && newCurrentRow)
3727                {
3728                   if(currentField)
3729                   {
3730                      editData.Activate();
3731                   }
3732                   else if(style.noDragging && newCurrentRow)
3733                      NotifyReclick(master, this, newCurrentRow, mods);
3734                }
3735             }
3736             else if(style.noDragging && newCurrentRow)
3737               NotifyReclick(master, this, newCurrentRow, mods);
3738          }
3739       }
3740       /*
3741          For drop box to capture...
3742       else
3743       {
3744          if(x < 0 || y < 0 || x >= clientSize.w || y >= clientSize.h)
3745          {
3746             bool goOn = true;
3747             master.parent.Activate();
3748             Update(null);
3749             ReleaseCapture();
3750             return true;
3751          }
3752       }
3753       */
3754       if(result)
3755       {
3756          if(!style.noDragging)
3757          {
3758             this.dragging = true;
3759             Capture();
3760          }
3761          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
3762             this.rolledOver = true;
3763          Update(null);
3764       }
3765       else
3766       {
3767          this.dragging = false;
3768          OnLeftButtonUp(x, y, mods);
3769       }
3770       return result;
3771    }
3772
3773    bool OnLeftButtonDown(int x, int y, Modifiers mods)
3774    {
3775       return OnButtonDown(x,y, mods, false);
3776    }
3777
3778    bool OnLeftButtonUp(int x, int y, Modifiers mods)
3779    {
3780       if(resizingField && style.alwaysEdit)
3781       {
3782          Window::FreeMouseRange();
3783          ReleaseCapture();
3784          resizingField = null;
3785       }
3786
3787       if(dragRow || editRow)
3788       {
3789          DataRow row, switchRow = rows.last;
3790          int rowY = (style.header) ? rowHeight : 0;
3791          for(row = firstRowShown; row; row = row.GetNextRow())
3792          {
3793             rowY += rowHeight;
3794             if(rowY > y) 
3795             {
3796                switchRow = row;
3797                break;
3798             }
3799          }
3800          if(this.editRow == switchRow && y >= 0)
3801          {
3802             // Find which field
3803             DataField field;
3804             int fieldX = 0;
3805             for(field = fields.first; field; field = field.next)
3806             {
3807                int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ? 
3808                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
3809                fieldX += width;
3810
3811                if(fieldX > x + scroll.x) 
3812                   break;
3813             }
3814             
3815             if(field && field.editable)
3816             {
3817                // true: destroy edit box
3818                HideEditBox(true, true, false);
3819                PopupEditBox(field, false);
3820             }
3821             else if(!style.noDragging)
3822                NotifyReclick(master, this, currentRow, mods);
3823          }
3824          else if(style.moveRows && switchRow)
3825          {
3826             if(this.dragRow == switchRow && this.movedRow == false)
3827             {
3828                DataRow row = this.dragRow;
3829                DataRow selRow;
3830                if(!(mods.ctrl))
3831                {
3832                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3833                      selRow.selectedFlag = unselected;
3834                }
3835                else
3836                {
3837                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3838                   {
3839                      if(selRow != clickedRow)
3840                      {
3841                         if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3842                         else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3843                      }
3844                   }
3845                }
3846                if(mods.ctrl)
3847                {
3848                   if(row.selectedFlag)
3849                      row.selectedFlag = tempUnselected;
3850                   else row.selectedFlag = tempSelected;
3851                }
3852                else
3853                   row.selectedFlag = tempSelected;
3854                clickedRow = row;
3855             }
3856             else
3857             { 
3858                if(style.multiSelect)
3859                {
3860                   if(!switchRow.selectedFlag)
3861                   {
3862                      bool foundSwitch = false;
3863                      bool after = false;
3864                      DataRow next;
3865                      DataRow afterRow = switchRow.prev;
3866                      for(row = rows.first; row; row = next)
3867                      {
3868                         next = row.GetNextRow();
3869                         if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3870                         {
3871                            if(!foundSwitch && !after)
3872                            {
3873                               after = true;
3874                               afterRow = switchRow;
3875                            }
3876                            if(!after || !(row.selectedFlag == selected || row.selectedFlag == tempSelected) ||
3877                               !foundSwitch)
3878                            {
3879                               row.Move(afterRow);
3880                               afterRow = row;
3881                            }
3882                         }
3883                         else if(row == switchRow)
3884                            foundSwitch = true;
3885                      }
3886                   }
3887                }
3888                else
3889                {
3890                   for(row = rows.first; row; row = row.GetNextRow())
3891                   {
3892                      if(row == switchRow || row == this.dragRow)
3893                         break;
3894                   }
3895
3896                   // Switch row first: move before
3897                   if(row == switchRow)
3898                   {
3899                      if(NotifyMove(master, this, switchRow.prev, mods))
3900                         dragRow.Move(switchRow.prev);
3901                   }
3902                   // Dragged row first: move after
3903                   else
3904                   {
3905                      if(NotifyMove(master, this, switchRow, mods))
3906                         dragRow.Move(switchRow);
3907                   }
3908                }
3909             }
3910          }
3911          dragRow = null;
3912          editRow = null;
3913          movedRow = false;
3914          dropIndex = -1;
3915          Update(null);
3916       }
3917
3918       timer.Stop();
3919       if(this.dragging || style.freeSelect)
3920       {
3921          if(this.dragging)
3922          {
3923             this.rolledOver = this.dragging = false;
3924          }
3925          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h && currentRow && style.freeSelect)
3926          {
3927             bool result;
3928             ReleaseCapture();
3929             result = NotifySelect(master, this, currentRow, mods);
3930             if(style.alwaysEdit)
3931                currentRow.Edit(currentField);
3932             return result;
3933
3934          }
3935          // if(!(style.freeSelect))
3936          ReleaseCapture();
3937       }
3938       return true;
3939    }
3940
3941    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
3942    {
3943       int rowStart = -scroll.x;
3944       DataRow row;
3945       int rowY = (style.header) ? rowHeight : 0;
3946       int plusIndent = 0;
3947
3948       OnLeftButtonUp(x,y,mods);
3949       if(style.alwaysEdit)
3950       {
3951          if(!(style.collapse) || x > 15)
3952             if(editData && editData.visible)
3953             {
3954                editData.Activate();
3955                NotifyDoubleClick(master, this, x, y, mods);
3956                return false;
3957             }
3958       }
3959       for(row = firstRowShown; row; row = row.GetNextRow())
3960       {
3961          rowY += rowHeight;
3962          if(rowY > y || (style.multiSelect && !row.GetNextRow())) 
3963          {
3964             if(style.treeBranch)
3965             {
3966                DataRow parent;
3967                for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3968                   if(!parent.header)
3969                      plusIndent += 20;
3970                plusIndent += 4;
3971             }
3972             break;
3973          }
3974       }
3975
3976       if((row && style.collapse && (x >= rowStart && x <= rowStart + 18 + plusIndent)) ||
3977          NotifyDoubleClick(master, this, x, y, mods))
3978       {
3979          if(style.collapse)
3980          {
3981             if(row && row.subRows.first)
3982             {
3983                row.collapsed = !row.collapsed;
3984                return false;
3985             }
3986          }
3987          // We need to return true here so that OnLeftButtonDown can popup the DataBox Editors
3988          return true;
3989       }
3990       return false;
3991    }
3992
3993    bool OnRightButtonDown(int x, int y, Modifiers mods)
3994    {
3995       return OnButtonDown(x,y, mods, true);
3996    }
3997
3998    bool OnRightButtonUp(int x, int y, Modifiers mods)
3999    {
4000       OnLeftButtonUp(x,y,mods);
4001       return NotifyRightClick(master, this, x, y, mods);
4002    }
4003
4004    bool GoToLetter(unichar ch, bool keyHit)
4005    {
4006       bool result = false;
4007       DataField field;
4008       bool checkNextField = true;
4009       int len = keyHit ? 0 : strlen(typedString);
4010
4011       typedString = renew typedString char[len + 2];
4012       typedString[len++] = (char)tolower(ch);         // TODO: FIX UNICODE
4013       typedString[len] = '\0';
4014
4015       for(field = fields.first; field; field = field.next)
4016       {
4017          DataRow startRow = currentRow ? currentRow : rows.first;
4018
4019          if(startRow && field.dataType && field.dataType._vTbl[__ecereVMethodID_class_OnGetString] && ch)
4020          {
4021             DataRow row, next;
4022             bool looped = false;
4023             if(len == 1 && currentRow)
4024                startRow = (next = startRow.GetNextRow(), (next ? next : rows.first));
4025         
4026             for(row = startRow; row != startRow || !looped; next = row.GetNextRow(), row = next ? next : rows.first)
4027             {
4028                void * data = row.GetData(field);
4029                char tempString[1024] = "";
4030                bool needClass = false;
4031                char * string = data ? (char *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString](field.dataType, data, tempString, null, &needClass) : null;
4032
4033                if(string && string[0])
4034                   checkNextField = false;
4035                if(string && string[0] && !strnicmp(string, typedString, len))
4036                {
4037                   if(style.multiSelect)
4038                   {
4039                      DataRow selRow;
4040                      bool foundRow = false;
4041
4042                      //this.clickedRowIndex = 0;
4043                      clickedRow = row;
4044                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4045                      {
4046                         if(selRow == row) foundRow = true;
4047                         selRow.selectedFlag = unselected;
4048                         //if(!foundRow) this.clickedRowIndex++;
4049                      }
4050                      row.selectedFlag = selected;
4051                   }
4052                   SetCurrentRow(row, true);
4053                   result = true;
4054                   break;
4055                }
4056                looped = true;
4057             }
4058             typingTimer.Stop();
4059             if(this.typingTimeOut && !keyHit)
4060                typingTimer.Start();
4061             if(!result || !this.typingTimeOut || keyHit)
4062                typedString[len-1] = '\0';
4063          }
4064          if(!checkNextField || result) break;
4065       }
4066       return result;
4067    }
4068
4069    bool OnKeyDown(Key key, unichar ch)
4070    {
4071       DataField field;
4072
4073       if(key == enter || key == keyPadEnter)
4074       {
4075          if(editData && editData.visible && editData.active)
4076          {
4077             HideEditBox(true, false, false);
4078             return false;
4079          }
4080       }
4081       else if(key == escape)
4082       {
4083          if(resizingField || this.movingFields || (editData && editData.visible) || this.dragRow)
4084          {
4085             if(editData && editData.visible && style.alwaysEdit && !editData.active)
4086                return true;
4087             // false: dont destroy edit box
4088             HideEditBox(false, false, false);
4089             if(resizingField)
4090             {
4091                resizingField.width = this.startWidth;
4092                AdaptToFieldWidth(resizingField, true);
4093                resizingField = null;
4094                ReleaseCapture();
4095             }
4096             this.dragRow = null;
4097             if(this.dragging)
4098             {
4099                this.dragging = false;
4100                ReleaseCapture();
4101             }
4102
4103             this.movingFields = false;
4104             draggingField = null;
4105             Update(null);
4106             return false;
4107          }      
4108       }
4109
4110       if(!currentField || !currentField.editable)            
4111          for(field = fields.first; field; field = field.next)
4112          {
4113             if(field.editable)
4114             {
4115                currentField = field;
4116                break;
4117             }
4118          }
4119       if(key == f2 && currentField && currentField.editable)
4120       {
4121          PopupEditBox(currentField, false);
4122          if(editData && editData.visible)
4123          {
4124             if(style.alwaysEdit)
4125                editData.Activate();
4126             return false;
4127          }
4128       }
4129  
4130       if(!NotifyKeyDown(master, this, currentRow, key, ch))
4131          return false;
4132
4133       // Editable fields...
4134       if(currentField)
4135       {
4136          if(style.alwaysEdit && editData && editData.visible)
4137             return editData.OnKeyDown(key, ch);
4138          return true;   // We want to pick up the OnKeyHit to replace contents, but skip GoToLetter
4139       }
4140       
4141       if(ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, false))
4142       {
4143          /*if(inactive && window.state != Hidden)
4144             NotifyHighlight(master, this, currentRow, 0);
4145          else
4146          {
4147             NotifySelect(master, this, currentRow, 0);
4148          }*/
4149          return false;
4150       }
4151       return true;
4152    }
4153
4154    bool OnKeyHit(Key key, unichar ch)
4155    {
4156       if(key.code == up && key.alt == true && key.ctrl == false && key.shift == false)
4157          return true;
4158
4159       if(!ch && !key.alt && !key.ctrl)
4160       {
4161          key.code = (SmartKey)key.code;
4162       }
4163       if(ch >= 32 && !key.alt && !key.ctrl && ch != 128)
4164       {
4165          DataField field;
4166          if(!currentField || !currentField.editable)
4167             for(field = fields.first; field; field = field.next)
4168             {
4169                if(field.editable)
4170                {
4171                   currentField = field;
4172                   break;
4173                }
4174             }
4175          if(currentField && currentField.editable)
4176          {
4177             if((!editData || !editData.visible) || !editData.active)
4178             {
4179                PopupEditBox(currentField, false);
4180                if(editData && editData.visible)
4181                {
4182                   editData.Activate();
4183                   editData.OnKeyHit(key, ch);
4184                }
4185             }
4186             return false;
4187          }
4188       }
4189
4190       if(!(style.multiSelect) && (key.ctrl))
4191          return true;
4192
4193       if(editData && editData.visible && ch && !key.alt && !key.ctrl && editData.active)
4194          return false;
4195
4196       switch(key.code)
4197       {
4198          case left:
4199             if(style.alwaysEdit)
4200             {
4201                if(currentField)
4202                {
4203                   DataField field;
4204                   for(field = currentField.prev; field; field = field.prev)
4205                   {
4206                      if(field.editable)
4207                      {
4208                         currentField = field;
4209                         HideEditBox(true, true, false);
4210                         PopupEditBox(currentField, false);
4211                         return false;
4212                      }                     
4213                   }
4214                }
4215             }
4216             if(style.collapse && currentRow /*&& !currentField*/)  // THIS PREVENTED COLLAPSING THE PROPERTY SHEET
4217             {
4218                if(currentRow.subRows.first && !currentRow.collapsed)
4219                {
4220                   currentRow.collapsed = true;
4221                }
4222                else if(currentRow.parent)
4223                   SetCurrentRow(currentRow.parent, true);
4224                return false;
4225             }
4226             break;
4227          case right:
4228             if(style.collapse && currentRow && currentRow.subRows.first)
4229             {
4230                if(currentRow.collapsed)
4231                   currentRow.collapsed = false;
4232                else
4233                   SetCurrentRow(currentRow.subRows.first, true);
4234                return false;
4235             }
4236             else if(style.alwaysEdit)
4237             {
4238                if(currentField)
4239                {
4240                   DataField field;
4241                   for(field = currentField.next; field; field = field.next)
4242                   {
4243                      if(field.editable)
4244                      {
4245                         currentField = field;
4246                         HideEditBox(true, true, false);
4247                         PopupEditBox(currentField, false);
4248                         break;
4249                      }                     
4250                   }
4251                }
4252             }
4253             break;
4254          case down: case up:
4255          case pageDown: case pageUp:
4256          case end: case home:
4257          {
4258             int headerSize = ((style.header) ? rowHeight : 0);
4259             int height = clientSize.h + 1 - headerSize;
4260             DataRow oldRow;
4261
4262             // true: destroy edit box
4263             // !!! TESTING true HERE !!!
4264             HideEditBox(true, true, false);
4265             // HideEditBox(false, true, false);
4266             
4267             oldRow = currentRow;
4268
4269             SNAPDOWN(height, rowHeight);
4270             if((!currentRow || key.code == home) && key.code != end)
4271             {
4272                currentRow = rows.first;
4273             }
4274             else
4275             {
4276                DataRow next;
4277                switch(key.code)
4278                {
4279                   case down:
4280                      if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4281                         next = currentRow;
4282                      else
4283                         next = currentRow.GetNextRow();
4284                      if(next)
4285                      {
4286                         currentRow = next;
4287                      }
4288                      break;
4289                   case up:
4290                      if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4291                         next = currentRow;
4292                      else
4293                         next = currentRow.GetPrevRow();
4294
4295                      if(next)
4296                      {
4297                         currentRow = next;
4298                      }
4299                      break;
4300                   case end:
4301                      currentRow = lastRow.GetLastRow();
4302                      break;
4303                   case pageUp:
4304                   {
4305                      int c;
4306                      for(c = 0;
4307                      currentRow && (next = currentRow.GetPrevRow()) && c < height / rowHeight;
4308                          c++, currentRow = next);
4309                      break;
4310                   }
4311                   case pageDown:
4312                   {
4313                      int c;
4314                      for(c = 0;
4315                          currentRow && (next = currentRow.GetNextRow()) && c < height / rowHeight;
4316                          c++, currentRow = next);
4317                      break;
4318                   }
4319                }
4320             }
4321             if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
4322                SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
4323             else if(!currentRow || currentRow.index * rowHeight < scroll.y)
4324                SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
4325
4326             if(style.multiSelect)
4327             {
4328                DataRow selRow;
4329
4330                if(!(key.shift) && (key.ctrl))
4331                {
4332                   DataRow row;
4333                   for(row = rows.first; row; row = row.GetNextRow())
4334                   {
4335                      if(row.selectedFlag == tempSelected)
4336                         row.selectedFlag = selected;
4337                      else if(row.selectedFlag == tempUnselected)
4338                         row.selectedFlag = unselected;
4339                   }
4340                }
4341
4342                if(!(key.ctrl))
4343                {
4344                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4345                      selRow.selectedFlag = unselected;
4346                }
4347                else
4348                {
4349                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4350                   {
4351                      if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
4352                      else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
4353                   }
4354                }
4355
4356                if(key.shift)
4357                {
4358                   if(currentRow.index >= clickedRow.index)
4359                   {
4360                      for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
4361                      {
4362                         if(key.ctrl)
4363                         {
4364                            if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4365                         }
4366                         else
4367                            selRow.selectedFlag = selected;
4368                         if(selRow == currentRow)
4369                            break;
4370                      }
4371                   }
4372                   else
4373                   {
4374                      for(selRow = currentRow; selRow; selRow = selRow.GetNextRow())
4375                      {
4376                         if(key.ctrl)
4377                         {
4378                            if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4379                         }
4380                         else
4381                            selRow.selectedFlag = selected;
4382                         if(selRow == clickedRow)
4383                            break;
4384                      }
4385                   }
4386                }
4387                else
4388                {
4389                   if(!(key.ctrl) && currentRow)
4390                   {
4391                      currentRow.selectedFlag = selected;
4392                   }
4393
4394                   clickedRow = currentRow;
4395                }
4396             }
4397             else
4398             {
4399                if(oldRow) oldRow.selectedFlag = unselected;
4400                if(currentRow) currentRow.selectedFlag = selected;
4401             }
4402
4403             if(currentRow)
4404             {
4405                if(style.freeSelect)
4406                   NotifyHighlight(master, this, currentRow, 0);
4407                else
4408                   NotifySelect(master, this, currentRow, 0);
4409
4410                if(style.alwaysEdit && currentRow)
4411                   currentRow.Edit(currentField /*null*/);
4412             }
4413             Update(null);
4414             return false;
4415          }
4416          case space:
4417          {
4418             if(style.multiSelect && currentRow)
4419             {
4420                if(currentRow.selectedFlag)
4421                {
4422                   if(key.ctrl)
4423                      currentRow.selectedFlag = unselected;
4424                }
4425                else
4426                   currentRow.selectedFlag = selected;
4427                Update(null);
4428
4429                if(style.freeSelect)
4430                   NotifyHighlight(master, this, currentRow, 0);
4431                else
4432                   NotifySelect(master, this, currentRow, 0);
4433             }
4434             break;
4435          }
4436       }
4437  
4438       if(!NotifyKeyHit(master, this, currentRow, key, ch))
4439          return false;
4440
4441       if(ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, true))
4442       {
4443          /*if(inactive && window.state != Hidden)
4444             return NotifyHighlight(master, this, currentRow, 0);
4445          else
4446          {
4447             return NotifySelect(master, this, currentRow, 0);
4448          }*/
4449          return false;
4450       }
4451       return true;
4452    }
4453
4454
4455    void OnHScroll(ScrollBarAction action, int position, Key key)
4456    {
4457       Update(null);
4458    }
4459
4460    void OnVScroll(ScrollBarAction action, int position, Key key)
4461    {
4462       int y = 0;
4463       DataRow next;
4464
4465       for(firstRowShown = rows.first; firstRowShown; firstRowShown = next)
4466       {
4467          next = firstRowShown.GetNextRow();
4468          if(y >= position || !next) break;
4469
4470          y += rowHeight;
4471       }
4472       Update(null);
4473    }
4474
4475    OldList fields;
4476    OldList rows;
4477    int numFields;
4478    DataRow firstRowShown;
4479    DataRow clickedRow;
4480    DataRow currentRow;
4481    int width;
4482    DataField sortField;
4483    int rowCount;
4484    int rowHeight;
4485    int fontH;
4486    double typingTimeOut;
4487    char * typedString;
4488
4489    int mouseX, mouseY;
4490
4491    Timer timer
4492    {
4493       delay = 0.1;
4494       userData = this;
4495
4496       bool DelayExpired()
4497       {
4498          Modifiers mods { };
4499          if(guiApp.GetKeyState(shift)) mods.shift = true;
4500          if(guiApp.GetKeyState(alt)) mods.alt = true;
4501          if(guiApp.GetKeyState(control)) mods.ctrl = true;
4502          OnMouseMove(MAXINT, MAXINT, mods);
4503
4504          return true;
4505       }
4506    };
4507
4508    Timer typingTimer
4509    {
4510       delay = 0.5; // typingTimeOut
4511       userData = this;
4512       
4513       bool DelayExpired()
4514       {
4515          typedString[0] = '\0';
4516
4517          // The next line was commented... Why? When commented typing words stops working ( only first character jumps )
4518          typingTimer.Stop();
4519          return true;
4520       }
4521    };
4522
4523    bool dragging, rolledOver;
4524    int numSelections;
4525    Button endBevel;
4526
4527    // For moving rows
4528    DataRow dragRow;
4529    int dropIndex;
4530    bool movedRow;
4531
4532    // For editing fields
4533    DataBox editData;
4534    DataField currentField;
4535    DataRow editRow;
4536
4537    // For moving fields
4538    DataField draggingField, dropField;
4539    bool movingFields;
4540
4541    // For resizing fields
4542    DataField resizingField;
4543    int resizeX, oldX, startWidth;
4544
4545    ListBoxBits style;
4546    FontResource boldFont;
4547    int maxShown;
4548
4549    // Only used for OnMouseMove so far, for avoiding problems with consequential mouse moves
4550    bool insideNotifySelect;
4551    Color selectionColor, selectionText, stippleColor;
4552    stippleColor = 0xFFFFFF80;
4553 };