1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.tabcontrol;
7 
8 private import dfl.internal.dlib;
9 
10 private import dfl.control, dfl.panel, dfl.internal.winapi, dfl.drawing;
11 private import dfl.application, dfl.event, dfl.base, dfl.collections;
12 
13 
14 private extern(Windows) void _initTabcontrol();
15 
16 
17 ///
18 class TabPage: Panel
19 {
20 	///
21 	this(Dstring tabText)
22 	{
23 		this();
24 		
25 		this.text = tabText;
26 	}
27 	
28 	/+
29 	/// ditto
30 	this(Object v) // package
31 	{
32 		this(getObjectString(v));
33 	}
34 	+/
35 	
36 	/// ditto
37 	this()
38 	{
39 		Application.ppin(cast(void*)this);
40 		
41 		ctrlStyle |= ControlStyles.CONTAINER_CONTROL;
42 		
43 		wstyle &= ~WS_VISIBLE;
44 		cbits &= ~CBits.VISIBLE;
45 	}
46 	
47 	
48 	override Dstring toString()
49 	{
50 		return text;
51 	}
52 	
53 
54 	alias Control.opEquals opEquals;
55 
56 	
57 	override Dequ opEquals(Object o)
58 	{
59 		return text == getObjectString(o);
60 	}
61 
62 	
63 	Dequ opEquals(Dstring val)
64 	{
65 		return text == val;
66 	}
67 	
68 	
69 	alias Control.opCmp opCmp;
70 
71 
72 	override int opCmp(Object o)
73 	{
74 		return stringICmp(text, getObjectString(o));
75 	}
76 
77 
78 	int opCmp(Dstring val)
79 	{
80 		return stringICmp(text, val);
81 	}
82 	
83 	
84 	// imageIndex
85 	
86 	
87 	override @property void text(Dstring newText) // setter
88 	{
89 		// Note: this probably causes toStringz() to be called twice,
90 		// allocating 2 of the same string.
91 		
92 		super.text = newText;
93 		
94 		if(created)
95 		{
96 			TabControl tc;
97 			tc = cast(TabControl)parent;
98 			if(tc)
99 				tc.updateTabText(this, newText);
100 		}
101 	}
102 	
103 	alias Panel.text text; // Overload with Panel.text.
104 	
105 	
106 	/+
107 	final @property void toolTipText(Dstring ttt) // setter
108 	{
109 		// TODO: ...
110 	}
111 	
112 	
113 	final @property Dstring toolTipText() // getter
114 	{
115 		// TODO: ...
116 		return null;
117 	}
118 	+/
119 	
120 	
121 	/+ package +/ /+ protected +/ override int _rtype() // package
122 	{
123 		return 4;
124 	}
125 	
126 	
127 	protected override void setBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
128 	{
129 		assert(0); // Cannot set bounds of TabPage; it is done automatically.
130 	}
131 	
132 	
133 	package final @property void realBounds(Rect r) // setter
134 	{
135 		// DMD 0.124: if I don't put this here, super.setBoundsCore ends up calling setBoundsCore instead of super.setBoundsCore.
136 		void delegate(int, int, int, int, BoundsSpecified) _foo = &setBoundsCore;
137 		
138 		super.setBoundsCore(r.x, r.y, r.width, r.height, BoundsSpecified.ALL);
139 	}
140 	
141 	
142 	protected override void setVisibleCore(bool byes)
143 	{
144 		assert(0); // Cannot set visibility of TabPage; it is done automatically.
145 	}
146 	
147 	
148 	package final @property void realVisible(bool byes) // setter
149 	{
150 		// DMD 0.124: if I don't put this here, super.setVisibleCore ends up calling setVisibleCore instead of super.setVisibleCore.
151 		void delegate(bool byes) _foo = &setVisibleCore;
152 		
153 		super.setVisibleCore(byes);
154 	}
155 }
156 
157 
158 package union TcItem
159 {
160 	TC_ITEMW tciw;
161 	TC_ITEMA tcia;
162 	struct
163 	{
164 		UINT mask;
165 		UINT lpReserved1;
166 		UINT lpReserved2;
167 		private void* pszText;
168 		int cchTextMax;
169 		int iImage;
170 		LPARAM lParam;
171 	}
172 }
173 
174 
175 ///
176 class TabPageCollection
177 {
178 	protected this(TabControl owner)
179 	in
180 	{
181 		assert(owner.tchildren is null);
182 	}
183 	body
184 	{
185 		tc = owner;
186 	}
187 	
188 	
189 	private:
190 	
191 	TabControl tc;
192 	TabPage[] _pages = null;
193 	
194 	
195 	void doPages()
196 	in
197 	{
198 		assert(created);
199 	}
200 	body
201 	{
202 		Rect area;
203 		area = tc.displayRectangle;
204 		
205 		Message m;
206 		m.hWnd = tc.handle;
207 		
208 		// Note: duplicate code.
209 		//TC_ITEMA tci;
210 		TcItem tci;
211 		if(dfl.internal.utf.useUnicode)
212 		{
213 			m.msg = TCM_INSERTITEMW; // <--
214 			foreach(int i, TabPage page; _pages)
215 			{
216 				// TODO: TCIF_RTLREADING flag based on rightToLeft property.
217 				tci.mask = TCIF_TEXT | TCIF_PARAM;
218 				tci.tciw.pszText = cast(typeof(tci.tciw.pszText))dfl.internal.utf.toUnicodez(page.text); // <--
219 				static assert(tci.lParam.sizeof >= (void*).sizeof);
220 				tci.lParam = cast(LPARAM)cast(void*)page;
221 				
222 				m.wParam = i;
223 				m.lParam = cast(LPARAM)&tci.tciw;
224 				tc.prevWndProc(m);
225 				assert(cast(int)m.result != -1);
226 			}
227 		}
228 		else
229 		{
230 			m.msg = TCM_INSERTITEMA; // <--
231 			foreach(int i, TabPage page; _pages)
232 			{
233 				// TODO: TCIF_RTLREADING flag based on rightToLeft property.
234 				tci.mask = TCIF_TEXT | TCIF_PARAM;
235 				tci.tcia.pszText = cast(typeof(tci.tcia.pszText))dfl.internal.utf.toAnsiz(page.text); // <--
236 				static assert(tci.lParam.sizeof >= (void*).sizeof);
237 				tci.lParam = cast(LPARAM)cast(void*)page;
238 				
239 				m.wParam = i;
240 				m.lParam = cast(LPARAM)&tci.tcia;
241 				tc.prevWndProc(m);
242 				assert(cast(int)m.result != -1);
243 			}
244 		}
245 	}
246 	
247 	
248 	package final @property bool created() // getter
249 	{
250 		return tc && tc.created();
251 	}
252 	
253 	
254 	void _added(size_t idx, TabPage val)
255 	{
256 		if(val.parent)
257 		{
258 			TabControl tc;
259 			tc = cast(TabControl)val.parent;
260 			if(tc && tc.tabPages.indexOf(val) != -1)
261 				throw new DflException("TabPage already has a parent");
262 		}
263 		
264 		//val.realVisible = false;
265 		assert(val.visible == false);
266 		assert(!(tc is null));
267 		val.parent = tc;
268 		
269 		if(created)
270 		{
271 			Message m;
272 			//TC_ITEMA tci;
273 			TcItem tci;
274 			// TODO: TCIF_RTLREADING flag based on rightToLeft property.
275 			tci.mask = TCIF_TEXT | TCIF_PARAM;
276 			static assert(tci.lParam.sizeof >= (void*).sizeof);
277 			tci.lParam = cast(LPARAM)cast(void*)val;
278 			if(dfl.internal.utf.useUnicode)
279 			{
280 				tci.tciw.pszText = cast(typeof(tci.tciw.pszText))dfl.internal.utf.toUnicodez(val.text);
281 				m = Message(tc.handle, TCM_INSERTITEMW, idx, cast(LPARAM)&tci.tciw);
282 			}
283 			else
284 			{
285 				tci.tcia.pszText = cast(typeof(tci.tcia.pszText))dfl.internal.utf.toAnsiz(val.text);
286 				m = Message(tc.handle, TCM_INSERTITEMA, idx, cast(LPARAM)&tci.tcia);
287 			}
288 			tc.prevWndProc(m);
289 			assert(cast(int)m.result != -1);
290 			
291 			if(tc.selectedTab is val)
292 			{
293 				//val.realVisible = true;
294 				tc.tabToFront(val);
295 			}
296 		}
297 	}
298 	
299 	
300 	void _removed(size_t idx, TabPage val)
301 	{
302 		if(size_t.max == idx) // Clear all.
303 		{
304 			if(created)
305 			{
306 				Message m;
307 				m = Message(tc.handle, TCM_DELETEALLITEMS, 0, 0);
308 				tc.prevWndProc(m);
309 			}
310 		}
311 		else
312 		{
313 			//val.parent = null; // Can't do that.
314 			
315 			if(created)
316 			{
317 				Message m;
318 				m = Message(tc.handle, TCM_DELETEITEM, idx, 0);
319 				tc.prevWndProc(m);
320 				
321 				// Hide this one.
322 				val.realVisible = false;
323 				
324 				// Show next visible.
325 				val = tc.selectedTab;
326 				if(val)
327 					tc.tabToFront(val);
328 			}
329 		}
330 	}
331 	
332 	
333 	public:
334 	
335 	mixin ListWrapArray!(TabPage, _pages,
336 		_blankListCallback!(TabPage), _added,
337 		_blankListCallback!(TabPage), _removed,
338 		true, false, false,
339 		true); // CLEAR_EACH
340 }
341 
342 
343 ///
344 enum TabAlignment: ubyte
345 {
346 	TOP, ///
347 	BOTTOM, /// ditto
348 	LEFT, /// ditto
349 	RIGHT, /// ditto
350 }
351 
352 
353 ///
354 enum TabAppearance: ubyte
355 {
356 	NORMAL, ///
357 	BUTTONS, /// ditto
358 	FLAT_BUTTONS, /// ditto
359 }
360 
361 
362 ///
363 enum TabDrawMode: ubyte
364 {
365 	NORMAL, ///
366 	OWNER_DRAW_FIXED, /// ditto
367 }
368 
369 
370 ///
371 class TabControlBase: ControlSuperClass
372 {
373 	this()
374 	{
375 		_initTabcontrol();
376 		
377 		wstyle |= WS_TABSTOP;
378 		ctrlStyle |= ControlStyles.SELECTABLE | ControlStyles.CONTAINER_CONTROL;
379 		wclassStyle = tabcontrolClassStyle;
380 	}
381 	
382 	
383 	///
384 	final @property void drawMode(TabDrawMode dm) // setter
385 	{
386 		switch(dm)
387 		{
388 			case TabDrawMode.OWNER_DRAW_FIXED:
389 				_style(wstyle | TCS_OWNERDRAWFIXED);
390 				break;
391 			
392 			case TabDrawMode.NORMAL:
393 				_style(wstyle & ~TCS_OWNERDRAWFIXED);
394 				break;
395 			
396 			default:
397 				assert(0);
398 		}
399 		
400 		_crecreate();
401 	}
402 	
403 	/// ditto
404 	final @property TabDrawMode drawMode() // getter
405 	{
406 		if(wstyle & TCS_OWNERDRAWFIXED)
407 			return TabDrawMode.OWNER_DRAW_FIXED;
408 		return TabDrawMode.NORMAL;
409 	}
410 	
411 	
412 	override @property Rect displayRectangle() // getter
413 	{
414 		if(!created)
415 		{
416 			return super.displayRectangle(); // Hack?
417 		}
418 		else
419 		{
420 			RECT drr;
421 			Message m;
422 			drr.left = 0;
423 			drr.top = 0;
424 			drr.right = clientSize.width;
425 			drr.bottom = clientSize.height;
426 			m = Message(hwnd, TCM_ADJUSTRECT, FALSE, cast(LPARAM)&drr);
427 			prevWndProc(m);
428 			return Rect(&drr);
429 		}
430 	}
431 	
432 	
433 	protected override @property Size defaultSize() // getter
434 	{
435 		return Size(200, 200); // ?
436 	}
437 	
438 	
439 	///
440 	final Rect getTabRect(int i)
441 	{
442 		Rect result;
443 		
444 		if(created)
445 		{
446 			RECT rt;
447 			Message m;
448 			m = Message(hwnd, TCM_GETITEMRECT, cast(WPARAM)i, cast(LPARAM)&rt);
449 			prevWndProc(m);
450 			if(!m.result)
451 				goto rtfail;
452 			result = Rect(&rt);
453 		}
454 		else
455 		{
456 			rtfail:
457 			with(result)
458 			{
459 				x = 0;
460 				y = 0;
461 				width = 0;
462 				height = 0;
463 			}
464 		}
465 		
466 		return result;
467 	}
468 	
469 	
470 	// drawItem event.
471 	//EventHandler selectedIndexChanged;
472 	Event!(TabControlBase, EventArgs) selectedIndexChanged; ///
473 	//CancelEventHandler selectedIndexChanging;
474 	Event!(TabControlBase, CancelEventArgs) selectedIndexChanging; ///
475 	
476 	
477 	protected override void createParams(ref CreateParams cp)
478 	{
479 		super.createParams(cp);
480 		
481 		cp.className = TABCONTROL_CLASSNAME;
482 	}
483 	
484 	
485 	///
486 	protected void onSelectedIndexChanged(EventArgs ea)
487 	{
488 		selectedIndexChanged(this, ea);
489 	}
490 	
491 	
492 	///
493 	protected void onSelectedIndexChanging(CancelEventArgs ea)
494 	{
495 		selectedIndexChanging(this, ea);
496 	}
497 	
498 	
499 	protected override void prevWndProc(ref Message msg)
500 	{
501 		//msg.result = CallWindowProcA(tabcontrolPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
502 		msg.result = dfl.internal.utf.callWindowProc(tabcontrolPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
503 	}
504 	
505 	
506 	protected override void wndProc(ref Message m)
507 	{
508 		// TODO: support the tab control messages.
509 		
510 		switch(m.msg)
511 		{
512 			/+
513 			case WM_SETFOCUS:
514 				_exStyle(_exStyle() | WS_EX_CONTROLPARENT);
515 				break;
516 			
517 			case WM_KILLFOCUS:
518 				_exStyle(_exStyle() & ~WS_EX_CONTROLPARENT);
519 				break;
520 			+/
521 			
522 			case TCM_DELETEALLITEMS:
523 				m.result = FALSE;
524 				return;
525 			
526 			case TCM_DELETEITEM:
527 				m.result = FALSE;
528 				return;
529 			
530 			case TCM_INSERTITEMA:
531 			case TCM_INSERTITEMW:
532 				m.result = -1;
533 				return;
534 			
535 			//case TCM_REMOVEIMAGE:
536 			//	return;
537 			
538 			//case TCM_SETIMAGELIST:
539 			//	m.result = cast(LRESULT)null;
540 			//	return;
541 			
542 			case TCM_SETITEMA:
543 			case TCM_SETITEMW:
544 				m.result = FALSE;
545 				return;
546 			
547 			case TCM_SETITEMEXTRA:
548 				m.result = FALSE;
549 				return;
550 			
551 			case TCM_SETITEMSIZE:
552 				m.result = 0;
553 				return;
554 			
555 			case TCM_SETPADDING:
556 				return;
557 			
558 			case TCM_SETTOOLTIPS:
559 				return;
560 			
561 			default:
562 		}
563 		
564 		super.wndProc(m);
565 	}
566 	
567 	
568 	protected override void onReflectedMessage(ref Message m)
569 	{
570 		super.onReflectedMessage(m);
571 		
572 		TabPage page;
573 		NMHDR* nmh;
574 		nmh = cast(NMHDR*)m.lParam;
575 		
576 		switch(nmh.code)
577 		{
578 			case TCN_SELCHANGE:
579 				onSelectedIndexChanged(EventArgs.empty);
580 				break;
581 			
582 			case TCN_SELCHANGING:
583 				{
584 					scope CancelEventArgs ea = new CancelEventArgs;
585 					onSelectedIndexChanging(ea);
586 					if(ea.cancel)
587 					{
588 						m.result = TRUE; // Prevent change.
589 						return;
590 					}
591 				}
592 				m.result = FALSE; // Allow change.
593 				return;
594 			
595 			default:
596 		}
597 	}
598 }
599 
600 
601 ///
602 class TabControl: TabControlBase // docmain
603 {
604 	this()
605 	{
606 		tchildren = new TabPageCollection(this);
607 		_pad = Point(6, 3);
608 	}
609 	
610 	
611 	///
612 	final @property void alignment(TabAlignment talign) // setter
613 	{
614 		switch(talign)
615 		{
616 			case TabAlignment.TOP:
617 				_style(wstyle & ~(TCS_VERTICAL | TCS_RIGHT | TCS_BOTTOM));
618 				break;
619 			
620 			case TabAlignment.BOTTOM:
621 				_style((wstyle & ~(TCS_VERTICAL | TCS_RIGHT)) | TCS_BOTTOM);
622 				break;
623 			
624 			case TabAlignment.LEFT:
625 				_style((wstyle & ~(TCS_BOTTOM | TCS_RIGHT)) | TCS_VERTICAL);
626 				break;
627 			
628 			case TabAlignment.RIGHT:
629 				_style((wstyle & ~TCS_BOTTOM) | TCS_VERTICAL | TCS_RIGHT);
630 				break;
631 			
632 			default:
633 				assert(0);
634 		}
635 		
636 		// Display rectangle changed.
637 		
638 		if(created && visible)
639 		{
640 			invalidate(true); // Update children too ?
641 			
642 			TabPage page;
643 			page = selectedTab;
644 			if(page)
645 				page.realBounds = displayRectangle;
646 		}
647 	}
648 	
649 	/// ditto
650 	final @property TabAlignment alignment() // getter
651 	{
652 		// Note: TCS_RIGHT and TCS_BOTTOM are the same flag.
653 		
654 		if(wstyle & TCS_VERTICAL)
655 		{
656 			if(wstyle & TCS_RIGHT)
657 				return TabAlignment.RIGHT;
658 			return TabAlignment.LEFT;
659 		}
660 		else
661 		{
662 			if(wstyle & TCS_BOTTOM)
663 				return TabAlignment.BOTTOM;
664 			return TabAlignment.TOP;
665 		}
666 	}
667 	
668 	
669 	///
670 	final @property void appearance(TabAppearance tappear) // setter
671 	{
672 		switch(tappear)
673 		{
674 			case TabAppearance.NORMAL:
675 				_style(wstyle & ~(TCS_BUTTONS | TCS_FLATBUTTONS));
676 				break;
677 			
678 			case TabAppearance.BUTTONS:
679 				_style((wstyle & ~TCS_FLATBUTTONS) | TCS_BUTTONS);
680 				break;
681 			
682 			case TabAppearance.FLAT_BUTTONS:
683 				_style(wstyle | TCS_BUTTONS | TCS_FLATBUTTONS);
684 				break;
685 			
686 			default:
687 				assert(0);
688 		}
689 		
690 		if(created && visible)
691 		{
692 			invalidate(false);
693 			
694 			TabPage page;
695 			page = selectedTab;
696 			if(page)
697 				page.realBounds = displayRectangle;
698 		}
699 	}
700 	
701 	/// ditto
702 	final @property TabAppearance appearance() // getter
703 	{
704 		if(wstyle & TCS_FLATBUTTONS)
705 			return TabAppearance.FLAT_BUTTONS;
706 		if(wstyle & TCS_BUTTONS)
707 			return TabAppearance.BUTTONS;
708 		return TabAppearance.NORMAL;
709 	}
710 	
711 	
712 	///
713 	final @property void padding(Point pad) // setter
714 	{
715 		if(created)
716 		{
717 			SendMessageA(hwnd, TCM_SETPADDING, 0, MAKELPARAM(pad.x, pad.y));
718 			
719 			TabPage page;
720 			page = selectedTab;
721 			if(page)
722 				page.realBounds = displayRectangle;
723 		}
724 		
725 		_pad = pad;
726 	}
727 	
728 	/// ditto
729 	final @property Point padding() // getter
730 	{
731 		return _pad;
732 	}
733 	
734 	
735 	///
736 	final @property TabPageCollection tabPages() // getter
737 	{
738 		return tchildren;
739 	}
740 	
741 	
742 	///
743 	final @property void multiline(bool byes) // setter
744 	{
745 		if(byes)
746 			_style(_style() | TCS_MULTILINE);
747 		else
748 			_style(_style() & ~TCS_MULTILINE);
749 		
750 		TabPage page;
751 		page = selectedTab;
752 		if(page)
753 			page.realBounds = displayRectangle;
754 	}
755 	
756 	/// ditto
757 	final @property bool multiline() // getter
758 	{
759 		return (_style() & TCS_MULTILINE) != 0;
760 	}
761 	
762 	
763 	///
764 	final @property int rowCount() // getter
765 	{
766 		if(!created || !multiline)
767 			return 0;
768 		Message m;
769 		m = Message(hwnd, TCM_GETROWCOUNT, 0, 0);
770 		prevWndProc(m);
771 		return cast(int)m.result;
772 	}
773 	
774 	
775 	///
776 	final @property int tabCount() // getter
777 	{
778 		return tchildren._pages.length;
779 	}
780 	
781 	
782 	///
783 	final @property void selectedIndex(int i) // setter
784 	{
785 		if(!created || !tchildren._pages.length)
786 			return;
787 		
788 		TabPage curpage;
789 		curpage = selectedTab;
790 		if(curpage is tchildren._pages[i])
791 			return; // Already selected.
792 		curpage.realVisible = false;
793 		
794 		SendMessageA(hwnd, TCM_SETCURSEL, cast(WPARAM)i, 0);
795 		tabToFront(tchildren._pages[i]);
796 	}
797 	
798 	/// ditto
799 	// Returns -1 if there are no tabs selected.
800 	final @property int selectedIndex() // getter
801 	{
802 		if(!created || !tchildren._pages.length)
803 			return -1;
804 		Message m;
805 		m = Message(hwnd, TCM_GETCURSEL, 0, 0);
806 		prevWndProc(m);
807 		return cast(int)m.result;
808 	}
809 	
810 	
811 	///
812 	final @property void selectedTab(TabPage page) // setter
813 	{
814 		int i;
815 		i = tabPages.indexOf(page);
816 		if(-1 != i)
817 			selectedIndex = i;
818 	}
819 	
820 	/// ditto
821 	final @property TabPage selectedTab() // getter
822 	{
823 		int i;
824 		i = selectedIndex;
825 		if(-1 == i)
826 			return null;
827 		return tchildren._pages[i];
828 	}
829 	
830 	
831 	/+
832 	///
833 	final @property void showToolTips(bool byes) // setter
834 	{
835 		if(byes)
836 			_style(_style() | TCS_TOOLTIPS);
837 		else
838 			_style(_style() & ~TCS_TOOLTIPS);
839 	}
840 	
841 	/// ditto
842 	final @property bool showToolTips() // getter
843 	{
844 		return (_style() & TCS_TOOLTIPS) != 0;
845 	}
846 	+/
847 	
848 	
849 	protected override void onHandleCreated(EventArgs ea)
850 	{
851 		super.onHandleCreated(ea);
852 		
853 		SendMessageA(hwnd, TCM_SETPADDING, 0, MAKELPARAM(_pad.x, _pad.y));
854 		
855 		tchildren.doPages();
856 		
857 		// Bring selected tab to front.
858 		if(tchildren._pages.length)
859 		{
860 			int i;
861 			i = selectedIndex;
862 			if(-1 != i)
863 				tabToFront(tchildren._pages[i]);
864 		}
865 	}
866 	
867 	
868 	protected override void onLayout(LayoutEventArgs ea)
869 	{
870 		if(tchildren._pages.length)
871 		{
872 			int i;
873 			i = selectedIndex;
874 			if(-1 != i)
875 			{
876 				tchildren._pages[i].realBounds = displayRectangle;
877 				//assert(tchildren._pages[i].bounds == displayRectangle);
878 			}
879 		}
880 		
881 		//super.onLayout(ea); // Tab control shouldn't even have other controls on it.
882 		super.onLayout(ea); // Should call it for consistency. Ideally it just checks handlers.length == 0 and does nothing.
883 	}
884 	
885 	
886 	/+
887 	protected override void wndProc(ref Message m)
888 	{
889 		// TODO: support the tab control messages.
890 		
891 		switch(m.msg)
892 		{
893 			/+ // Now handled in onLayout().
894 			case WM_WINDOWPOSCHANGED:
895 				{
896 					WINDOWPOS* wp;
897 					wp = cast(WINDOWPOS*)m.lParam;
898 					
899 					if(!(wp.flags & SWP_NOSIZE) || (wp.flags & SWP_FRAMECHANGED))
900 					{
901 						if(tchildren._pages.length)
902 						{
903 							int i;
904 							i = selectedIndex;
905 							if(-1 != i)
906 							{
907 								tchildren._pages[i].realBounds = displayRectangle;
908 								//assert(tchildren._pages[i].bounds == displayRectangle);
909 							}
910 						}
911 					}
912 				}
913 				break;
914 			+/
915 			
916 			default:
917 		}
918 		
919 		super.wndProc(m);
920 	}
921 	+/
922 	
923 	
924 	protected override void onReflectedMessage(ref Message m)
925 	{
926 		TabPage page;
927 		NMHDR* nmh;
928 		nmh = cast(NMHDR*)m.lParam;
929 		
930 		switch(nmh.code)
931 		{
932 			case TCN_SELCHANGE:
933 				page = selectedTab;
934 				if(page)
935 					tabToFront(page);
936 				super.onReflectedMessage(m);
937 				break;
938 			
939 			case TCN_SELCHANGING:
940 				super.onReflectedMessage(m);
941 				if(!m.result) // Allowed.
942 				{
943 					page = selectedTab;
944 					if(page)
945 						page.realVisible = false;
946 				}
947 				return;
948 			
949 			default:
950 				super.onReflectedMessage(m);
951 		}
952 	}
953 	
954 	
955 	/+
956 	/+ package +/ /+ protected +/ override int _rtype() // package
957 	{
958 		return 0x20;
959 	}
960 	+/
961 	
962 	
963 	private:
964 	Point _pad;
965 	TabPageCollection tchildren;
966 	
967 	
968 	void tabToFront(TabPage page)
969 	{
970 		page.realBounds = displayRectangle;
971 		//page.realVisible = true;
972 		SetWindowPos(page.handle, HWND_TOP, 0, 0, 0, 0, /+ SWP_NOACTIVATE | +/ SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
973 		assert(page.visible == true);
974 		
975 		/+
976 		// Make sure the previous tab isn't still focused.
977 		// Will "steal" focus if done programatically.
978 		SetFocus(handle);
979 		//SetFocus(page.handle);
980 		+/
981 	}
982 	
983 	
984 	void updateTabText(TabPage page, Dstring newText)
985 	in
986 	{
987 		assert(created);
988 	}
989 	body
990 	{
991 		int i;
992 		i = tabPages.indexOf(page);
993 		assert(-1 != i);
994 		
995 		//TC_ITEMA tci;
996 		TcItem tci;
997 		tci.mask = TCIF_TEXT;
998 		Message m;
999 		if(dfl.internal.utf.useUnicode)
1000 		{
1001 			tci.tciw.pszText = cast(typeof(tci.tciw.pszText))dfl.internal.utf.toUnicodez(newText);
1002 			m = Message(hwnd, TCM_SETITEMW, cast(WPARAM)i, cast(LPARAM)&tci.tciw);
1003 		}
1004 		else
1005 		{
1006 			tci.tcia.pszText = cast(typeof(tci.tcia.pszText))dfl.internal.utf.toAnsiz(newText);
1007 			m = Message(hwnd, TCM_SETITEMA, cast(WPARAM)i, cast(LPARAM)&tci.tcia);
1008 		}
1009 		prevWndProc(m);
1010 		
1011 		// Updating a tab's text could cause tab rows to be adjusted,
1012 		// so update the selected tab's area.
1013 		page = selectedTab;
1014 		if(page)
1015 			page.realBounds = displayRectangle;
1016 	}
1017 }
1018