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