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