* TreeView.cs: Don't draw the selected node when we lose
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TabControl.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24
25
26 using System;
27 using System.Collections;
28 using System.ComponentModel;
29 using System.ComponentModel.Design;
30 using System.Drawing;
31
32
33 namespace System.Windows.Forms {
34         [DefaultEvent("SelectedIndexChanged")]
35         [DefaultProperty("TabPages")]
36         [Designer("System.Windows.Forms.Design.TabControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
37         public class TabControl : Control {
38                 #region Fields
39                 private int selected_index = -1;
40                 private TabAlignment alignment;
41                 private TabAppearance appearance;
42                 private TabDrawMode draw_mode;
43                 private bool multiline;
44                 private ImageList image_list;
45                 private Size item_size = Size.Empty;
46                 private Point padding;
47                 private int row_count = 1;
48                 private bool hottrack;
49                 private TabPageCollection tab_pages;
50                 private bool show_tool_tips;
51                 private TabSizeMode size_mode;
52                 private Rectangle display_rect;
53                 private bool show_slider = false;
54                 private ButtonState right_slider_state;
55                 private ButtonState left_slider_state;
56                 private int slider_pos = 0;
57                 #endregion      // Fields
58
59                 #region Public Constructors
60                 public TabControl ()
61                 {
62                         tab_pages = new TabPageCollection (this);
63                         SetStyle (ControlStyles.UserPaint, false);
64                         padding = ThemeEngine.Current.TabControlDefaultPadding;
65                         item_size = ThemeEngine.Current.TabControlDefaultItemSize;
66
67                         MouseDown += new MouseEventHandler (MouseDownHandler);
68                         MouseUp += new MouseEventHandler (MouseUpHandler);
69                         SizeChanged += new EventHandler (SizeChangedHandler);
70                 }
71
72                 #endregion      // Public Constructors
73
74                 #region Public Instance Properties
75                 [DefaultValue(TabAlignment.Top)]
76                 [Localizable(true)]
77                 [RefreshProperties(RefreshProperties.All)]
78                 public TabAlignment Alignment {
79                         get { return alignment; }
80                         set {
81                                 if (alignment == value)
82                                         return;
83                                 alignment = value;
84                                 if (alignment == TabAlignment.Left || alignment == TabAlignment.Right)
85                                         multiline = true;
86                                 Redraw ();
87                         }
88                 }
89
90                 [DefaultValue(TabAppearance.Normal)]
91                 [Localizable(true)]
92                 public TabAppearance Appearance {
93                         get { return appearance; }
94                         set {
95                                 if (appearance == value)
96                                         return;
97                                 appearance = value;
98                                 Redraw ();
99                         }
100                 }
101
102                 [Browsable(false)]
103                 [EditorBrowsable(EditorBrowsableState.Never)]
104                 public override Color BackColor {
105                         get { return base.BackColor; }
106                         set { /* nothing happens on set on MS */ }
107                 }
108
109                 [Browsable(false)]
110                 [EditorBrowsable(EditorBrowsableState.Never)]
111                 public override Image BackgroundImage {
112                         get { return base.BackgroundImage; }
113                         set { base.BackgroundImage = value; }
114                 }
115
116                 public override Rectangle DisplayRectangle {
117                         get {
118                                 return ThemeEngine.Current.GetTabControlDisplayRectangle (this);
119                         }
120                 }
121
122                 [DefaultValue(TabDrawMode.Normal)]
123                 public TabDrawMode DrawMode {
124                         get { return draw_mode; }
125                         set {
126                                 if (draw_mode == value)
127                                         return;
128                                 draw_mode = value;
129                                 Redraw ();
130                         }
131                 }
132
133                 [Browsable(false)]
134                 [EditorBrowsable(EditorBrowsableState.Never)]
135                 public override Color ForeColor {
136                         get { return base.ForeColor; }
137                         set { base.ForeColor = value; }
138                 }
139
140                 [DefaultValue(false)]
141                 public bool HotTrack {
142                         get { return hottrack; }
143                         set {
144                                 if (hottrack == value)
145                                         return;
146                                 hottrack = value;
147                                 Redraw ();
148                         }
149                 }
150
151                 [DefaultValue(null)]
152                 public ImageList ImageList {
153                         get { return image_list; }
154                         set { image_list = value; }
155                 }
156
157                 [Localizable(true)]
158                 public Size ItemSize {
159                         get {
160                                 return item_size;
161                         }
162                         set {
163                                 if (value.Height < 0 || value.Width < 0)
164                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
165                                 item_size = value;
166                                 Redraw ();
167                         }
168                 }
169
170                 [DefaultValue(false)]
171                 public bool Multiline {
172                         get { return multiline; }
173                         set {
174                                 if (multiline == value)
175                                         return;
176                                 multiline = value;
177                                 if (!multiline && alignment == TabAlignment.Left || alignment == TabAlignment.Right)
178                                         alignment = TabAlignment.Top;
179                                 Redraw ();
180                         }
181                 }
182
183                 [Localizable(true)]
184                 public Point Padding {
185                         get { return padding; }
186                         set {
187                                 if (value.X < 0 || value.Y < 0)
188                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'Padding'.");
189                                 if (padding == value)
190                                         return;
191                                 padding = value;
192                                 Redraw ();
193                         }
194
195                 }
196
197                 [Browsable(false)]
198                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
199                 public int RowCount {
200                         get { return row_count; }
201                 }
202
203                 [DefaultValue(-1)]
204                 [Browsable(false)]
205                 public int SelectedIndex {
206                         get { return selected_index; }
207                         set {
208                                 if (selected_index == value)
209                                         return;
210                                 if (selected_index < -1) {
211                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
212                                                         "'value' must be greater than or equal to -1.");
213                                 }
214
215                                 SuspendLayout ();
216
217                                 Rectangle invalid = Rectangle.Empty;
218                                 bool refresh = false;
219                                 
220                                 if (-1 != value && show_slider && value < slider_pos) {
221                                         slider_pos = value;
222                                         refresh = true;
223                                 }
224
225                                 int le = TabPages [value].TabBounds.Right;
226                                 int re = ThemeEngine.Current.GetTabControlLeftScrollRect (this).Left;
227
228                                 if (-1 != value && show_slider && le > re) {
229                                         int diff = le - re;
230                                         int ind = value - 1;
231                                         while (ind > 0 && diff > 0) {
232                                                 diff -= TabPages [ind++].Width;
233                                         }
234                                         slider_pos = ind - 1;
235                                         refresh = true;
236                                 }
237
238                                 if (selected_index != -1) {
239                                         if (!refresh)
240                                                 invalid = GetTabRect (selected_index);
241                                         Controls [selected_index].Visible = false;
242                                 }
243                                 selected_index = value;
244
245                                 OnSelectedIndexChanged (EventArgs.Empty);
246
247                                 TabPage selected = (TabPage) Controls [selected_index];
248
249                                 if (selected_index != -1) {
250                                         invalid = Rectangle.Union (invalid, GetTabRect (selected_index));
251                                         selected.Visible = true;
252                                         // selected.Focus ();
253                                 }
254
255                                 ResumeLayout ();
256
257                                 if (refresh) {
258                                         SizeTabs ();
259                                         Refresh ();
260                                 } else if (selected_index != -1 && selected.Row != BottomRow) {
261                                         DropRow (TabPages [selected_index].Row);
262                                         // calculating what to invalidate here seems to be slower then just
263                                         // refreshing the whole thing
264                                         SizeTabs ();
265                                         Refresh ();
266                                 } else {
267                                         SizeTabs ();
268                                         // The lines are drawn on the edges of the tabs so the invalid area should
269                                         // needs to include the extra pixels of line width.
270                                         if (appearance == TabAppearance.Normal)
271                                                 invalid.Inflate (6, 4);
272                                         Invalidate (invalid);
273                                 }
274                         }
275                 }
276
277                 [Browsable(false)]
278                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
279                 public TabPage SelectedTab {
280                         get {
281                                 if (selected_index == -1)
282                                         return null;
283                                 return tab_pages [selected_index];
284                         }
285                         set {
286                                 int index = IndexForTabPage (value);
287                                 if (index == selected_index)
288                                         return;
289                                 SelectedIndex = index;
290                         }
291                 }
292
293                 [DefaultValue(false)]
294                 [Localizable(true)]
295                 public bool ShowToolTips {
296                         get { return show_tool_tips; }
297                         set {
298                                 if (show_tool_tips == value)
299                                         return;
300                                 show_tool_tips = value;
301                                 Redraw ();
302                         }
303                 }
304
305                 [DefaultValue(TabSizeMode.Normal)]
306                 [RefreshProperties(RefreshProperties.Repaint)]
307                 public TabSizeMode SizeMode {
308                         get { return size_mode; }
309                         set {
310                                 if (size_mode == value)
311                                         return;
312                                 size_mode = value;
313                                 Redraw ();
314                         }
315                 }
316
317                 [Browsable(false)]
318                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
319                 public int TabCount {
320                         get {
321                                 return tab_pages.Count;
322                         }
323                 }
324
325                 [DefaultValue(null)]
326                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
327                 [MergableProperty(false)]
328                 public TabPageCollection TabPages {
329                         get { return tab_pages; }
330                 }
331
332                 [Browsable(false)]
333                 [Bindable(false)]
334                 [EditorBrowsable(EditorBrowsableState.Never)]
335                 public override string Text {
336                         get { return base.Text; }
337                         set { base.Text = value; }
338                 }
339
340                 #endregion      // Public Instance Properties
341
342                 #region Internal Properties
343                 internal bool ShowSlider {
344                         get { return show_slider; }
345                         set { show_slider = value; }
346                 }
347
348                 internal int SliderPos {
349                         get { return slider_pos; }
350                 }
351
352                 internal ButtonState RightSliderState {
353                         get { return right_slider_state; }
354                 }
355
356                 internal ButtonState LeftSliderState {
357                         get { return left_slider_state; }
358                 }
359
360                 private Size DefaultItemSize {
361                         get {
362                                 return ThemeEngine.Current.TabControlDefaultItemSize;
363                         }
364                 }
365
366                 #endregion      // Internal Properties
367
368                 #region Protected Instance Properties
369                 protected override CreateParams CreateParams {
370                         get {
371                                 CreateParams c = base.CreateParams;
372                                 return c;
373                         }
374                 }
375
376                 protected override Size DefaultSize {
377                         get { return new Size (200, 100); }  
378                 }
379
380                 #endregion      // Protected Instance Properties
381
382                 #region Public Instance Methods
383                 public Rectangle GetTabRect (int index)
384                 {
385                         TabPage page = GetTab (index);
386                         return page.TabBounds;
387                 }
388
389                 public Control GetControl (int index)
390                 {
391                         return GetTab (index);
392                 }
393
394                 public override string ToString ()
395                 {
396                         string res = String.Concat (base.ToString (),
397                                         ", TabPages.Count: ",
398                                         TabCount);
399                         if (TabCount > 0)
400                                 res = String.Concat (res, ", TabPages[0]: ",
401                                                 TabPages [0]);
402                         return res;
403                 }
404
405                 #endregion      // Public Instance Methods
406
407                 #region Protected Instance Methods
408                 protected override Control.ControlCollection CreateControlsInstance ()
409                 {
410                         return new TabControl.ControlCollection (this);
411                 }
412
413                 protected override void CreateHandle ()
414                 {
415                         base.CreateHandle ();
416                         ResizeTabPages ();
417                 }
418
419                 protected override void OnHandleCreated (EventArgs e)
420                 {
421                         base.OnHandleCreated (e);
422                 }
423
424                 protected override void OnHandleDestroyed (EventArgs e)
425                 {
426                         base.OnHandleDestroyed (e);
427                 }
428
429                 protected override void OnFontChanged (EventArgs e)
430                 {
431                         base.OnFontChanged (e);
432                         ResizeTabPages ();
433                 }
434
435                 protected override void OnResize (EventArgs e)
436                 {
437                         base.OnResize (e);
438                 }
439
440                 protected override void OnStyleChanged (EventArgs e)
441                 {
442                         base.OnStyleChanged (e);
443                 }
444
445                 protected override bool ProcessKeyPreview (ref Message m)
446                 {
447                         if (ProcessKeyEventArgs (ref m))
448                                 return true;
449                         return base.ProcessKeyPreview (ref m);
450                 }
451
452                 protected override void OnKeyDown (KeyEventArgs e)
453                 {
454                         if (e.KeyCode == Keys.Tab && (e.KeyData & Keys.Control) != 0) {
455                                 if ((e.KeyData & Keys.Shift) == 0)
456                                         SelectedIndex = (SelectedIndex + 1) % (TabCount);
457                                 else
458                                         SelectedIndex = (SelectedIndex - 1) % (TabCount);
459                                 e.Handled = true;
460                         } else if (e.KeyCode == Keys.Home) {
461                                 SelectedIndex = 0;
462                                 e.Handled = true;
463                         } else if (e.KeyCode == Keys.End) {
464                                 SelectedIndex = TabCount - 1;
465                                 e.Handled = true;
466                         } else if (e.KeyCode == Keys.Left && SelectedIndex > 0) {
467                                 SelectedIndex--;
468                                 e.Handled = true;
469                         } else if (e.KeyCode == Keys.Right && SelectedIndex < TabCount - 1) {
470                                 SelectedIndex++;
471                                 e.Handled = true;
472                         }
473
474                         base.OnKeyDown (e);
475                 }
476
477                 protected override bool IsInputKey (Keys key)
478                 {
479                         switch (key & Keys.KeyCode) {
480                         case Keys.Prior:
481                         case Keys.Home:
482                                 return true;
483                         }
484                         return base.IsInputKey (key);
485                 }
486
487                 protected override void Dispose (bool disposing)
488                 {
489                         base.Dispose (disposing);
490                 }
491
492                 protected void RemoveAll ()
493                 {
494                         Controls.Clear ();
495                 }
496
497                 protected virtual object [] GetItems ()
498                 {
499                         TabPage [] pages = new TabPage [Controls.Count];
500                         Controls.CopyTo (pages, 0);
501                         return pages;
502                 }
503
504                 protected virtual object [] GetItems (Type type)
505                 {
506                         object [] pages = (object []) Array.CreateInstance (type, Controls.Count);
507                         Controls.CopyTo (pages, 0);
508                         return pages;
509                 }
510
511                 protected string GetToolTipText (object item)
512                 {
513                         TabPage page = (TabPage) item;
514                         return page.ToolTipText;
515                 }
516
517                 protected override void WndProc (ref Message m)
518                 {
519                         switch ((Msg) m.Msg) {
520                         case Msg.WM_PAINT:
521                                 PaintEventArgs  paint_event;
522                                 paint_event = XplatUI.PaintEventStart (Handle, true);
523                                 PaintInternal (paint_event);
524                                 XplatUI.PaintEventEnd (Handle, true);
525                                 break;
526                         default:
527                                 base.WndProc (ref m);
528                                 break;
529                         }
530                 }
531
532                 protected virtual void OnSelectedIndexChanged (EventArgs e)
533                 {
534                         if (SelectedIndexChanged != null)
535                                 SelectedIndexChanged (this, e);
536                 }
537
538                 #endregion      // Protected Instance Methods
539
540                 #region Internal & Private Methods
541                 private bool CanScrollRight {
542                         get {
543                                 return (slider_pos < TabCount - 1);
544                         }
545                 }
546
547                 private bool CanScrollLeft {
548                         get { return slider_pos > 0; }
549                 }
550
551                 private void MouseDownHandler (object sender, MouseEventArgs e)
552                 {
553                         if (ShowSlider) {
554                                 Rectangle right = ThemeEngine.Current.GetTabControlRightScrollRect (this);
555                                 Rectangle left = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
556                                 if (right.Contains (e.X, e.Y)) {
557                                         right_slider_state = ButtonState.Pushed;
558                                         if (CanScrollRight) {
559                                                 slider_pos++;
560                                                 SizeTabs ();
561
562                                                 Invalidate (new Rectangle (0, 0, Width, DisplayRectangle.Top));
563                                         } else {
564                                                 Invalidate (right);
565                                         }
566                                         return;
567                                 } else if (left.Contains (e.X, e.Y)) {
568                                         left_slider_state = ButtonState.Pushed;
569                                         if (CanScrollLeft) {
570                                                 slider_pos--;
571                                                 SizeTabs ();
572
573                                                 Invalidate (new Rectangle (0, 0, Width, DisplayRectangle.Top));
574                                         } else {
575                                                 Invalidate (left);
576                                         }
577                                         return;
578                                 }
579                         }
580
581                         int count = Controls.Count;
582                         for (int i = SliderPos; i < count; i++) {
583                                 if (!GetTabRect (i).Contains (e.X, e.Y))
584                                         continue;
585                                 SelectedIndex = i;
586                                 break;
587                         }
588                 }
589
590                 private void MouseUpHandler (object sender, MouseEventArgs e)
591                 {
592                         if (ShowSlider && (left_slider_state == ButtonState.Pushed || right_slider_state == ButtonState.Pushed)) {
593                                 Rectangle invalid;
594                                 if (left_slider_state == ButtonState.Pushed)
595                                         invalid = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
596                                 else
597                                         invalid = ThemeEngine.Current.GetTabControlRightScrollRect (this);
598                                 left_slider_state = ButtonState.Normal;
599                                 right_slider_state = ButtonState.Normal;
600
601                                 Invalidate (invalid);
602                         }
603                 }
604
605                 private void SizeChangedHandler (object sender, EventArgs e)
606                 {
607                         ResizeTabPages ();
608                 }
609
610                 internal void UpdateTabpage (TabPage page)
611                 {
612
613                 }
614
615                 internal int IndexForTabPage (TabPage page)
616                 {
617                         for (int i = 0; i < tab_pages.Count; i++) {
618                                 if (page == tab_pages [i])
619                                         return i;
620                         }
621                         return -1;
622                 }
623
624                 private void ResizeTabPages ()
625                 {
626                         CalcTabRows ();
627                         SizeTabs ();
628                         Rectangle r = DisplayRectangle;
629                         foreach (TabPage page in Controls) {
630                                 page.Bounds = r;
631                         }
632                 }
633
634                 private int MinimumTabWidth {
635                         get {
636                                 return ThemeEngine.Current.TabControlMinimumTabWidth;
637                         }
638                 }
639
640                 private Size TabSpacing {
641                         get {
642                                 return ThemeEngine.Current.TabControlGetSpacing (this);
643                         }
644                 }
645
646                 private void CalcTabRows ()
647                 {
648                         switch (Alignment) {
649                         case TabAlignment.Right:
650                         case TabAlignment.Left:
651                                 CalcTabRows (Height);
652                                 break;
653                         default:
654                                 CalcTabRows (Width);
655                                 break;
656                         }
657                 }
658
659                 private void CalcTabRows (int row_width)
660                 {
661                         int xpos = 4;
662                         Size spacing = TabSpacing;
663
664                         row_count = 1;
665                         show_slider = false;
666                         
667                         for (int i = 0; i < TabPages.Count; i++) {
668                                 TabPage page = TabPages [i];
669                                 int width;
670
671                                 page.Row = 1;
672
673                                 if (SizeMode == TabSizeMode.Fixed) {
674                                         width = item_size.Width;
675                                 } else {
676                                         width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
677                                 }
678
679                                 if (i == SelectedIndex)
680                                         width += 8;
681                                 if (width < MinimumTabWidth)
682                                         width = MinimumTabWidth;
683
684                                 if (xpos + width > row_width && multiline) {
685                                         xpos = 4;
686                                         for (int j = 0; j < i; j++) {
687                                                 TabPages [j].Row++;
688                                         }
689                                         row_count++;
690                                 } else if (xpos + width > row_width) {
691                                         show_slider = true;
692                                 }
693
694                                 xpos += width + 1 + spacing.Width;
695                         }
696
697                         if (SelectedIndex != -1 && TabPages [SelectedIndex].Row != BottomRow)
698                                 DropRow (TabPages [SelectedIndex].Row);
699                 }
700
701                 private int BottomRow {
702                         get {
703                                 switch (Alignment) {
704                                 case TabAlignment.Right:
705                                 case TabAlignment.Bottom:
706                                         return row_count;
707                                 default:
708                                         return 1;
709                                 }
710                         }
711                 }
712
713                 private int Direction
714                 {
715                         get {
716                                 switch (Alignment) {
717                                 case TabAlignment.Right:
718                                 case TabAlignment.Bottom:
719                                         return -1;
720                                 default:
721                                         return 1;
722                                 }
723                         }
724                 }
725
726                 private void DropRow (int row)
727                 {
728                         int bottom = BottomRow;
729                         int direction = Direction;
730
731                         foreach (TabPage page in TabPages) {
732                                 if (page.Row == row) {
733                                         page.Row = bottom;
734                                 } else if (direction == 1 && page.Row < row) {
735                                         page.Row += direction;
736                                 } else if (direction == -1 && page.Row > row) {
737                                         page.Row += direction;
738                                 }
739                         }
740                 }
741
742                 private int CalcYPos ()
743                 {
744                         if (Alignment == TabAlignment.Bottom) {
745                                 Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
746                                 return r.Bottom + 3;
747                         }
748                         return 1;
749                 }
750
751                 private int CalcXPos ()
752                 {
753                         if (Alignment == TabAlignment.Right) {
754                                 Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
755                                 return r.Right + 4;
756                         }
757                         return 4;
758
759                 }
760
761                 private void SizeTabs ()
762                 {
763                         switch (Alignment) {
764                         case TabAlignment.Right:
765                         case TabAlignment.Left:
766                                 SizeTabsV (Height);
767                                 break;
768                         default:
769                                 SizeTabs (Width);
770                                 break;
771                         }
772                 }
773
774                 private void SizeTabsV (int row_width)
775                 {
776                         int ypos = 1;
777                         int prev_row = 1;
778                         Size spacing = TabSpacing;
779                         int xpos = CalcXPos ();
780                         int begin_prev = 0;
781                         
782                         if (TabPages.Count == 0)
783                                 return;
784
785                         prev_row = TabPages [0].Row;
786
787                         for (int i = 0; i < TabPages.Count; i++) {
788                                 TabPage page = TabPages [i];
789                                 int width;
790
791                                 if (SizeMode == TabSizeMode.Fixed) {
792                                         width = item_size.Width;
793                                 } else {
794                                         width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
795                                 }
796
797                                 if (width < MinimumTabWidth)
798                                         width = MinimumTabWidth;
799                                 if (page.Row != prev_row)
800                                         ypos = 1;
801
802                                 page.TabBounds = new Rectangle (xpos + (row_count - page.Row) * ((item_size.Height - 2) + spacing.Width),
803                                                 ypos, item_size.Height - 2, width);
804
805                                 if (page.Row != prev_row) {
806                                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
807                                                 FillRowV (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Bottom) / (i - begin_prev)), spacing);
808                                         }
809                                         begin_prev = i;
810                                 }
811
812                                 ypos += width + spacing.Width;
813                                 prev_row = page.Row;
814                         }
815
816                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
817                                 FillRowV (begin_prev, TabPages.Count - 1,
818                                                 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Bottom) / (TabPages.Count - begin_prev)), spacing);
819                         }
820
821                         if (SelectedIndex != -1) {
822                                 ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
823                         }
824                 }
825
826                 private void SizeTabs (int row_width)
827                 {
828                         int ypos = CalcYPos ();
829                         int prev_row = 1;
830                         Size spacing = TabSpacing;
831                         int xpos = 4;
832                         int begin_prev = 0;
833
834                         if (TabPages.Count == 0)
835                                 return;
836
837                         prev_row = TabPages [0].Row;
838
839                         // Reset the slider position if the slider isn't needed
840                         // anymore (ie window size was increased so all tabs are visible)
841                         if (!show_slider)
842                                 slider_pos = 0;
843
844                         for (int i = slider_pos; i < TabPages.Count; i++) {
845                                 TabPage page = TabPages [i];
846                                 int width;
847  
848                                 if (SizeMode == TabSizeMode.Fixed) {
849                                         width = item_size.Width;
850                                 } else {                                        
851                                         width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
852                                 }
853
854                                 if (width < MinimumTabWidth)
855                                         width = MinimumTabWidth;
856                                 if (page.Row != prev_row)
857                                         xpos = 4;
858
859                                 page.TabBounds = new Rectangle (xpos,
860                                                 ypos + (row_count - page.Row) * (item_size.Height + spacing.Height),
861                                                 width, item_size.Height);
862                                 
863                                 if (page.Row != prev_row) {
864                                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
865                                                 FillRow (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Right) / (i - begin_prev)), spacing);
866                                         }
867                                         begin_prev = i;
868                                 }
869
870                                 xpos += width + 1 + spacing.Width;
871                                 prev_row = page.Row;                                
872                         }
873
874                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
875                                 FillRow (begin_prev, TabPages.Count - 1,
876                                                 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Right) / (TabPages.Count - begin_prev)), spacing);
877                         }
878
879                         if (SelectedIndex != -1) {
880                                 ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
881                         }
882                 }
883                 
884                 private void FillRow (int start, int end, int amount, Size spacing)
885                 {
886                         int xpos = TabPages [start].TabBounds.Left;
887                         for (int i = start; i <= end; i++) {
888                                 TabPage page = TabPages [i];
889                                 int left = xpos;
890                                 int width = (i == end ? Width - left - 3 : page.TabBounds.Width + amount);
891
892                                 page.TabBounds = new Rectangle (left, page.TabBounds.Top,
893                                                 width, page.TabBounds.Height);
894                                 xpos = page.TabBounds.Right + 1 + spacing.Width;
895                         }
896                 }
897
898                 private void FillRowV (int start, int end, int amount, Size spacing)
899                 {
900                         int ypos = TabPages [start].TabBounds.Top;
901                         for (int i = start; i <= end; i++) {
902                                 TabPage page = TabPages [i];
903                                 int top = ypos;
904                                 int height = (i == end ? Height - top - 5 : page.TabBounds.Height + amount);
905
906                                 page.TabBounds = new Rectangle (page.TabBounds.Left, top,
907                                                 page.TabBounds.Width, height);
908                                 ypos = page.TabBounds.Bottom + 1;
909                         }
910                 }
911
912                 private void ExpandSelected (TabPage page, int left_edge, int right_edge)
913                 {
914                         if (Appearance != TabAppearance.Normal)
915                                 return;
916
917                         if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom) {
918                                 int l = page.TabBounds.Left - 4;
919                                 int r = page.TabBounds.Right + 4;
920                                 int y = page.TabBounds.Y;
921                                 int h = page.TabBounds.Height + 3;
922
923                                 if (l < left_edge)
924                                         l = left_edge;
925                                 if (r > right_edge && SizeMode != TabSizeMode.Normal)
926                                         r = right_edge;
927                                 if (Alignment == TabAlignment.Top)
928                                         y -= 1;
929                                 if (Alignment == TabAlignment.Bottom)
930                                         y -= 2;
931
932                                 page.TabBounds = new Rectangle (l, y, r - l, h);
933                         } else {
934                                 int l = page.TabBounds.Left - 3;
935                                 int r = page.TabBounds.Right + 3;
936                                 int t = page.TabBounds.Top - 3;
937                                 int b = page.TabBounds.Bottom + 3;
938
939                                 if (t < left_edge)
940                                         t = left_edge;
941                                 if (b > right_edge)
942                                         b = right_edge;
943
944                                 page.TabBounds = new Rectangle (l, t, r - l, b - t);
945                         }
946                 }
947
948                 private void PaintInternal (PaintEventArgs pe)
949                 {
950                         Draw (pe.Graphics, pe.ClipRectangle);
951                         // On MS the Paint event never seems to be raised
952                 }
953
954                 private void Draw (Graphics dc, Rectangle clip)
955                 {
956                         ThemeEngine.Current.DrawTabControl (dc, clip, this);
957                 }
958
959                 private TabPage GetTab (int index)
960                 {
961                         return Controls [index] as TabPage;
962                 }
963
964                 private void SetTab (int index, TabPage value)
965                 {
966                         ((IList) Controls).Insert (index, value);
967                         Redraw ();
968                 }
969
970                 internal void Redraw ()
971                 {
972                         ResizeTabPages ();
973                         Refresh ();
974                 }
975
976                 #endregion      // Internal & Private Methods
977
978                 #region Events
979                 [Browsable(false)]
980                 [EditorBrowsable(EditorBrowsableState.Never)]
981                 public new event EventHandler BackColorChanged {
982                         add { base.BackColorChanged += value; }
983                         remove { base.BackColorChanged -= value; }
984                 }
985
986                 [Browsable(false)]
987                 [EditorBrowsable(EditorBrowsableState.Never)]
988                 public new event EventHandler BackgroundImageChanged {
989                         add { base.BackgroundImageChanged += value; }
990                         remove { base.BackgroundImageChanged -= value; }
991                 }
992
993                 [Browsable(false)]
994                 [EditorBrowsable(EditorBrowsableState.Never)]
995                 public new event EventHandler ForeColorChanged {
996                         add { base.ForeColorChanged += value; }
997                         remove { base.ForeColorChanged -= value; }
998                 }
999
1000                 [Browsable(false)]
1001                 [EditorBrowsable(EditorBrowsableState.Never)]
1002                 public new event PaintEventHandler Paint {
1003                         add { base.Paint += value; }
1004                         remove { base.Paint -= value; }
1005                 }
1006
1007                 [Browsable(false)]
1008                 [EditorBrowsable(EditorBrowsableState.Never)]
1009                 public new event EventHandler TextChanged {
1010                         add { base.TextChanged += value; }
1011                         remove { base.TextChanged -= value; }
1012                 }
1013
1014                 public event DrawItemEventHandler DrawItem;
1015                 public event EventHandler SelectedIndexChanged;
1016                 #endregion      // Events
1017
1018
1019                 #region Class TabPage.ControlCollection
1020                 public class ControlCollection : System.Windows.Forms.Control.ControlCollection {
1021
1022                         private TabControl owner;
1023                         private ArrayList list = new ArrayList ();
1024
1025                         public ControlCollection (TabControl owner) : base (owner)
1026                         {
1027                                 this.owner = owner;
1028                         }
1029
1030                         public override void Add (Control value)
1031                         {
1032                                 if (!(value is TabPage))
1033                                         throw new ArgumentException ("Cannot add " +
1034                                                 value.GetType ().Name + " to TabControl. " +
1035                                                 "Only TabPages can be directly added to TabControls.");
1036
1037                                 value.Visible = false;
1038                                 base.Add (value);
1039                                 if (Count == 1) {
1040                                         owner.SelectedIndex = 0;
1041                                 } else {
1042                                         // Setting the selected index will calc the tab rows so
1043                                         // we don't need to do it again
1044                                         owner.ResizeTabPages ();
1045                                 }
1046                         }
1047                 }
1048                 #endregion      // Class TabPage.ControlCollection
1049
1050                 #region Class TabPage.TabPageCollection
1051                 public class TabPageCollection  : IList, ICollection, IEnumerable {
1052
1053                         private TabControl owner;
1054                         private IList controls;
1055
1056                         public TabPageCollection (TabControl owner)
1057                         {
1058                                 if (owner == null)
1059                                         throw new ArgumentNullException ("Value cannot be null.");
1060                                 this.owner = owner;
1061                                 controls = owner.Controls;
1062                         }
1063
1064                         [Browsable(false)]
1065                         public virtual int Count {
1066                                 get { return owner.Controls.Count; }
1067                         }
1068
1069                         public virtual bool IsReadOnly {
1070                                 get { return false; }
1071                         }
1072
1073                         public virtual TabPage this [int index] {
1074                                 get {
1075                                         return owner.GetTab (index);
1076                                 }
1077                                 set {
1078                                         owner.SetTab (index, value);
1079                                 }    
1080                         }
1081
1082                         bool ICollection.IsSynchronized {
1083                                 get { return false; }
1084                         }
1085
1086                         object ICollection.SyncRoot {
1087                                 get { return this; }
1088                         }
1089
1090                         bool IList.IsFixedSize {
1091                                 get { return false; }
1092                         }
1093
1094                         object IList.this [int index] {
1095                                 get {
1096                                         return owner.GetTab (index);
1097                                 }
1098                                 set {
1099                                         owner.SetTab (index, (TabPage) value);
1100                                 }
1101                         }
1102
1103                         public void Add (TabPage page)
1104                         {
1105                                 if (page == null)
1106                                         throw new ArgumentNullException ("Value cannot be null.");
1107                                 owner.Controls.Add (page);
1108                         }
1109
1110                         public void AddRange (TabPage [] pages)
1111                         {
1112                                 if (pages == null)
1113                                         throw new ArgumentNullException ("Value cannot be null.");
1114                                 owner.Controls.AddRange (pages);
1115                         }
1116
1117                         public virtual void Clear ()
1118                         {
1119                                 owner.Controls.Clear ();
1120                         }
1121
1122                         public bool Contains (TabPage page)
1123                         {
1124                                 if (page == null)
1125                                         throw new ArgumentNullException ("Value cannot be null.");
1126                                 return owner.Controls.Contains (page);
1127                         }
1128
1129                         public virtual IEnumerator GetEnumerator ()
1130                         {
1131                                 return owner.Controls.GetEnumerator ();
1132                         }
1133
1134                         public int IndexOf (TabPage page)
1135                         {
1136                                 return owner.Controls.IndexOf (page);
1137                         }
1138
1139                         public void Remove (TabPage page)
1140                         {
1141                                 owner.Controls.Remove (page);
1142                         }
1143
1144                         public virtual void RemoveAt (int index)
1145                         {
1146                                 owner.Controls.RemoveAt (index);
1147                         }
1148
1149                         void ICollection.CopyTo (Array dest, int index)
1150                         {
1151                                 owner.Controls.CopyTo (dest, index);
1152                         }
1153
1154                         int IList.Add (object value)
1155                         {
1156                                 TabPage page = value as TabPage;
1157                                 if (value == null)
1158                                         throw new ArgumentException ("value");
1159                                 owner.Controls.Add (page);
1160                                 return owner.Controls.IndexOf (page);
1161                         }
1162
1163                         bool IList.Contains (object value)
1164                         {
1165                                 TabPage page = value as TabPage;
1166                                 if (page == null)
1167                                         return false;
1168                                 return Contains (page);
1169                         }
1170
1171                         int IList.IndexOf (object value)
1172                         {
1173                                 TabPage page = value as TabPage;
1174                                 if (page == null)
1175                                         return -1;
1176                                 return IndexOf ((TabPage) page);
1177                         }
1178
1179                         void IList.Insert (int index, object value)
1180                         {
1181                                 throw new NotSupportedException ();
1182                         }
1183
1184                         void IList.Remove (object value)
1185                         {
1186                                 TabPage page = value as TabPage;
1187                                 if (page == null)
1188                                         return;
1189                                 Remove ((TabPage) value);
1190                         }
1191                 }
1192                 #endregion      // Class TabPage.TabPageCollection
1193         }
1194 }
1195
1196