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