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