* ImageList.cs: When the image stream is set pull all the images
[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 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)]
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 bool redraw;
53                 private Rectangle display_rect;
54                 private bool show_slider = false;
55                 private ButtonState right_slider_state;
56                 private ButtonState left_slider_state;
57                 private int slider_pos = 0;
58                 #endregion      // Fields
59
60                 #region Public Constructors
61                 public TabControl ()
62                 {
63                         tab_pages = new TabPageCollection (this);
64                         SetStyle (ControlStyles.UserPaint, true);
65                         padding = ThemeEngine.Current.TabControlDefaultPadding;
66                         item_size = ThemeEngine.Current.TabControlDefaultItemSize;
67
68                         MouseDown += new MouseEventHandler (MouseDownHandler);
69                         MouseUp += new MouseEventHandler (MouseUpHandler);
70                         SizeChanged += new EventHandler (SizeChangedHandler);
71                 }
72
73                 #endregion      // Public Constructors
74
75                 #region Public Instance Properties
76                 [DefaultValue(TabAlignment.Top)]
77                 [Localizable(true)]
78                 [RefreshProperties(RefreshProperties.All)]
79                 public TabAlignment Alignment {
80                         get { return alignment; }
81                         set {
82                                 if (alignment == value)
83                                         return;
84                                 alignment = value;
85                                 if (alignment == TabAlignment.Left || alignment == TabAlignment.Right)
86                                         multiline = true;
87                                 Refresh ();
88                         }
89                 }
90
91                 [DefaultValue(TabAppearance.Normal)]
92                 [Localizable(true)]
93                 public TabAppearance Appearance {
94                         get { return appearance; }
95                         set {
96                                 if (appearance == value)
97                                         return;
98                                 appearance = value;
99                                 Refresh ();
100                         }
101                 }
102
103                 [Browsable(false)]
104                 [EditorBrowsable(EditorBrowsableState.Never)]
105                 public override Color BackColor {
106                         get { return base.BackColor; }
107                         set { /* nothing happens on set on MS */ }
108                 }
109
110                 [Browsable(false)]
111                 [EditorBrowsable(EditorBrowsableState.Never)]
112                 public override Image BackgroundImage {
113                         get { return base.BackgroundImage; }
114                         set { base.BackgroundImage = value; }
115                 }
116
117                 public override Rectangle DisplayRectangle {
118                         get {
119                                 return ThemeEngine.Current.GetTabControlDisplayRectangle (this);
120                         }
121                 }
122
123                 [DefaultValue(TabDrawMode.Normal)]
124                 public TabDrawMode DrawMode {
125                         get { return draw_mode; }
126                         set {
127                                 if (draw_mode == value)
128                                         return;
129                                 draw_mode = value;
130                                 Refresh ();
131                         }
132                 }
133
134                 [Browsable(false)]
135                 [EditorBrowsable(EditorBrowsableState.Never)]
136                 public override Color ForeColor {
137                         get { return base.ForeColor; }
138                         set { base.ForeColor = value; }
139                 }
140
141                 [DefaultValue(false)]
142                 public bool HotTrack {
143                         get { return hottrack; }
144                         set {
145                                 if (hottrack == value)
146                                         return;
147                                 hottrack = value;
148                                 Refresh ();
149                         }
150                 }
151
152                 [DefaultValue(null)]
153                 public ImageList ImageList {
154                         get { return image_list; }
155                         set { image_list = value; }
156                 }
157
158                 [Localizable(true)]
159                 public Size ItemSize {
160                         get {
161                                 return item_size;
162                         }
163                         set {
164                                 if (value.Height < 0 || value.Width < 0)
165                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
166                                 item_size = value;
167                                 Refresh ();
168                         }
169                 }
170
171                 [DefaultValue(false)]
172                 public bool Multiline {
173                         get { return multiline; }
174                         set {
175                                 if (multiline == value)
176                                         return;
177                                 multiline = value;
178                                 if (!multiline && alignment == TabAlignment.Left || alignment == TabAlignment.Right)
179                                         alignment = TabAlignment.Top;
180                                 Refresh ();
181                         }
182                 }
183
184                 [Localizable(true)]
185                 public Point Padding {
186                         get { return padding; }
187                         set {
188                                 if (value.X < 0 || value.Y < 0)
189                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'Padding'.");
190                                 if (padding == value)
191                                         return;
192                                 padding = value;
193                                 Refresh ();
194                         }
195
196                 }
197
198                 [Browsable(false)]
199                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
200                 public int RowCount {
201                         get { return row_count; }
202                 }
203
204                 [DefaultValue(-1)]
205                 [Browsable(false)]
206                 public int SelectedIndex {
207                         get { return selected_index; }
208                         set {
209                                 if (selected_index == value)
210                                         return;
211                                 if (selected_index < -1) {
212                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
213                                                         "'value' must be greater than or equal to -1.");
214                                 }
215
216                                 SuspendLayout ();
217
218                                 Rectangle invalid = Rectangle.Empty;
219
220                                 if (selected_index != -1) {
221                                         invalid = GetTabRect (selected_index);
222                                         Controls [selected_index].Visible = false;
223                                 }
224                                 selected_index = value;
225
226                                 OnSelectedIndexChanged (EventArgs.Empty);
227
228                                 if (selected_index != -1) {
229                                         invalid = Rectangle.Union (invalid, GetTabRect (selected_index));
230                                         Controls [selected_index].Visible = true;
231                                 }
232
233                                 ResumeLayout ();
234
235                                 if (SelectedIndex != -1 && TabPages [SelectedIndex].Row != BottomRow) {
236                                         DropRow (TabPages [selected_index].Row);
237                                         // calculating what to invalidate here seems to be slower then just
238                                         // refreshing the whole thing
239                                         SizeTabs ();
240                                         Refresh ();
241                                 } else {
242                                         SizeTabs ();
243                                         Invalidate (invalid);
244                                 }
245                         }
246                 }
247
248                 [Browsable(false)]
249                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
250                 public TabPage SelectedTab {
251                         get {
252                                 if (selected_index == -1)
253                                         return null;
254                                 return tab_pages [selected_index];
255                         }
256                         set {
257                                 int index = IndexForTabPage (value);
258                                 if (index == selected_index)
259                                         return;
260                                 SelectedIndex = index;
261                         }
262                 }
263
264                 [DefaultValue(false)]
265                 [Localizable(true)]
266                 public bool ShowToolTips {
267                         get { return show_tool_tips; }
268                         set {
269                                 if (show_tool_tips == value)
270                                         return;
271                                 show_tool_tips = value;
272                                 Refresh ();
273                         }
274                 }
275
276                 [DefaultValue(TabSizeMode.Normal)]
277                 [RefreshProperties(RefreshProperties.Repaint)]
278                 public TabSizeMode SizeMode {
279                         get { return size_mode; }
280                         set {
281                                 if (size_mode == value)
282                                         return;
283                                 size_mode = value;
284                                 Refresh ();
285                         }
286                 }
287
288                 [Browsable(false)]
289                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
290                 public int TabCount {
291                         get {
292                                 return tab_pages.Count;
293                         }
294                 }
295
296                 [DefaultValue(null)]
297                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
298                 [MergableProperty(false)]
299                 public TabPageCollection TabPages {
300                         get { return tab_pages; }
301                 }
302
303                 [Browsable(false)]
304                 [Bindable(false)]
305                 [EditorBrowsable(EditorBrowsableState.Never)]
306                 public override string Text {
307                         get { return base.Text; }
308                         set { base.Text = value; }
309                 }
310
311                 #endregion      // Public Instance Properties
312
313                 #region Internal Properties
314                 internal bool ShowSlider {
315                         get { return show_slider; }
316                         set { show_slider = value; }
317                 }
318
319                 internal int SliderPos {
320                         get { return slider_pos; }
321                 }
322
323                 internal ButtonState RightSliderState {
324                         get { return right_slider_state; }
325                 }
326
327                 internal ButtonState LeftSliderState {
328                         get { return left_slider_state; }
329                 }
330
331                 private Size DefaultItemSize {
332                         get {
333                                 return ThemeEngine.Current.TabControlDefaultItemSize;
334                         }
335                 }
336
337                 #endregion      // Internal Properties
338
339                 #region Protected Instance Properties
340                 [MonoTODO ("Anything special need to be done?")]
341                 protected override CreateParams CreateParams {
342                         get {
343                                 CreateParams c = base.CreateParams;
344                                 // Do we need to do anything here?
345                                 return c;
346                         }
347                 }
348
349                 protected override Size DefaultSize {
350                         get { return new Size (200, 100); }  
351                 }
352
353                 #endregion      // Protected Instance Properties
354
355                 #region Public Instance Methods
356                 public Rectangle GetTabRect (int index)
357                 {
358                         TabPage page = GetTab (index);
359                         return page.TabBounds;
360                 }
361
362                 public Control GetControl (int index)
363                 {
364                         return GetTab (index);
365                 }
366
367                 #endregion      // Public Instance Methods
368
369                 #region Protected Instance Methods
370                 protected override Control.ControlCollection CreateControlsInstance ()
371                 {
372                         return new TabControl.ControlCollection (this);
373                 }
374
375                 protected override void CreateHandle ()
376                 {
377                         ResizeTabPages ();
378                         base.CreateHandle ();
379                 }
380
381                 protected override void Dispose (bool disposing)
382                 {
383                         base.Dispose (disposing);
384                 }
385
386                 protected virtual object [] GetItems ()
387                 {
388                         TabPage [] pages = new TabPage [Controls.Count];
389                         Controls.CopyTo (pages, 0);
390                         return pages;
391                 }
392
393                 protected virtual object [] GetItems (Type type)
394                 {
395                         object [] pages = (object []) Array.CreateInstance (type, Controls.Count);
396                         Controls.CopyTo (pages, 0);
397                         return pages;
398                 }
399
400                 protected string GetToolTipText (object item)
401                 {
402                         TabPage page = (TabPage) item;
403                         return page.ToolTipText;
404                 }
405
406                 protected override void WndProc (ref Message m)
407                 {
408                         switch ((Msg) m.Msg) {
409                         case Msg.WM_PAINT:
410                                 PaintEventArgs  paint_event;
411                                 paint_event = XplatUI.PaintEventStart (Handle);
412                                 PaintInternal (paint_event);
413                                 XplatUI.PaintEventEnd (Handle);
414                                 break;
415                         default:
416                                 base.WndProc (ref m);
417                                 break;
418                         }
419                 }
420
421                 protected virtual void OnSelectedIndexChanged (EventArgs e)
422                 {
423                         if (SelectedIndexChanged != null)
424                                 SelectedIndexChanged (this, e);
425                 }
426
427                 #endregion      // Protected Instance Methods
428
429                 #region Internal & Private Methods
430                 private bool CanScrollRight {
431                         get {
432                                 if (TabPages [TabCount - 1].TabBounds.Right > ClientRectangle.Right - 40)
433                                         return true;
434                                 return false;
435                         }
436                 }
437
438                 private bool CanScrollLeft {
439                         get { return slider_pos > 0; }
440                 }
441
442                 private void MouseDownHandler (object sender, MouseEventArgs e)
443                 {
444                         if (ShowSlider) {
445                                 Rectangle right = ThemeEngine.Current.GetTabControlRightScrollRect (this);
446                                 Rectangle left = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
447                                 if (right.Contains (e.X, e.Y)) {
448                                         right_slider_state = ButtonState.Pushed;
449                                         if (CanScrollRight) {
450                                                 slider_pos++;
451                                                 SizeTabs ();
452                                                 Refresh ();
453                                         } else {
454                                                 Invalidate (right);
455                                         }
456                                 } else if (left.Contains (e.X, e.Y)) {
457                                         left_slider_state = ButtonState.Pushed;
458                                         if (CanScrollLeft) {
459                                                 slider_pos--;
460                                                 SizeTabs ();
461                                                 Refresh ();
462                                         } else {
463                                                 Invalidate (left);
464                                         }
465                                 }
466                         }
467
468                         int count = Controls.Count;
469                         for (int i = SliderPos; i < count; i++) {
470                                 if (!GetTabRect (i).Contains (e.X, e.Y))
471                                         continue;
472                                 SelectedIndex = i;
473                                 break;
474                         }
475                 }
476
477                 private void MouseUpHandler (object sender, MouseEventArgs e)
478                 {
479                         if (ShowSlider && (left_slider_state != ButtonState.Pushed || right_slider_state != ButtonState.Pushed)) {
480                                 Rectangle invalid;
481                                 if (left_slider_state == ButtonState.Pushed)
482                                         invalid = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
483                                 else
484                                         invalid = ThemeEngine.Current.GetTabControlRightScrollRect (this);
485                                 left_slider_state = ButtonState.Normal;
486                                 right_slider_state = ButtonState.Normal;
487
488                                 Invalidate (invalid);
489                         }
490                 }
491
492                 private void SizeChangedHandler (object sender, EventArgs e)
493                 {
494                         ResizeTabPages ();
495                 }
496
497                 internal void UpdateTabpage (TabPage page)
498                 {
499
500                 }
501
502                 internal int IndexForTabPage (TabPage page)
503                 {
504                         for (int i = 0; i < tab_pages.Count; i++) {
505                                 if (page == tab_pages [i])
506                                         return i;
507                         }
508                         return -1;
509                 }
510
511                 private void ResizeTabPages ()
512                 {
513                         CalcTabRows ();
514                         SizeTabs ();
515                         Rectangle r = DisplayRectangle;
516                         foreach (TabPage page in Controls) {
517                                 page.Bounds = r;
518                         }
519                 }
520
521                 private int MinimumTabWidth {
522                         get {
523                                 return ThemeEngine.Current.TabControlMinimumTabWidth;
524                         }
525                 }
526
527                 private Size TabSpacing {
528                         get {
529                                 return ThemeEngine.Current.TabControlGetSpacing (this);
530                         }
531                 }
532
533                 private void CalcTabRows ()
534                 {
535                         switch (Alignment) {
536                         case TabAlignment.Right:
537                         case TabAlignment.Left:
538                                 CalcTabRows (Height);
539                                 break;
540                         default:
541                                 CalcTabRows (Width);
542                                 break;
543                         }
544                 }
545
546                 private void CalcTabRows (int row_width)
547                 {
548                         int xpos = 4;
549                         Size spacing = TabSpacing;
550
551                         row_count = 1;
552                         show_slider = false;
553                         
554                         for (int i = 0; i < TabPages.Count; i++) {
555                                 TabPage page = TabPages [i];
556                                 int width;
557
558                                 page.Row = 1;
559
560                                 if (SizeMode == TabSizeMode.Fixed) {
561                                         width = item_size.Width;
562                                 } else {
563                                         width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
564                                 }
565
566                                 if (i == SelectedIndex)
567                                         width += 8;
568                                 if (width < MinimumTabWidth)
569                                         width = MinimumTabWidth;
570
571                                 if (xpos + width > row_width && multiline) {
572                                         xpos = 4;
573                                         for (int j = 0; j < i; j++) {
574                                                 TabPages [j].Row++;
575                                         }
576                                         row_count++;
577                                 } else if (xpos + width > row_width) {
578                                         show_slider = true;
579                                 }
580
581                                 xpos += width + 1 + spacing.Width;
582                         }
583
584                         if (SelectedIndex != -1 && TabPages [SelectedIndex].Row != BottomRow)
585                                 DropRow (TabPages [SelectedIndex].Row);
586                 }
587
588                 private int BottomRow {
589                         get {
590                                 switch (Alignment) {
591                                 case TabAlignment.Right:
592                                 case TabAlignment.Bottom:
593                                         return row_count;
594                                 default:
595                                         return 1;
596                                 }
597                         }
598                 }
599
600                 private int Direction
601                 {
602                         get {
603                                 switch (Alignment) {
604                                 case TabAlignment.Right:
605                                 case TabAlignment.Bottom:
606                                         return -1;
607                                 default:
608                                         return 1;
609                                 }
610                         }
611                 }
612
613                 private void DropRow (int row)
614                 {
615                         int bottom = BottomRow;
616                         int direction = Direction;
617
618                         foreach (TabPage page in TabPages) {
619                                 if (page.Row == row) {
620                                         page.Row = bottom;
621                                 } else if (direction == 1 && page.Row < row) {
622                                         page.Row += direction;
623                                 } else if (direction == -1 && page.Row > row) {
624                                         page.Row += direction;
625                                 }
626                         }
627                 }
628
629                 private int CalcYPos ()
630                 {
631                         if (Alignment == TabAlignment.Bottom) {
632                                 Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
633                                 return r.Bottom + 3;
634                         }
635                         return 1;
636                 }
637
638                 private int CalcXPos ()
639                 {
640                         if (Alignment == TabAlignment.Right) {
641                                 Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
642                                 return r.Right + 4;
643                         }
644                         return 4;
645
646                 }
647
648                 private void SizeTabs ()
649                 {
650                         switch (Alignment) {
651                         case TabAlignment.Right:
652                         case TabAlignment.Left:
653                                 SizeTabsV (Height);
654                                 break;
655                         default:
656                                 SizeTabs (Width);
657                                 break;
658                         }
659                 }
660
661                 private void SizeTabsV (int row_width)
662                 {
663                         int ypos = 1;
664                         int prev_row = 1;
665                         Size spacing = TabSpacing;
666                         int xpos = CalcXPos ();
667                         int begin_prev = 0;
668                         
669                         if (TabPages.Count == 0)
670                                 return;
671
672                         prev_row = TabPages [0].Row;
673
674                         for (int i = 0; i < TabPages.Count; i++) {
675                                 TabPage page = TabPages [i];
676                                 int width;
677
678                                 if (SizeMode == TabSizeMode.Fixed) {
679                                         width = item_size.Width;
680                                 } else {
681                                         width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
682                                 }
683
684                                 if (width < MinimumTabWidth)
685                                         width = MinimumTabWidth;
686                                 if (page.Row != prev_row)
687                                         ypos = 1;
688
689                                 page.TabBounds = new Rectangle (xpos + (row_count - page.Row) * ((item_size.Height - 2) + spacing.Width),
690                                                 ypos, item_size.Height - 2, width);
691
692                                 if (page.Row != prev_row) {
693                                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
694                                                 FillRowV (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Bottom) / (i - begin_prev)), spacing);
695                                         }
696                                         begin_prev = i;
697                                 }
698
699                                 ypos += width + spacing.Width;
700                                 prev_row = page.Row;
701                         }
702
703                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
704                                 FillRowV (begin_prev, TabPages.Count - 1,
705                                                 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Bottom) / (TabPages.Count - begin_prev)), spacing);
706                         }
707
708                         if (SelectedIndex != -1) {
709                                 ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
710                         }
711                 }
712
713                 private void SizeTabs (int row_width)
714                 {
715                         int ypos = CalcYPos ();
716                         int prev_row = 1;
717                         Size spacing = TabSpacing;
718                         int xpos = 4;
719                         int begin_prev = 0;
720
721                         if (TabPages.Count == 0)
722                                 return;
723
724                         prev_row = TabPages [0].Row;
725
726                         for (int i = slider_pos; i < TabPages.Count; i++) {
727                                 TabPage page = TabPages [i];
728                                 int width;
729  
730                                 if (SizeMode == TabSizeMode.Fixed) {
731                                         width = item_size.Width;
732                                 } else {
733                                         width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
734                                 }
735
736                                 if (width < MinimumTabWidth)
737                                         width = MinimumTabWidth;
738                                 if (page.Row != prev_row)
739                                         xpos = 4;
740
741                                 page.TabBounds = new Rectangle (xpos,
742                                                 ypos + (row_count - page.Row) * (item_size.Height + spacing.Height),
743                                                 width, item_size.Height);
744                                 
745                                 if (page.Row != prev_row) {
746                                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
747                                                 FillRow (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Right) / (i - begin_prev)), spacing);
748                                         }
749                                         begin_prev = i;
750                                 }
751
752                                 xpos += width + 1 + spacing.Width;
753                                 prev_row = page.Row;                                
754                         }
755
756                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
757                                 FillRow (begin_prev, TabPages.Count - 1,
758                                                 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Right) / (TabPages.Count - begin_prev)), spacing);
759                         }
760
761                         if (SelectedIndex != -1) {
762                                 ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
763                         }
764                 }
765                 
766                 private void FillRow (int start, int end, int amount, Size spacing)
767                 {
768                         int xpos = TabPages [start].TabBounds.Left;
769                         for (int i = start; i <= end; i++) {
770                                 TabPage page = TabPages [i];
771                                 int left = xpos;
772                                 int width = (i == end ? Width - left - 3 : page.TabBounds.Width + amount);
773
774                                 page.TabBounds = new Rectangle (left, page.TabBounds.Top,
775                                                 width, page.TabBounds.Height);
776                                 xpos = page.TabBounds.Right + 1 + spacing.Width;
777                         }
778                 }
779
780                 private void FillRowV (int start, int end, int amount, Size spacing)
781                 {
782                         int ypos = TabPages [start].TabBounds.Top;
783                         for (int i = start; i <= end; i++) {
784                                 TabPage page = TabPages [i];
785                                 int top = ypos;
786                                 int height = (i == end ? Height - top - 5 : page.TabBounds.Height + amount);
787
788                                 page.TabBounds = new Rectangle (page.TabBounds.Left, top,
789                                                 page.TabBounds.Width, height);
790                                 ypos = page.TabBounds.Bottom + 1;
791                         }
792                 }
793
794                 private void ExpandSelected (TabPage page, int left_edge, int right_edge)
795                 {
796                         if (Appearance != TabAppearance.Normal)
797                                 return;
798
799                         if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom) {
800                                 int l = page.TabBounds.Left - 4;
801                                 int r = page.TabBounds.Right + 4;
802                                 int y = page.TabBounds.Y;
803                                 int h = page.TabBounds.Height + 2;
804
805                                 if (l < left_edge)
806                                         l = left_edge;
807                                 if (r > right_edge && SizeMode != TabSizeMode.Normal)
808                                         r = right_edge;
809                                 if (Alignment == TabAlignment.Top)
810                                         y -= 1;
811                                 if (Alignment == TabAlignment.Bottom)
812                                         y -= 2;
813
814                                 page.TabBounds = new Rectangle (l, y, r - l, h);
815                         } else {
816                                 int l = page.TabBounds.Left - 3;
817                                 int r = page.TabBounds.Right + 3;
818                                 int t = page.TabBounds.Top - 3;
819                                 int b = page.TabBounds.Bottom + 3;
820
821                                 if (t < left_edge)
822                                         t = left_edge;
823                                 if (b > right_edge)
824                                         b = right_edge;
825
826                                 page.TabBounds = new Rectangle (l, t, r - l, b - t);
827                         }
828                 }
829
830                 internal void RefreshTabs ()
831                 {
832                         Redraw (true);
833                 }
834
835                 private void PaintInternal (PaintEventArgs pe)
836                 {
837                         if (this.Width <= 0 || this.Height <=  0 || this.Visible == false)
838                                 return;
839
840                         Draw (pe.ClipRectangle);
841                         pe.Graphics.DrawImage (ImageBuffer, pe.ClipRectangle,
842                                         pe.ClipRectangle, GraphicsUnit.Pixel);
843                         // On MS the Paint event never seems to be raised
844                 }
845
846                 private void Redraw (bool recalculate)
847                 {
848                         if (recalculate) {
849                                 ResizeTabPages ();
850                         }
851                         redraw = true;
852                         Refresh ();
853                 }
854
855                 private void Draw (Rectangle clip)
856                 {
857                         ThemeEngine.Current.DrawTabControl (DeviceContext, clip, this);
858                         redraw = false;
859                 }
860
861                 private TabPage GetTab (int index)
862                 {
863                         return Controls [index] as TabPage;
864                 }
865
866                 private void SetTab (int index, TabPage value)
867                 {
868                         ((IList) Controls).Insert (index, value);
869                         Refresh ();
870                 }
871
872                 #endregion      // Internal & Private Methods
873
874                 #region Events
875                 [Browsable(false)]
876                 [EditorBrowsable(EditorBrowsableState.Never)]
877                 public new event EventHandler BackColorChanged {
878                         add { base.BackColorChanged += value; }
879                         remove { base.BackColorChanged -= value; }
880                 }
881
882                 [Browsable(false)]
883                 [EditorBrowsable(EditorBrowsableState.Never)]
884                 public new event EventHandler BackgroundImageChanged {
885                         add { base.BackgroundImageChanged += value; }
886                         remove { base.BackgroundImageChanged -= value; }
887                 }
888
889                 [Browsable(false)]
890                 [EditorBrowsable(EditorBrowsableState.Never)]
891                 public new event EventHandler ForeColorChanged {
892                         add { base.ForeColorChanged += value; }
893                         remove { base.ForeColorChanged -= value; }
894                 }
895
896                 [Browsable(false)]
897                 [EditorBrowsable(EditorBrowsableState.Never)]
898                 public new event PaintEventHandler Paint {
899                         add { base.Paint += value; }
900                         remove { base.Paint -= value; }
901                 }
902
903                 [Browsable(false)]
904                 [EditorBrowsable(EditorBrowsableState.Never)]
905                 public new event EventHandler TextChanged {
906                         add { base.TextChanged += value; }
907                         remove { base.TextChanged -= value; }
908                 }
909
910                 public event DrawItemEventHandler DrawItem;
911                 public event EventHandler SelectedIndexChanged;
912                 #endregion      // Events
913
914
915                 #region Class TabPage.ControlCollection
916                 public class ControlCollection : System.Windows.Forms.Control.ControlCollection {
917
918                         private TabControl owner;
919                         private ArrayList list = new ArrayList ();
920
921                         public ControlCollection (TabControl owner) : base (owner)
922                         {
923                                 this.owner = owner;
924                         }
925
926                         public override void Add (Control value)
927                         {
928                                 if (!(value is TabPage))
929                                         throw new ArgumentException ("Cannot add " +
930                                                 value.GetType ().Name + " to TabControl. " +
931                                                 "Only TabPages can be directly added to TabControls.");
932
933                                 value.Visible = false;
934                                 base.Add (value);
935                                 if (Count == 1) {
936                                         owner.SelectedIndex = 0;
937                                 } else {
938                                         // Setting the selected index will calc the tab rows so
939                                         // we don't need to do it again
940                                         owner.ResizeTabPages ();
941                                 }
942                         }
943                 }
944                 #endregion      // Class TabPage.ControlCollection
945
946                 #region Class TabPage.TabPageCollection
947                 public class TabPageCollection  : IList, ICollection, IEnumerable {
948
949                         private TabControl owner;
950                         private IList controls;
951
952                         public TabPageCollection (TabControl owner)
953                         {
954                                 if (owner == null)
955                                         throw new ArgumentNullException ("Value cannot be null.");
956                                 this.owner = owner;
957                                 controls = owner.Controls;
958                         }
959
960                         [Browsable(false)]
961                         public virtual int Count {
962                                 get { return owner.Controls.Count; }
963                         }
964
965                         public virtual bool IsReadOnly {
966                                 get { return false; }
967                         }
968
969                         public virtual TabPage this [int index] {
970                                 get {
971                                         return owner.GetTab (index);
972                                 }
973                                 set {
974                                         owner.SetTab (index, value);
975                                 }    
976                         }
977
978                         bool ICollection.IsSynchronized {
979                                 get { return false; }
980                         }
981
982                         object ICollection.SyncRoot {
983                                 get { return this; }
984                         }
985
986                         bool IList.IsFixedSize {
987                                 get { return false; }
988                         }
989
990                         object IList.this [int index] {
991                                 get {
992                                         return owner.GetTab (index);
993                                 }
994                                 set {
995                                         owner.SetTab (index, (TabPage) value);
996                                 }
997                         }
998
999                         public void Add (TabPage page)
1000                         {
1001                                 if (page == null)
1002                                         throw new ArgumentNullException ("Value cannot be null.");
1003                                 owner.Controls.Add (page);
1004                         }
1005
1006                         public void AddRange (TabPage [] pages)
1007                         {
1008                                 if (pages == null)
1009                                         throw new ArgumentNullException ("Value cannot be null.");
1010                                 owner.Controls.AddRange (pages);
1011                         }
1012
1013                         public virtual void Clear ()
1014                         {
1015                                 owner.Controls.Clear ();
1016                         }
1017
1018                         public bool Contains (TabPage page)
1019                         {
1020                                 if (page == null)
1021                                         throw new ArgumentNullException ("Value cannot be null.");
1022                                 return owner.Controls.Contains (page);
1023                         }
1024
1025                         public virtual IEnumerator GetEnumerator ()
1026                         {
1027                                 return owner.Controls.GetEnumerator ();
1028                         }
1029
1030                         public int IndexOf (TabPage page)
1031                         {
1032                                 return owner.Controls.IndexOf (page);
1033                         }
1034
1035                         public void Remove (TabPage page)
1036                         {
1037                                 owner.Controls.Remove (page);
1038                         }
1039
1040                         public virtual void RemoveAt (int index)
1041                         {
1042                                 owner.Controls.RemoveAt (index);
1043                         }
1044
1045                         void ICollection.CopyTo (Array dest, int index)
1046                         {
1047                                 owner.Controls.CopyTo (dest, index);
1048                         }
1049
1050                         int IList.Add (object value)
1051                         {
1052                                 //       return owner.Controls.Add ((TabPage) value);
1053                                 return -1;
1054                         }
1055
1056                         bool IList.Contains (object page)
1057                         {
1058                                 return Contains ((TabPage) page);
1059                         }
1060
1061                         int IList.IndexOf (object page)
1062                         {
1063                                 return IndexOf ((TabPage) page);
1064                         }
1065
1066                         void IList.Insert (int index, object value)
1067                         {
1068                                 controls.Insert (index, (TabPage) value);
1069                         }
1070
1071                         void IList.Remove (object value)
1072                         {
1073                                 Remove ((TabPage) value);
1074                         }
1075                 }
1076                 #endregion      // Class TabPage.TabPageCollection
1077         }
1078 }
1079
1080