ecere/gui/controls/DropBox: Extra safety to avoid crashes
[sdk] / ecere / src / gui / controls / DropBox.ec
1 namespace gui::controls;
2
3 import "Window"
4
5 #include <stdarg.h>
6
7 //#define BTN_WIDTH       20
8 #define BTN_WIDTH       16
9 #define ROW_HEIGHT      listBox.rowHeight /*16*/
10
11 default:
12 extern int __ecereVMethodID_class_OnDisplay;
13 extern int __ecereVMethodID_class_OnGetString;
14
15 private:
16
17 class DropBoxBits { bool noHighlight:1, noStipple:1, editText:1, activeColor:1, showNone:1, changeContents:1; };
18
19 public class DropBox : CommonControl
20 {
21    tabCycle = true;
22    borderStyle = deep;
23    style.activeColor = true;
24
25    class_property(icon) = "<:ecere>controls/dropBox.png";
26
27    watch(foreground)     { listBox.foreground = foreground;         if(editBox) editBox.foreground = foreground; };
28    watch(background)     { listBox.background = background;         if(editBox) editBox.background = background; };
29    watch(selectionColor) { listBox.selectionColor = selectionColor; if(editBox) editBox.selectionColor = selectionColor; };
30    watch(selectionText)  { listBox.selectionText = selectionText;   if(editBox) editBox.selectionText = selectionText; };
31    watch(opacity)        { listBox.opacity = opacity;               if(editBox) editBox.opacity = opacity; };
32
33 public:
34    property bool activeStipple
35    {
36       property_category $"Appearance"
37       set
38       {
39          style.noStipple = !value;
40          Update(null);
41       }
42       get { return !style.noStipple; }
43    };
44    property bool showButton
45    {
46       property_category $"Appearance"
47       set
48       {
49          button.visible = value;
50          listBox.borderStyle = value ? contour : none;
51       }
52       get { return button.visible; }
53    };
54    property Alignment alignment
55    {
56       property_category $"Appearance"
57       set
58       {
59          alignment = value;
60          if(field)
61             field.alignment = value;
62       }
63       get { return alignment; }
64    };
65    property bool noHighlight
66    {
67       property_category $"Appearance"
68       set
69       {
70          if(this)
71          {
72             style.noHighlight = value;
73             listBox.fullRowSelect = !value;
74             Update(null);
75          }
76       }
77       get { return style.noHighlight; }
78    };
79    property bool activeColor
80    {
81       property_category $"Appearance"
82       set
83       {
84          if(this)
85          {
86             style.activeColor = value;
87             Update(null);
88          }
89       }
90       get { return style.activeColor; }
91    };
92    property DataRow currentRow
93    {
94       property_category $"Private"
95       set
96       {
97          if(this)
98          {
99             currentRow = value;
100             listBox.currentRow = value ? value : (style.showNone ? noneRow : null);
101             if(style.editText && style.changeContents)
102             {
103                char tempString[4096];
104                if(currentRow)
105                {
106                   const char *(* onGetString)(void *, void *, char *, void *, bool *) = (void *)dataType._vTbl[__ecereVMethodID_class_OnGetString];
107                   editBox.contents = onGetString(dataType, currentRow.GetData(null), tempString, null, null);
108                }
109                else
110                   editBox.contents = "";
111             }
112             Update(null);
113             if(style.editText)
114             {
115                if(editBox.modifiedDocument)
116                {
117                   NotifyTextEntry(master, this, editBox.contents, false);
118                   editBox.modifiedDocument = false;
119                }
120                // editBox.Deactivate();
121             }
122          }
123       }
124       get { return currentRow; }
125    };
126    property DataRow firstRow { get { return this ? listBox.firstRow : null; } };
127    property DataRow lastRow { get { return this ? listBox.lastRow : null; } };
128    property const char * contents { property_category $"Data" set { if(editBox) editBox.contents = value; } get { return editBox ? editBox.contents : null; } };
129    property bool editText
130    {
131       property_category $"Behavior"
132       set
133       {
134          if(this)
135          {
136             if(value)
137             {
138                if(!editBox)
139                {
140                   editBox = EditBox
141                   {
142                      this, textHorzScroll = true, borderStyle = 0;
143                      anchor = Anchor { left = 0, top = 0, right = BTN_WIDTH, bottom = 0 };
144                      // visible = false, modifyVirtualArea = false;
145                      foreground = foreground;
146                      background = background;
147                      selectionColor = selectionColor;
148                      selectionText = selectionText;
149                   };
150                   incref editBox;
151                   editBox.Create();
152                   button.inactive = false;
153                }
154                style.editText = true;
155                tabCycle = false;
156             }
157             else
158             {
159                if(editBox)
160                {
161                   editBox.Destroy(0);
162                   delete editBox;
163                   button.inactive = true;
164                }
165                style.editText = false;
166             }
167          }
168       }
169       get { return style.editText; }
170    };
171    property EditBox editBox { get { return editBox; } }
172    property Seconds typingTimeout { property_category $"Behavior" set { listBox.typingTimeout = value; } get { return listBox.typingTimeout; } };
173    property int rowHeight { property_category $"Appearance" set { listBox.rowHeight = value; } get { return listBox.rowHeight; } };
174    property int maxShown
175    {
176       property_category $"Behavior"
177       set
178       {
179          listBoxMaxShown = value;
180          OnPosition(position.x, position.y, clientSize.w, clientSize.h);
181       }
182       get { return listBoxMaxShown; }
183    };
184    property Window pullDown
185    {
186       get { return pulledWindow; }
187    }
188    property bool showNone
189    {
190       set
191       {
192          if(value != style.showNone)
193          {
194             if(style.showNone)
195             {
196                listBox.DeleteRow(noneRow);
197                noneRow = null;
198             }
199             style.showNone = value;
200             if(value)
201             {
202                noneRow = listBox.AddRowNone();
203             }
204          }
205       }
206       get { return style.showNone; }
207    }
208    property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
209    property Color selectionText  { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
210    property bool changeContents
211    {
212       set { style.changeContents = value; }
213       get { return style.changeContents; }
214    }
215
216    property ListBox listBox { get { return listBox; } }
217
218    property int rowCount { get { return listBox.rowCount; } }
219
220    // Notifications
221    virtual bool Window::NotifySelect(DropBox dropBox, DataRow row, Modifiers mods);
222    virtual bool Window::NotifyClose(DropBox dropBox);
223    virtual bool Window::NotifyHighlight(DropBox dropBox, DataRow row, Modifiers mods);
224    virtual bool Window::NotifyTextEntry(DropBox dropBox, const char * string, bool confirmed);
225
226    virtual Window OnDropDown()
227    {
228       // NASTY BUG ON WINDOWS IN PROPERTIES SHEET IF WE DON'T RECREATE THE LISTBOX HERE
229       // To reproduce, comment out, select borderStyle property, alt-tab to another program, come back, drop down
230       // The listBox goes annoyingly to 0, 0
231       listBox.Destroy(0);
232       listBox.Create();
233
234       ResizeListbox();
235       // listBox.visible = true;
236       return listBox;
237    }
238
239    virtual void OnCloseDropDown(Window pullDown)
240    {
241       incref this;
242       listBox.visible = false;
243       if(style.editText)
244       {
245          editBox.ActivateEx(true, false, false, false, null, null);
246          editBox.SelectAll();
247       }
248       delete this;
249    }
250
251    // Methods
252    DataRow AddStringf(const char * format, ...)
253    {
254       if(this)
255       {
256          DataRow row;
257
258          char string[MAX_F_STRING];
259          va_list args;
260
261          va_start(args, format);
262          vsnprintf(string, sizeof(string), format, args);
263          string[sizeof(string)-1] = 0;
264          va_end(args);
265
266          row = AddRow();
267          row.SetData(null, string);
268          return row;
269       }
270       return null;
271    }
272
273    DataRow AddString(const char * string)
274    {
275       if(this)
276       {
277          DataRow row;
278          row = AddRow();
279          row.SetData(null, string);
280          return row;
281       }
282       return null;
283    }
284
285    void DeleteRow(DataRow row)
286    {
287       if(!row) row = currentRow;
288       listBox.DeleteRow(row);
289       /*
290       if(row == currentRow)
291          currentRow = null;*/
292       Update(null);
293    }
294
295    // Convenience function using current rows
296    int64 GetTag()
297    {
298       return currentRow.tag;
299    }
300
301    void * SetData(DataField field, any_object data)
302    {
303       if(this)
304       {
305          if(currentRow)
306             return currentRow.SetData(field, data);
307       }
308       return null;
309    }
310
311    any_object GetData(DataField field)
312    {
313       return this ? currentRow.GetData(field) : null;
314    }
315
316    void Sort(DataField field, int order)
317    {
318       listBox.Sort(field, order);
319    }
320
321    void AddField(DataField field)
322    {
323       if(this)
324       {
325          if(!field)
326             field = DataField { alignment = alignment };
327
328          listBox.AddField(field);
329          this.field = listBox.firstField;
330          dataType = this.field.dataType;
331       }
332    }
333
334    DataRow AddRow()
335    {
336       DataRow row = null;
337       if(this)
338       {
339          row = listBox.AddRow();
340          OnPosition(position.x, position.y, clientSize.w, clientSize.h);
341       }
342       return row;
343    }
344
345    DataRow AddRowAfter(DataRow after)
346    {
347       DataRow row = null;
348       if(this)
349       {
350          row = listBox.AddRowAfter(after);
351          OnPosition(position.x, position.y, clientSize.w, clientSize.h);
352       }
353       return row;
354    }
355
356    int GetRowCount()
357    {
358       return listBox.rowCount;
359    }
360
361    void Clear()
362    {
363       if(this)
364       {
365          Window master = this.master;
366          listBox.Clear();
367
368          if(currentRow && master)
369          {
370             currentRow = null;
371             NotifySelect(master, this, null, 0);
372          }
373          currentRow = null;
374          if(style.editText && style.changeContents)
375             editBox.contents = "";
376          Update(null);
377          if(style.showNone)
378             noneRow = listBox.AddRowNone();
379       }
380    }
381
382    DataRow FindRow(int64 tag)
383    {
384       if(this)
385       {
386          return listBox.FindRow(tag);
387       }
388       return null;
389    }
390
391    DataRow FindSubRow(int64 tag)
392    {
393       if(this)
394       {
395          return listBox.FindSubRow(tag);
396       }
397       return null;
398    }
399
400    // Drop Box Specific
401    void SelectRow(DataRow row)
402    {
403       if(this)
404       {
405          currentRow = row;
406          if(style.editText && style.changeContents)
407          {
408             char tempString[4096];
409             if(currentRow)
410             {
411                const char *(* onGetString)(void *, void *, char *, void *, bool *) = (void *)dataType._vTbl[__ecereVMethodID_class_OnGetString];
412                editBox.contents = onGetString(dataType, currentRow.GetData(null), tempString, null, null);
413             }
414             else
415                editBox.contents = "";
416          }
417          listBox.SelectRow(currentRow ? currentRow : (style.showNone ? noneRow : null));
418
419          Update(null);
420       }
421    }
422
423    bool Save()
424    {
425       if(editBox && editBox.modifiedDocument)
426       {
427          NotifyTextEntry(master, this, editBox.contents, true);
428          if(editBox)
429             editBox.modifiedDocument = false;
430          return true;
431       }
432       return false;
433    }
434
435 private:
436    DropBox()
437    {
438       listBoxMaxShown = 1000;
439       field = listBox.firstField;
440       dataType = field.dataType;
441       style.changeContents = true;
442       return true;
443    }
444
445    ~DropBox()
446    {
447       delete editBox;
448    }
449
450    void ResizeListbox()
451    {
452       int rowCount = Min(listBox.rowCount, listBoxMaxShown);
453       int height = rowCount * ROW_HEIGHT;
454       listBox.clientSize = { clientSize.w, height };
455    }
456
457    void OnPosition(int x, int y, int width, int height)
458    {
459       if(pulledWindow)
460       {
461          int lx = absPosition.x - guiApp.desktop.absPosition.x;
462          int ly = absPosition.y - guiApp.desktop.absPosition.y;
463          int availHeight = pulledWindow.parent.clientSize.h;
464          int height = pulledWindow.clientSize.h;
465
466          // If it won't fit below but fits above, place it above...
467          if(ly + size.h + height > availHeight && height < ly)
468             ly -= height;
469          else
470          {
471             ly += size.h;
472             if(ly + height > availHeight)
473                height = ((availHeight - ly) / ROW_HEIGHT) * ROW_HEIGHT;
474          }
475
476          if(!pulledWindow.initSize.w)
477             pulledWindow.size.w = size.w;
478          else
479             // Align it to the right (pull down button)
480             lx -= pulledWindow.size.w - size.w;
481          pulledWindow.position = { lx, ly };
482          pulledWindow.clientSize = { pulledWindow.clientSize.w, height };
483       }
484    }
485
486    bool OnKeyDown(Key key, unichar ch)
487    {
488       switch(key)
489       {
490          case hotKey:
491          case space:
492          case right:
493          case escape:
494             // ADDED THIS CHECK HERE TO NAVIGATE THROUGH GRID LISTBOX
495             if(key == right)
496             {
497                if(eClass_IsDerived(master._class, class(DataBox)))
498                {
499                   if(eClass_IsDerived(master.master._class, class(ListBox)))
500                      return true;
501                }
502                else if(eClass_IsDerived(master._class, class(ListBox)))
503                   return true;
504             }
505             if(key == escape && !pulledWindow)
506             {
507                //if(editBox) button.Deactivate();
508                //if(editBox) button.Activate();
509                if(style.editText)
510                {
511                   if(editBox.modifiedDocument)
512                   {
513                      NotifyTextEntry(master, this, editBox.contents, false);
514                      editBox.modifiedDocument = false;
515                   }
516                   //editBox.Deactivate();
517                }
518                break;
519             }
520             if(style.editText && (key == space || key == right))
521                break;
522
523             if(pulledWindow)
524             {
525                OnCloseDropDown(pulledWindow);
526                pulledWindow = null;
527                button.checked = false;
528                NotifyClose(master, this);
529
530                if(style.editText)
531                {
532                   if(editBox.modifiedDocument)
533                   {
534                      NotifyTextEntry(master, this, editBox.contents, false);
535                      editBox.modifiedDocument = false;
536                   }
537                   editBox.Activate();
538                }
539             }
540             else
541             {
542                listBox.currentRow = currentRow ? currentRow : (style.showNone ? noneRow : null);
543                pulledWindow = OnDropDown();
544                OnPosition(position.x, position.y, clientSize.w, clientSize.h);
545                if(pulledWindow) pulledWindow.visible = true;
546                button.checked = true;
547             }
548             Update(null);
549             return false;
550          case enter:
551          case keyPadEnter:
552             if(!pulledWindow)
553             {
554                if(style.editText)
555                {
556                   if(editBox.modifiedDocument)
557                   {
558                      NotifyTextEntry(master, this, editBox.contents, true);
559                      editBox.modifiedDocument = false;
560                      //editBox.Deactivate();
561                   }
562
563                   // Add code to look through listbox and set current row if listbox is used
564                }
565                break;
566             }
567             else
568             {
569                incref this;
570
571                OnCloseDropDown(pulledWindow);
572                pulledWindow = null;
573                button.checked = false;
574                Update(null);
575
576                if(!NotifyClose(master, this))
577                {
578                   delete this;
579                   return false;
580                }
581
582                // Moved this from below NotifySelect
583                currentRow = listBox.currentRow;
584                if(currentRow && currentRow.noneRow) currentRow = null;
585                if(style.editText && style.changeContents)
586                {
587                   char tempString[4096];
588                   if(currentRow)
589                   {
590                      const char *(* onGetString)(void *, void *, char *, void *, bool *) = (void *)dataType._vTbl[__ecereVMethodID_class_OnGetString];
591                      editBox.contents = onGetString(dataType, currentRow.GetData(null), tempString, null, null);
592                   }
593                   else
594                      editBox.contents = "";
595                }
596
597                NotifySelect(master, this, currentRow, key.modifiers | { closingDropDown = true });
598
599                delete this;
600                return false;
601             }
602             return false;
603          //default:
604          //   return listBox.OnKeyDown(key, ch);
605       }
606       return true;
607    }
608
609    void OnApplyGraphics()
610    {
611       button.anchor = { right = 0, top = 0, bottom = 0 };
612       button.size = { guiApp.textMode ? 8 : BTN_WIDTH, 0 };
613    }
614
615    /*Timer timer
616    {
617       window = this, delay = 0.01;
618
619       bool DelayExpired()
620       {
621          background = active ? red : lime;
622          parent.parent.background = parent.parent.active ? red : lime;
623          guiApp.desktop.Update(null);
624          return true;
625       }
626    };*/
627
628    void OnRedraw(Surface surface)
629    {
630       //timer.started = true;
631       if(!style.editText)
632       {
633          if(active && !pulledWindow/*listBox.visible*/)
634          {
635             if(style.activeColor)
636             {
637                surface.SetBackground(selectionColor ? selectionColor : SELECTION_COLOR);
638                if(!style.noHighlight || !currentRow)
639                {
640                   surface.Area(0,0, clientSize.w-1,clientSize.h-1);
641                }
642             }
643          }
644          if(!isEnabled)
645             surface.SetForeground(Color { 85,85,85 } );
646          else
647             surface.SetForeground((active && style.activeColor && !pulledWindow /*listBox.visible*/) ? selectionText : foreground);
648          surface.TextOpacity(true);
649
650          if(currentRow)
651          {
652             DataDisplayFlags displayFlags { active = active, current = true, dropBox = true, selected = true, fullRow = true };
653             void (* onDisplay)(void *, void *, void *, int, int, int, void *, uint, uint) = (void *)dataType._vTbl[__ecereVMethodID_class_OnDisplay];
654             if(onDisplay)
655                onDisplay(dataType, currentRow.GetData(null), surface, 3,
656                   1+(clientSize.h - listBox.rowHeight) / 2, clientSize.w - (button.visible ? button.size.w : 0) - 3,
657                   field.userData, alignment, displayFlags);
658          }
659          else
660             surface.WriteText(2,2, "(none)", 6);
661
662          if(!style.noStipple && (!style.noHighlight || !currentRow))
663          {
664             surface.SetForeground(0xFFFFFF80);
665             if(active && !pulledWindow /*listBox.visible*/)
666             {
667                surface.Rectangle(0,0,clientSize.w-1-BTN_WIDTH,clientSize.h-1);
668                surface.SetForeground(black);
669                surface.LineStipple(0xAAAA);
670                surface.Rectangle(0,0,clientSize.w-1-BTN_WIDTH,clientSize.h-1);
671                surface.LineStipple(0);
672             }
673          }
674       }
675    }
676
677    bool OnResizing(int *width, int *height)
678    {
679       int rowHeight = 0;
680
681       display.FontExtent(fontObject, "W", 1, null, &rowHeight);
682       rowHeight = Max(rowHeight, 16);
683
684       if(!*width) *width = Max(*width, rowHeight * 100 / 16);
685
686       //if(!*width) *width = Max(*width, 100);
687       //if(!*height) *height = Max(*height, 20);
688       if(!*height) *height = Max(*height, rowHeight * 20 / 16);
689
690       return true;
691    }
692
693    /*
694    watch(font)
695    {
696       SetInitSize(initSize);
697    };
698    */
699
700    bool OnLeftButtonDown(int x, int y, Modifiers mods)
701    {
702       Update(null);
703       if(pulledWindow)
704       {
705          OnCloseDropDown(pulledWindow);
706          button.checked = false;
707          pulledWindow = null;
708
709          if(!NotifyClose(master, this))
710             return false;
711       }
712       else
713       {
714          listBox.currentRow = currentRow ? currentRow : (style.showNone ? noneRow : null);
715          pulledWindow = OnDropDown();
716          OnPosition(position.x, position.y, clientSize.w, clientSize.h);
717          if(pulledWindow) pulledWindow.visible = true;
718          if(listBox.freeSelect)
719             pulledWindow.OnLeftButtonDown(-2,-2, 0);
720          button.checked = true;
721       }
722       return true;
723    }
724
725    bool OnKeyHit(Key key, unichar ch)
726    {
727       DataRow currentRow = this.currentRow;
728
729       if(!pulledWindow /*listBox.visible*/)
730       {
731          int c;
732          if(style.showNone && !currentRow) currentRow = noneRow;
733
734          switch(key)
735          {
736             case wheelDown:
737             case down: currentRow = currentRow ? currentRow.next : null; break;
738             case wheelUp:
739             case up:   currentRow = currentRow ? currentRow.prev : null; break;
740             case pageDown:
741                for(c = 0; c<listBoxMaxShown && currentRow && currentRow.next; c++, currentRow = currentRow.next);
742                break;
743             case pageUp:
744                for(c = 0; c<listBoxMaxShown && currentRow && currentRow.prev; c++, currentRow = currentRow.prev);
745                break;
746             case end:
747                for(; currentRow && currentRow.next; currentRow = currentRow.next);
748                break;
749             case home:
750                for(; currentRow && currentRow.prev; currentRow = currentRow.prev);
751                break;
752             default:
753                if(!editBox || button.active || !editBox.active || editBox.OnKeyHit(key, ch))
754                {
755                   return listBox.OnKeyHit(key, ch);
756                }
757                else
758                   return false;
759          }
760          if(currentRow)
761          {
762             if(currentRow.noneRow) currentRow = null;
763             property::currentRow = currentRow;
764             //return
765                NotifySelect(master, this, currentRow, key.modifiers);
766             //return false;
767          }
768          return false;
769       }
770       else
771       {
772          if(listBox.vertScroll)
773          {
774             if(key == wheelUp)
775                listBox.vertScroll.Action(up, 0, key);
776             else if(key == wheelDown)
777                listBox.vertScroll.Action(down, 0, key);
778          }
779          return listBox.OnKeyHit(key, ch);
780       }
781       return true;
782    }
783
784    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
785    {
786       // WAS !active JUST MISSING HERE ?
787       if(!active && style.editText)
788       {
789          if(editBox.modifiedDocument)
790          {
791             if(editBox) editBox.modifiedDocument = false;
792             NotifyTextEntry(master, this, editBox.contents, true); //false);
793             // MOVED THIS ABOVE TO AVOID INFINITE RECURSION
794             // if(editBox) editBox.modifiedDocument = false;
795          }
796          //editBox.Deactivate();
797       }
798       Update(null);
799       return true;
800    }
801
802    public Button button
803    {
804       this, toggle = true, bevel = true, inactive = true,
805       anchor = Anchor { right = 0, top = 0, bottom = 0 },
806       size = Size { BTN_WIDTH, 0 },
807       symbol = 25;
808       bitmap = { "<:ecere>elements/arrowDown.png" };
809
810       bool NotifyPushed(Button control, int x, int y, Modifiers mods)
811       {
812          if(pulledWindow)
813          {
814             OnCloseDropDown(pulledWindow);
815             pulledWindow = null;
816             button.checked = false;
817             NotifyClose(master, this);
818          }
819          else
820          {
821             incref this;
822             if(editBox && editBox.modifiedDocument)
823             {
824                NotifyTextEntry(master, this, editBox.contents, true);
825                editBox.modifiedDocument = false;
826             }
827             pulledWindow = OnDropDown();
828             if(pulledWindow)
829             {
830                OnPosition(position.x, position.y, clientSize.w, clientSize.h);
831                if(listBox.freeSelect)
832                   pulledWindow.OnLeftButtonDown(-2,-2, 0);
833                button.checked = true;
834                pulledWindow.visible = true;
835             }
836             delete this;
837          }
838          Update(null);
839          return true;
840       }
841    };
842
843    ListBox listBox
844    {
845       master = this, fullRowSelect = true, freeSelect = true, /* noHighlight = style.noHighlight , */
846       interim = true, hasVertScroll = true, borderStyle = contour, position = { absPosition.x, absPosition.y + size.h },
847       size.w = size.w, visible = false;
848
849       bool NotifyActivate(Window control, bool active, Window swap)
850       {
851          if(!active)
852          {
853             Update(null);
854             if(swap != button && swap != this)
855             {
856                if(editBox && editBox.modifiedDocument)
857                {
858                   NotifyTextEntry(master, this, editBox.contents, true);
859                   editBox.modifiedDocument = false;
860                }
861
862                OnCloseDropDown(pulledWindow);
863                pulledWindow = null;
864                button.checked = false;
865                NotifyClose(master, this);
866             }
867          }
868          return true;
869       }
870
871       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
872       {
873          bool result = true;
874          incref this;
875          Update(null);
876
877          // Add code to set text to list box contents if it has an editbox
878
879          if(pulledWindow)
880          {
881             OnCloseDropDown(pulledWindow);
882             pulledWindow = null;
883             button.checked = false;
884             mods.closingDropDown = true;
885             if(!NotifyClose(master, this))
886             {
887                delete this;
888                return false;
889             }
890          }
891          currentRow = (row && !row.noneRow) ? row : null;
892          if(style.editText && style.changeContents)
893          {
894             char tempString[4096];
895             if(currentRow)
896             {
897                const char *(* onGetString)(void *, void *, char *, void *, bool *) = (void *)dataType._vTbl[__ecereVMethodID_class_OnGetString];
898                editBox.contents = onGetString(dataType, currentRow.GetData(null), tempString, null, null);
899             }
900             else
901                editBox.contents = "";
902             if(active)
903                editBox.SelectAll();
904          }
905          button.Deactivate();
906          result = NotifySelect(master, this, currentRow, mods);
907          delete this;
908          return result;
909       }
910
911       bool NotifyHighlight(ListBox control, DataRow row, Modifiers mods)
912       {
913          return NotifyHighlight(master, this, row, mods);
914       }
915
916       bool NotifyKeyDown(ListBox listBox, DataRow selection, Key key, unichar ch)
917       {
918          return OnKeyDown(key, ch);
919          //return true;
920       }
921    };
922
923    bool OnPostCreate()
924    {
925       if(font)
926          listBox.font = { font.faceName, font.size };
927       return true;
928    }
929
930    DropBoxBits style;
931    Window pulledWindow;
932    DataRow currentRow;
933    int listBoxMaxShown;
934    Class dataType;
935    DataField field;
936    EditBox editBox;
937    Alignment alignment;
938    DataRow noneRow;
939    ColorAlpha selectionColor, selectionText;
940
941    selectionColor = SELECTION_COLOR;
942    selectionText = SELECTION_TEXT;
943 };