1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.statusbar;
7 
8 
9 private import dfl.control, dfl.base, dfl.internal.winapi, dfl.event,
10 	dfl.collections, dfl.internal.utf, dfl.internal.dlib, dfl.application;
11 
12 private import dfl.internal.dlib;
13 
14 
15 private extern(Windows) void _initStatusbar();
16 
17 
18 /+
19 enum StatusBarPanelAutoSize: ubyte
20 {
21 	NONE,
22 	CONTENTS,
23 	SPRING,
24 }
25 +/
26 
27 
28 ///
29 enum StatusBarPanelBorderStyle: ubyte
30 {
31 	NONE, ///
32 	SUNKEN, /// ditto
33 	RAISED /// ditto
34 }
35 
36 
37 ///
38 class StatusBarPanel: DObject
39 {
40 	///
41 	this(Dstring text)
42 	{
43 		this._txt = text;
44 	}
45 	
46 	/// ditto
47 	this(Dstring text, int width)
48 	{
49 		this._txt = text;
50 		this._width = width;
51 	}
52 	
53 	/// ditto
54 	this()
55 	{
56 	}
57 	
58 	
59 	override Dstring toString()
60 	{
61 		return _txt;
62 	}
63 	
64 	
65 	override Dequ opEquals(Object o)
66 	{
67 		return _txt == getObjectString(o); // ?
68 	}
69 	
70 	Dequ opEquals(StatusBarPanel pnl)
71 	{
72 		return _txt == pnl._txt;
73 	}
74 	
75 	Dequ opEquals(Dstring val)
76 	{
77 		return _txt == val;
78 	}
79 	
80 	
81 	override int opCmp(Object o)
82 	{
83 		return stringICmp(_txt, getObjectString(o)); // ?
84 	}
85 	
86 	int opCmp(StatusBarPanel pnl)
87 	{
88 		return stringICmp(_txt, pnl._txt);
89 	}
90 	
91 	int opCmp(Dstring val)
92 	{
93 		return stringICmp(_txt, val);
94 	}
95 	
96 	
97 	/+
98 	///
99 	final @property void alignment(HorizontalAlignment ha) // setter
100 	{
101 		
102 	}
103 	
104 	/// ditto
105 	final @property HorizontalAlignment alignment() // getter
106 	{
107 		//LEFT
108 	}
109 	+/
110 	
111 	
112 	/+
113 	///
114 	final @property void autoSize(StatusBarPanelAutoSize asize) // setter
115 	{
116 		
117 	}
118 	
119 	/// ditto
120 	final @property StatusBarPanelAutoSize autoSize() // getter
121 	{
122 		//NONE
123 	}
124 	+/
125 	
126 	
127 	///
128 	final @property void borderStyle(StatusBarPanelBorderStyle bs) // setter
129 	{
130 		switch(bs)
131 		{
132 			case StatusBarPanelBorderStyle.NONE:
133 				_utype = (_utype & ~SBT_POPOUT) | SBT_NOBORDERS;
134 				break;
135 			
136 			case StatusBarPanelBorderStyle.RAISED:
137 				_utype = (_utype & ~SBT_NOBORDERS) | SBT_POPOUT;
138 				break;
139 			
140 			case StatusBarPanelBorderStyle.SUNKEN:
141 				_utype &= ~(SBT_NOBORDERS | SBT_POPOUT);
142 				break;
143 			
144 			default:
145 				assert(0);
146 		}
147 		
148 		if(_parent && _parent.isHandleCreated)
149 		{
150 			_parent.panels._fixtexts(); // Also fixes styles.
151 		}
152 	}
153 	
154 	/// ditto
155 	final @property StatusBarPanelBorderStyle borderStyle() // getter
156 	{
157 		if(_utype & SBT_POPOUT)
158 			return StatusBarPanelBorderStyle.RAISED;
159 		if(_utype & SBT_NOBORDERS)
160 			return StatusBarPanelBorderStyle.NONE;
161 		return StatusBarPanelBorderStyle.RAISED;
162 	}
163 	
164 	
165 	// icon
166 	
167 	
168 	/+
169 	///
170 	final @property void minWidth(int mw) // setter
171 	in
172 	{
173 		assert(mw >= 0);
174 	}
175 	body
176 	{
177 		
178 	}
179 	
180 	/// ditto
181 	final @property int minWidth() // getter
182 	{
183 		//10
184 	}
185 	+/
186 	
187 	
188 	///
189 	final @property StatusBar parent() // getter
190 	{
191 		return _parent;
192 	}
193 	
194 	
195 	// style
196 	
197 	
198 	///
199 	final @property void text(Dstring txt) // setter
200 	{
201 		if(_parent && _parent.isHandleCreated)
202 		{
203 			int idx = _parent.panels.indexOf(this);
204 			assert(-1 != idx);
205 			_parent._sendidxtext(idx, _utype, txt);
206 		}
207 		
208 		this._txt = txt;
209 	}
210 	
211 	/// ditto
212 	final @property Dstring text() // getter
213 	{
214 		return _txt;
215 	}
216 	
217 	
218 	/+
219 	///
220 	final @property void toolTipText(Dstring txt) // setter
221 	{
222 		
223 	}
224 	
225 	/// ditto
226 	final @property Dstring toolTipText() // getter
227 	{
228 		//null
229 	}
230 	+/
231 	
232 	
233 	///
234 	final @property void width(int w) // setter
235 	{
236 		_width = w;
237 		
238 		if(_parent && _parent.isHandleCreated)
239 		{
240 			_parent.panels._fixwidths();
241 		}
242 	}
243 	
244 	/// ditto
245 	final @property int width() // getter
246 	{
247 		return _width;
248 	}
249 	
250 	
251 	private:
252 	
253 	Dstring _txt = null;
254 	int _width = 100;
255 	StatusBar _parent = null;
256 	WPARAM _utype = 0; // StatusBarPanelBorderStyle.SUNKEN.
257 }
258 
259 
260 /+
261 ///
262 class StatusBarPanelClickEventArgs: MouseEventArgs
263 {
264 	///
265 	this(StatusBarPanel sbpanel, MouseButtons btn, int clicks, int x, int y)
266 	{
267 		this._sbpanel = sbpanel;
268 		super(btn, clicks, x, y, 0);
269 	}
270 	
271 	
272 	private:
273 	StatusBarPanel _sbpanel;
274 }
275 +/
276 
277 
278 ///
279 class StatusBar: ControlSuperClass // docmain
280 {
281 	///
282 	class StatusBarPanelCollection
283 	{
284 		protected this(StatusBar sb)
285 		in
286 		{
287 			assert(sb.lpanels is null);
288 		}
289 		body
290 		{
291 			this.sb = sb;
292 		}
293 		
294 		
295 		private:
296 		
297 		StatusBar sb;
298 		package StatusBarPanel[] _panels;
299 		
300 		
301 		package void _fixwidths()
302 		{
303 			assert(isHandleCreated);
304 			
305 			UINT[20] _pws = void;
306 			UINT[] pws = _pws;
307 			if(_panels.length > _pws.length)
308 				pws = new UINT[_panels.length];
309 			UINT right = 0;
310 			foreach(idx, pnl; _panels)
311 			{
312 				if(-1 == pnl.width)
313 				{
314 					pws[idx] = -1;
315 				}
316 				else
317 				{
318 					right += pnl.width;
319 					pws[idx] = right;
320 				}
321 			}
322 			sb.prevwproc(SB_SETPARTS, cast(WPARAM)_panels.length, cast(LPARAM)pws.ptr);
323 		}
324 		
325 		
326 		void _fixtexts()
327 		{
328 			assert(isHandleCreated);
329 			
330 			if(dfl.internal.utf.useUnicode)
331 			{
332 				foreach(idx, pnl; _panels)
333 				{
334 					sb.prevwproc(SB_SETTEXTW, cast(WPARAM)idx | pnl._utype, cast(LPARAM)dfl.internal.utf.toUnicodez(pnl._txt));
335 				}
336 			}
337 			else
338 			{
339 				foreach(idx, pnl; _panels)
340 				{
341 					sb.prevwproc(SB_SETTEXTA, cast(WPARAM)idx | pnl._utype, cast(LPARAM)dfl.internal.utf.toAnsiz(pnl._txt));
342 				}
343 			}
344 		}
345 		
346 		
347 		void _setcurparts()
348 		{
349 			assert(isHandleCreated);
350 			
351 			_fixwidths();
352 			
353 			_fixtexts();
354 		}
355 		
356 		
357 		void _removed(size_t idx, Object val)
358 		{
359 			if(size_t.max == idx) // Clear all.
360 			{
361 				if(sb.isHandleCreated)
362 				{
363 					sb.prevwproc(SB_SETPARTS, 0, 0); // 0 parts.
364 				}
365 			}
366 			else
367 			{
368 				if(sb.isHandleCreated)
369 				{
370 					_setcurparts();
371 				}
372 			}
373 		}
374 		
375 		
376 		void _added(size_t idx, StatusBarPanel val)
377 		{
378 			if(val._parent)
379 				throw new DflException("StatusBarPanel already belongs to a StatusBar");
380 			
381 			val._parent = sb;
382 			
383 			if(sb.isHandleCreated)
384 			{
385 				_setcurparts();
386 			}
387 		}
388 		
389 		
390 		void _adding(size_t idx, StatusBarPanel val)
391 		{
392 			if(_panels.length >= 254) // Since SB_SETTEXT with 255 has special meaning.
393 				throw new DflException("Too many status bar panels");
394 		}
395 		
396 		
397 		public:
398 		
399 		mixin ListWrapArray!(StatusBarPanel, _panels,
400 			_adding, _added,
401 			_blankListCallback!(StatusBarPanel), _removed,
402 			true, /+true+/ false, false) _wraparray;
403 	}
404 	
405 	
406 	///
407 	this()
408 	{
409 		_initStatusbar();
410 		
411 		_issimple = true;
412 		wstyle |= SBARS_SIZEGRIP;
413 		wclassStyle = statusbarClassStyle;
414 		//height = ?;
415 		dock = DockStyle.BOTTOM;
416 		
417 		lpanels = new StatusBarPanelCollection(this);
418 	}
419 	
420 	
421 	// backColor / font / foreColor ...
422 	
423 	
424 	override @property void dock(DockStyle ds) // setter
425 	{
426 		switch(ds)
427 		{
428 			case DockStyle.BOTTOM:
429 			case DockStyle.TOP:
430 				super.dock = ds;
431 				break;
432 			
433 			default:
434 				throw new DflException("Invalid status bar dock");
435 		}
436 	}
437 	
438 	alias Control.dock dock; // Overload.
439 	
440 	
441 	///
442 	final @property StatusBarPanelCollection panels() // getter
443 	{
444 		return lpanels;
445 	}
446 	
447 	
448 	///
449 	final @property void showPanels(bool byes) // setter
450 	{
451 		if(!byes == _issimple)
452 			return;
453 		
454 		if(isHandleCreated)
455 		{
456 			prevwproc(SB_SIMPLE, cast(WPARAM)!byes, 0);
457 			
458 			/+ // It's kept in sync even if simple.
459 			if(byes)
460 			{
461 				panels._setcurparts();
462 			}
463 			+/
464 			
465 			if(!byes)
466 			{
467 				_sendidxtext(255, 0, _simpletext);
468 			}
469 		}
470 		
471 		_issimple = !byes;
472 	}
473 	
474 	/// ditto
475 	final @property bool showPanels() // getter
476 	{
477 		return !_issimple;
478 	}
479 	
480 	
481 	///
482 	final @property void sizingGrip(bool byes) // setter
483 	{
484 		if(byes == sizingGrip)
485 			return;
486 		
487 		if(byes)
488 			_style(_style() | SBARS_SIZEGRIP);
489 		else
490 			_style(_style() & ~SBARS_SIZEGRIP);
491 	}
492 	
493 	/// ditto
494 	final @property bool sizingGrip() // getter
495 	{
496 		if(wstyle & SBARS_SIZEGRIP)
497 			return true;
498 		return false;
499 	}
500 	
501 	
502 	override @property void text(Dstring txt) // setter
503 	{
504 		if(isHandleCreated && !showPanels)
505 		{
506 			_sendidxtext(255, 0, txt);
507 		}
508 		
509 		this._simpletext = txt;
510 		
511 		onTextChanged(EventArgs.empty);
512 	}
513 	
514 	/// ditto
515 	override @property Dstring text() // getter
516 	{
517 		return this._simpletext;
518 	}
519 	
520 	
521 	protected override void onHandleCreated(EventArgs ea)
522 	{
523 		super.onHandleCreated(ea);
524 		
525 		if(_issimple)
526 		{
527 			prevwproc(SB_SIMPLE, cast(WPARAM)true, 0);
528 			panels._setcurparts();
529 			if(_simpletext.length)
530 				_sendidxtext(255, 0, _simpletext);
531 		}
532 		else
533 		{
534 			panels._setcurparts();
535 			prevwproc(SB_SIMPLE, cast(WPARAM)false, 0);
536 		}
537 	}
538 	
539 	
540 	protected override void createParams(ref CreateParams cp)
541 	{
542 		super.createParams(cp);
543 		
544 		cp.className = STATUSBAR_CLASSNAME;
545 	}
546 	
547 	
548 	protected override void prevWndProc(ref Message msg)
549 	{
550 		//msg.result = CallWindowProcA(statusbarPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
551 		msg.result = dfl.internal.utf.callWindowProc(statusbarPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
552 	}
553 	
554 	
555 	/+
556 	protected override void createHandle()
557 	{
558 		//CreateStatusWindow
559 	}
560 	+/
561 	
562 	
563 	//StatusBarPanelClickEventHandler panelClick;
564 	//Event!(StatusBar, StatusBarPanelClickEventArgs) panelClick; ///
565 	
566 	
567 	protected:
568 	
569 	// onDrawItem ...
570 	
571 	
572 	/+
573 	///
574 	void onPanelClick(StatusBarPanelClickEventArgs ea)
575 	{
576 		panelClick(this, ea);
577 	}
578 	+/
579 	
580 	
581 	private:
582 	
583 	StatusBarPanelCollection lpanels;
584 	Dstring _simpletext = null;
585 	bool _issimple = true;
586 	
587 	
588 	package:
589 	final:
590 	
591 	LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam)
592 	{
593 		//return CallWindowProcA(statusbarPrevWndProc, hwnd, msg, wparam, lparam);
594 		return dfl.internal.utf.callWindowProc(statusbarPrevWndProc, hwnd, msg, wparam, lparam);
595 	}
596 	
597 	
598 	void _sendidxtext(int idx, WPARAM utype, Dstring txt)
599 	{
600 		assert(isHandleCreated);
601 		
602 		if(dfl.internal.utf.useUnicode)
603 			prevwproc(SB_SETTEXTW, cast(WPARAM)idx | utype, cast(LPARAM)dfl.internal.utf.toUnicodez(txt));
604 		else
605 			prevwproc(SB_SETTEXTA, cast(WPARAM)idx | utype, cast(LPARAM)dfl.internal.utf.toAnsiz(txt));
606 	}
607 }
608