1 /** DGui project file.
2 
3 Copyright: Trogu Antonio Davide 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Trogu Antonio Davide
8 */
9 module dguihub.combobox;
10 
11 import std.utf : toUTFz;
12 import dguihub.core.controls.subclassedcontrol;
13 import dguihub.core.utils;
14 public import dguihub.imagelist;
15 
16 enum DropDownStyles : uint {
17    none = 0, // Internal Use
18    simple = CBS_SIMPLE,
19    dropdown = CBS_DROPDOWN,
20    dropdownList = CBS_DROPDOWNLIST,
21 }
22 
23 class ComboBoxItem {
24    private ComboBox _owner;
25    private string _text;
26    private int _imgIndex = -1;
27 
28    mixin tagProperty;
29 
30    package this(string txt, int idx = -1) {
31       this._text = txt;
32       this._imgIndex = idx;
33    }
34 
35    @property public final int index() {
36       if (this._owner.items) {
37          foreach (int i, ComboBoxItem cbi; this._owner.items) {
38             if (cbi is this) {
39                return i;
40             }
41          }
42       }
43 
44       return -1;
45    }
46 
47    @property public final ComboBox comboBox() {
48       return this._owner;
49    }
50 
51    @property package void comboBox(ComboBox cbx) {
52       this._owner = cbx;
53    }
54 
55    @property public final int imageIndex() {
56       return this._imgIndex;
57    }
58 
59    @property public final void imageIndex(int idx) {
60       this._imgIndex = idx;
61 
62       if (this._owner && this._owner.created) {
63          COMBOBOXEXITEMW cbei;
64 
65          cbei.mask = CBEIF_IMAGE;
66          cbei.iImage = idx;
67          cbei.iItem = this.index;
68 
69          this._owner.sendMessage(CBEM_SETITEMW, 0, cast(LPARAM)&cbei);
70       }
71    }
72 
73    @property public final string text() {
74       return this._text;
75    }
76 
77    @property public final void text(string txt) {
78       this._text = txt;
79 
80       if (this._owner && this._owner.created) {
81          COMBOBOXEXITEMW cbei;
82 
83          cbei.mask = CBEIF_TEXT;
84          cbei.pszText = toUTFz!(wchar*)(txt);
85          cbei.iItem = this.index;
86 
87          this._owner.sendMessage(CBEM_SETITEMW, 0, cast(LPARAM)&cbei);
88       }
89    }
90 }
91 
92 class ComboBox : SubclassedControl {
93    public Event!(Control, EventArgs) itemChanged;
94 
95    private Collection!(ComboBoxItem) _items;
96    private DropDownStyles _oldDDStyle = DropDownStyles.none;
97    private int _selectedIndex;
98    private ImageList _imgList;
99 
100    public this() {
101       this.setStyle(DropDownStyles.dropdown, true);
102    }
103 
104    public final ComboBoxItem addItem(string s, int imgIndex = -1) {
105       if (!this._items) {
106          this._items = new Collection!(ComboBoxItem);
107       }
108 
109       ComboBoxItem cbi = new ComboBoxItem(s, imgIndex);
110       this._items.add(cbi);
111 
112       if (this.created) {
113          return this.insertItem(cbi);
114       }
115 
116       return cbi;
117    }
118 
119    public final void removeItem(int idx) {
120       if (this.created) {
121          this.sendMessage(CB_DELETESTRING, idx, 0);
122       }
123 
124       this._items.removeAt(idx);
125    }
126 
127    @property public final int selectedIndex() {
128       if (this.created) {
129          return this.sendMessage(CB_GETCURSEL, 0, 0);
130       }
131 
132       return this._selectedIndex;
133    }
134 
135    @property public final void selectedIndex(int i) {
136       this._selectedIndex = i;
137 
138       if (this.created) {
139          this.sendMessage(CB_SETCURSEL, i, 0);
140       }
141    }
142 
143    public void clear() {
144       if (this._items) {
145          foreach (ComboBoxItem cbi; this._items) {
146             this.sendMessage(CB_DELETESTRING, 0, 0);
147          }
148 
149          this._items.clear();
150       }
151 
152       this.selectedIndex = -1;
153    }
154 
155    @property public final ComboBoxItem selectedItem() {
156       if (this.created) {
157          return this._items[this._selectedIndex];
158       } else {
159          int idx = this.selectedIndex;
160 
161          if (this._items) {
162             return this._items[idx];
163          }
164       }
165 
166       return null;
167    }
168 
169    @property public override bool focused() {
170       if (this.created) {
171          return GetFocus() == cast(HWND)this.sendMessage(CBEM_GETCOMBOCONTROL, 0, 0);
172       }
173 
174       return false;
175    }
176 
177    @property public final ImageList imageList() {
178       return this._imgList;
179    }
180 
181    @property public void imageList(ImageList imgList) {
182       this._imgList = imgList;
183 
184       if (this.created) {
185          this.sendMessage(CBEM_SETIMAGELIST, 0, cast(LPARAM)this._imgList.handle);
186       }
187    }
188 
189    @property public final void dropDownStyle(DropDownStyles dds) {
190       if (dds !is this._oldDDStyle) {
191          this.setStyle(this._oldDDStyle, false); //Rimuovo il vecchio
192          this.setStyle(dds, true); //Aggiungo il nuovo
193 
194          this._oldDDStyle = dds;
195       }
196    }
197 
198    @property public final ComboBoxItem[] items() {
199       if (this._items) {
200          return this._items.get();
201       }
202 
203       return null;
204    }
205 
206    private ComboBoxItem insertItem(ComboBoxItem cbi) {
207       COMBOBOXEXITEMW cbei;
208 
209       cbei.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_LPARAM;
210       cbei.iItem = -1;
211       cbei.iImage = cbi.imageIndex;
212       cbei.iSelectedImage = cbi.imageIndex;
213       cbei.pszText = toUTFz!(wchar*)(cbi.text);
214       cbei.lParam = winCast!(LPARAM)(cbi);
215 
216       this.sendMessage(CBEM_INSERTITEMW, 0, cast(LPARAM)&cbei);
217       cbi.comboBox = this;
218       return cbi;
219    }
220 
221    protected void onItemChanged(EventArgs e) {
222       this.itemChanged(this, e);
223    }
224 
225    protected override void createControlParams(ref CreateControlParams ccp) {
226       // Use Original Paint Routine, the double buffered one causes some issues
227 
228       ccp.superclassName = WC_COMBOBOXEX;
229       ccp.className = WC_DCOMBOBOX;
230 
231       this.setStyle(WS_CLIPCHILDREN | WS_CLIPSIBLINGS, true); //Clip child ComboBox
232       //this.setStyle(CBS_NOINTEGRALHEIGHT, true);
233 
234       super.createControlParams(ccp);
235    }
236 
237    protected override void onHandleCreated(EventArgs e) {
238       if (this._imgList) {
239          this.sendMessage(CBEM_SETIMAGELIST, 0, cast(LPARAM)this._imgList.handle);
240       }
241 
242       if (this._items) {
243          foreach (ComboBoxItem cbi; this._items) {
244             this.insertItem(cbi);
245          }
246       }
247 
248       if (this._selectedIndex != -1) {
249          this.sendMessage(CB_SETCURSEL, this._selectedIndex, 0);
250       }
251 
252       super.onHandleCreated(e);
253    }
254 
255    protected override void onReflectedMessage(ref Message m) {
256       if (m.msg == WM_COMMAND && HIWORD(m.wParam) == CBN_SELCHANGE) {
257          this._selectedIndex = this.sendMessage(CB_GETCURSEL, 0, 0);
258          this.onItemChanged(EventArgs.empty);
259       }
260 
261       super.onReflectedMessage(m);
262    }
263 
264    protected override void wndProc(ref Message m) {
265       switch (m.msg) {
266       case WM_COMMAND: {
267             /* Retrieve focus notifications from child ComboBox  */
268             if (HIWORD(m.wParam) == CBN_SETFOCUS || HIWORD(m.wParam) == CBN_KILLFOCUS) {
269                this.onFocusChanged(EventArgs.empty);
270             }
271 
272             super.wndProc(m);
273          }
274          break;
275 
276       case WM_SETFOCUS, WM_KILLFOCUS:
277          this.originalWndProc(m); //Don't send focusChanged event here!
278          break;
279 
280       default:
281          super.wndProc(m);
282          break;
283       }
284    }
285 }