Merge pull request #2057 from directhex/monolite-on-jenkins
[mono.git] / mcs / class / System.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 using System.Runtime.InteropServices;
32 using System.Windows.Forms.Theming;
33 using System.Windows.Forms.VisualStyles;
34
35 namespace System.Windows.Forms {
36         [ComVisibleAttribute (true)]
37         [ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
38         [DefaultEvent("SelectedIndexChanged")]
39         [DefaultProperty("TabPages")]
40         [Designer("System.Windows.Forms.Design.TabControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
41         public class TabControl : Control {
42                 #region Fields
43                 private int selected_index = -1;
44                 private TabAlignment alignment;
45                 private TabAppearance appearance;
46                 private TabDrawMode draw_mode;
47                 private bool multiline;
48                 private ImageList image_list;
49                 private Size item_size = Size.Empty;
50                 private bool item_size_manual;
51                 private Point padding;
52                 private int row_count = 0;
53                 private bool hottrack;
54                 private TabPageCollection tab_pages;
55                 private bool show_tool_tips;
56                 private TabSizeMode size_mode;
57                 private bool show_slider = false;
58                 private PushButtonState right_slider_state = PushButtonState.Normal;
59                 private PushButtonState left_slider_state = PushButtonState.Normal;
60                 private int slider_pos = 0;
61                 TabPage entered_tab_page;
62                 bool mouse_down_on_a_tab_page;
63                 ToolTip tooltip;
64                 ToolTip.TipState tooltip_state = ToolTip.TipState.Down;
65                 Timer tooltip_timer;
66
67                 private bool rightToLeftLayout;
68                 #endregion      // Fields
69
70                 #region UIA Framework Events
71                 static object UIAHorizontallyScrollableChangedEvent = new object ();
72
73                 internal event EventHandler UIAHorizontallyScrollableChanged {
74                         add { Events.AddHandler (UIAHorizontallyScrollableChangedEvent, value); }
75                         remove { Events.RemoveHandler (UIAHorizontallyScrollableChangedEvent, value); }
76                 }
77
78                 internal void OnUIAHorizontallyScrollableChanged (EventArgs e)
79                 {
80                         EventHandler eh
81                                 = (EventHandler) Events [UIAHorizontallyScrollableChangedEvent];
82                         if (eh != null)
83                                 eh (this, e);
84                 }
85
86                 static object UIAHorizontallyScrolledEvent = new object ();
87
88                 internal event EventHandler UIAHorizontallyScrolled {
89                         add { Events.AddHandler (UIAHorizontallyScrolledEvent, value); }
90                         remove { Events.RemoveHandler (UIAHorizontallyScrolledEvent, value); }
91                 }
92
93                 internal void OnUIAHorizontallyScrolled (EventArgs e)
94                 {
95                         EventHandler eh
96                                 = (EventHandler) Events [UIAHorizontallyScrolledEvent];
97                         if (eh != null)
98                                 eh (this, e);
99                 }
100                 #endregion
101
102                 #region UIA Framework Property
103                 internal double UIAHorizontalViewSize {
104                         get { return LeftScrollButtonArea.Left * 100 / TabPages [TabCount - 1].TabBounds.Right; }
105                 }
106                 #endregion
107
108                 #region Public Constructors
109                 public TabControl ()
110                 {
111                         tab_pages = new TabPageCollection (this);
112                         SetStyle (ControlStyles.UserPaint, false);
113                         padding = ThemeEngine.Current.TabControlDefaultPadding;
114
115                         MouseDown += new MouseEventHandler (MouseDownHandler);
116                         MouseLeave += new EventHandler (OnMouseLeave);
117                         MouseMove += new MouseEventHandler (OnMouseMove);
118                         MouseUp += new MouseEventHandler (MouseUpHandler);
119                         SizeChanged += new EventHandler (SizeChangedHandler);
120                 }
121
122                 #endregion      // Public Constructors
123
124                 #region Public Instance Properties
125                 [DefaultValue(TabAlignment.Top)]
126                 [Localizable(true)]
127                 [RefreshProperties(RefreshProperties.All)]
128                 public TabAlignment Alignment {
129                         get { return alignment; }
130                         set {
131                                 if (alignment == value)
132                                         return;
133                                 alignment = value;
134                                 if (alignment == TabAlignment.Left || alignment == TabAlignment.Right)
135                                         multiline = true;
136                                 Redraw ();
137                         }
138                 }
139
140                 [DefaultValue(TabAppearance.Normal)]
141                 [Localizable(true)]
142                 public TabAppearance Appearance {
143                         get { return appearance; }
144                         set {
145                                 if (appearance == value)
146                                         return;
147                                 appearance = value;
148                                 Redraw ();
149                         }
150                 }
151
152                 [Browsable(false)]
153                 [EditorBrowsable(EditorBrowsableState.Never)]
154                 public override Color BackColor {
155                         get { return ThemeEngine.Current.ColorControl; }
156                         set { /* nothing happens on set on MS */ }
157                 }
158
159                 [Browsable(false)]
160                 [EditorBrowsable(EditorBrowsableState.Never)]
161                 public override Image BackgroundImage {
162                         get { return base.BackgroundImage; }
163                         set { base.BackgroundImage = value; }
164                 }
165
166                 [Browsable (false)]
167                 [EditorBrowsable (EditorBrowsableState.Never)]
168                 public override ImageLayout BackgroundImageLayout {
169                         get { return base.BackgroundImageLayout; }
170                         set { base.BackgroundImageLayout = value; }
171                 }
172                 
173                 public override Rectangle DisplayRectangle {
174                         get {
175                                 return ThemeEngine.Current.TabControlGetDisplayRectangle (this);
176                         }
177                 }
178
179                 [EditorBrowsable (EditorBrowsableState.Never)]
180                 protected override bool DoubleBuffered {
181                         get { return base.DoubleBuffered; }
182                         set { base.DoubleBuffered = value; }
183                 }
184
185                 [DefaultValue(TabDrawMode.Normal)]
186                 public TabDrawMode DrawMode {
187                         get { return draw_mode; }
188                         set {
189                                 if (draw_mode == value)
190                                         return;
191                                 draw_mode = value;
192                                 Redraw ();
193                         }
194                 }
195
196                 [Browsable(false)]
197                 [EditorBrowsable(EditorBrowsableState.Never)]
198                 public override Color ForeColor {
199                         get { return base.ForeColor; }
200                         set { base.ForeColor = value; }
201                 }
202
203                 [DefaultValue(false)]
204                 public bool HotTrack {
205                         get { return hottrack; }
206                         set {
207                                 if (hottrack == value)
208                                         return;
209                                 hottrack = value;
210                                 Redraw ();
211                         }
212                 }
213
214                 [RefreshProperties (RefreshProperties.Repaint)]
215                 [DefaultValue(null)]
216                 public ImageList ImageList {
217                         get { return image_list; }
218                         set { 
219                                 image_list = value; 
220                                 Redraw ();
221                         }
222                 }
223
224                 [Localizable(true)]
225                 public Size ItemSize {
226                         get {
227                                 if (item_size_manual)
228                                         return item_size;
229
230                                 if (!IsHandleCreated)
231                                         return Size.Empty;
232
233                                 Size size = item_size;
234                                 if (SizeMode != TabSizeMode.Fixed) {
235                                         size.Width += padding.X * 2;
236                                         size.Height += padding.Y * 2;
237                                 }
238
239                                 if (tab_pages.Count == 0)
240                                         size.Width = 0;
241
242                                 return size;
243                         }
244                         set {
245                                 if (value.Height < 0 || value.Width < 0)
246                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
247                                 item_size = value;
248                                 item_size_manual = true;
249                                 Redraw ();
250                         }
251                 }
252
253                 [DefaultValue(false)]
254                 public bool Multiline {
255                         get { return multiline; }
256                         set {
257                                 if (multiline == value)
258                                         return;
259                                 multiline = value;
260                                 if (!multiline && alignment == TabAlignment.Left || alignment == TabAlignment.Right)
261                                         alignment = TabAlignment.Top;
262                                 Redraw ();
263                         }
264                 }
265
266                 [Localizable(true)]
267                 public
268                 new
269                 Point Padding {
270                         get { return padding; }
271                         set {
272                                 if (value.X < 0 || value.Y < 0)
273                                         throw new ArgumentException ("'" + value + "' is not a valid value for 'Padding'.");
274                                 if (padding == value)
275                                         return;
276                                 padding = value;
277                                 Redraw ();
278                         }
279
280                 }
281
282                 [MonoTODO ("RTL not supported")]
283                 [Localizable (true)]
284                 [DefaultValue (false)]
285                 public virtual bool RightToLeftLayout {
286                         get { return this.rightToLeftLayout; }
287                         set {
288                                 if (value != this.rightToLeftLayout) {
289                                         this.rightToLeftLayout = value;
290                                         this.OnRightToLeftLayoutChanged (EventArgs.Empty);
291                                 }
292                         }
293                 }
294
295                 [Browsable(false)]
296                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
297                 public int RowCount {
298                         get { return row_count; }
299                 }
300
301                 [DefaultValue(-1)]
302                 [Browsable(false)]
303                 public int SelectedIndex {
304                         get { return selected_index; }
305                         set {
306
307                                 if (value < -1) {
308                                         throw new ArgumentOutOfRangeException ("SelectedIndex", "Value of '" + value + "' is valid for 'SelectedIndex'. " +
309                                                 "'SelectedIndex' must be greater than or equal to -1.");
310                                 }
311                                 if (!this.IsHandleCreated) {
312                                         if (selected_index != value) {
313                                                 selected_index = value;
314                                         }
315                                         return;
316                                 }
317
318                                 if (value >= TabCount) {
319                                         if (value != selected_index)
320                                                 OnSelectedIndexChanged (EventArgs.Empty);
321                                         return;
322                                 }
323
324                                 if (value == selected_index) {
325                                         if (selected_index > -1)
326                                                 Invalidate(GetTabRect (selected_index));
327                                         return;
328                                 }
329
330                                 TabControlCancelEventArgs ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Deselecting);
331                                 OnDeselecting (ret);
332                                 if (ret.Cancel)
333                                         return;
334
335                                 Focus ();
336                                 int old_index = selected_index;
337                                 int new_index = value;
338
339                                 selected_index = new_index;
340
341                                 ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Selecting);
342                                 OnSelecting (ret);
343                                 if (ret.Cancel) {
344                                         selected_index = old_index;
345                                         return;
346                                 }
347
348                                 SuspendLayout ();
349
350                                 Rectangle invalid = Rectangle.Empty;
351                                 bool refresh = false;
352
353                                 if (new_index != -1 && show_slider && new_index < slider_pos) {
354                                         slider_pos = new_index;
355                                         refresh = true;
356                                 }
357
358                                 if (new_index != -1) {
359                                         int le = TabPages[new_index].TabBounds.Right;
360                                         int re = LeftScrollButtonArea.Left;
361                                         if (show_slider && le > re) {
362                                                 int i = 0;
363                                                 for (i = 0; i < new_index; i++) {
364                                                         if (TabPages [i].TabBounds.Left < 0) // tab scrolled off the visible area, ignore
365                                                                 continue;
366
367                                                         if (TabPages [new_index].TabBounds.Right - TabPages[i].TabBounds.Right < re) {
368                                                                 i++;
369                                                                 break;
370                                                         }
371                                                 }
372                                                 slider_pos = i;
373                                                 refresh = true;
374                                         }
375                                 }
376
377                                 if (old_index != -1 && new_index != -1) {
378                                         if (!refresh)
379                                                 invalid = GetTabRect (old_index);
380                                         ((TabPage) Controls[old_index]).SetVisible (false);
381                                 }
382
383                                 TabPage selected = null;
384
385                                 if (new_index != -1) {
386                                         selected = (TabPage) Controls[new_index];
387                                         invalid = Rectangle.Union (invalid, GetTabRect (new_index));
388                                         selected.SetVisible (true);
389                                 }
390
391                                 OnSelectedIndexChanged (EventArgs.Empty);
392
393                                 ResumeLayout ();
394
395                                 if (refresh) {
396                                         SizeTabs ();
397                                         Refresh ();
398                                 } else if (new_index != -1 && selected.Row != BottomRow) {
399                                         DropRow (TabPages[new_index].Row);
400                                         // calculating what to invalidate here seems to be slower then just
401                                         // refreshing the whole thing
402                                         SizeTabs ();
403                                         Refresh ();
404                                 } else {
405                                         SizeTabs ();
406                                         // The lines are drawn on the edges of the tabs so the invalid area should
407                                         // needs to include the extra pixels of line width (but should not
408                                         // overflow the control bounds).
409                                         if (appearance == TabAppearance.Normal) {
410                                                 invalid.Inflate (6, 4);
411                                                 invalid.Intersect (ClientRectangle);
412                                         }
413                                         Invalidate (invalid);
414                                 }
415                         }
416                 }
417
418                 [Browsable(false)]
419                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
420                 public TabPage SelectedTab {
421                         get {
422                                 if (selected_index == -1)
423                                         return null;
424                                 return tab_pages [selected_index];
425                         }
426                         set {
427                                 int index = IndexForTabPage (value);
428                                 if (index == selected_index)
429                                         return;
430                                 SelectedIndex = index;
431                         }
432                 }
433
434                 [DefaultValue(false)]
435                 [Localizable(true)]
436                 public bool ShowToolTips {
437                         get { return show_tool_tips; }
438                         set {
439                                 if (show_tool_tips == value)
440                                         return;
441                                 show_tool_tips = value;
442                         }
443                 }
444
445                 [DefaultValue(TabSizeMode.Normal)]
446                 [RefreshProperties(RefreshProperties.Repaint)]
447                 public TabSizeMode SizeMode {
448                         get { return size_mode; }
449                         set {
450                                 if (size_mode == value)
451                                         return;
452                                 size_mode = value;
453                                 Redraw ();
454                         }
455                 }
456
457                 [Browsable(false)]
458                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
459                 public int TabCount {
460                         get {
461                                 return tab_pages.Count;
462                         }
463                 }
464
465                 [Editor ("System.Windows.Forms.Design.TabPageCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
466                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
467                 [MergableProperty(false)]
468                 public TabPageCollection TabPages {
469                         get { return tab_pages; }
470                 }
471
472                 [Browsable(false)]
473                 [Bindable(false)]
474                 [EditorBrowsable(EditorBrowsableState.Never)]
475                 public override string Text {
476                         get { return base.Text; }
477                         set { base.Text = value; }
478                 }
479                 #endregion      // Public Instance Properties
480
481                 #region Internal Properties
482                 internal bool ShowSlider {
483                         get { return show_slider; }
484                         set {
485                                 show_slider = value;
486
487                                 // UIA Framework Event: HorizontallyScrollable Changed
488                                 OnUIAHorizontallyScrollableChanged (EventArgs.Empty);
489                         }
490                 }
491
492                 internal int SliderPos {
493                         get { return slider_pos; }
494                 }
495
496                 internal PushButtonState RightSliderState {
497                         get { return right_slider_state; }
498                         private set {
499                                 if (right_slider_state == value)
500                                         return;
501                                 PushButtonState old_value = right_slider_state;
502                                 right_slider_state = value;
503                                 if (NeedsToInvalidateScrollButton (old_value, value))
504                                         Invalidate (RightScrollButtonArea);
505                         }
506                 }
507
508                 internal PushButtonState LeftSliderState {
509                         get { return left_slider_state; }
510                         set {
511                                 if (left_slider_state == value)
512                                         return;
513                                 PushButtonState old_value = left_slider_state;
514                                 left_slider_state = value;
515                                 if (NeedsToInvalidateScrollButton (old_value, value))
516                                         Invalidate (LeftScrollButtonArea);
517                         }
518                 }
519
520                 bool NeedsToInvalidateScrollButton (PushButtonState oldState, PushButtonState newState)
521                 {
522                         if ((oldState == PushButtonState.Hot && newState == PushButtonState.Normal) ||
523                                 (oldState == PushButtonState.Normal && newState == PushButtonState.Hot))
524                                 return HasHotElementStyles;
525                         return true;
526                 }
527
528                 internal TabPage EnteredTabPage {
529                         get { return entered_tab_page; }
530                         private set {
531                                 if (entered_tab_page == value)
532                                         return;
533                                 if (HasHotElementStyles) {
534                                         Region area_to_invalidate = new Region ();
535                                         area_to_invalidate.MakeEmpty ();
536                                         if (entered_tab_page != null)
537                                                 area_to_invalidate.Union (entered_tab_page.TabBounds);
538                                         entered_tab_page = value;
539                                         if (entered_tab_page != null)
540                                                 area_to_invalidate.Union (entered_tab_page.TabBounds);
541                                         Invalidate (area_to_invalidate);
542                                         area_to_invalidate.Dispose ();
543                                 } else
544                                         entered_tab_page = value;
545
546                                 if (value == null)
547                                         CloseToolTip ();
548                                 else
549                                         SetToolTip (GetToolTipText (value));
550                         }
551                 }
552                 #endregion      // Internal Properties
553
554                 #region Protected Instance Properties
555                 protected override CreateParams CreateParams {
556                         get {
557                                 CreateParams c = base.CreateParams;
558                                 return c;
559                         }
560                 }
561
562                 protected override Size DefaultSize {
563                         get { return new Size (200, 100); }  
564                 }
565
566                 #endregion      // Protected Instance Properties
567
568                 #region Public Instance Methods
569                 public Rectangle GetTabRect (int index)
570                 {
571                         TabPage page = GetTab (index);
572                         return page.TabBounds;
573                 }
574
575                 public Control GetControl (int index)
576                 {
577                         return GetTab (index);
578                 }
579
580                 public void SelectTab (TabPage tabPage)
581                 {
582                         if (tabPage == null)
583                                 throw new ArgumentNullException ("tabPage");
584
585                         SelectTab (this.tab_pages [tabPage]);
586                 }
587
588                 public void SelectTab (string tabPageName)
589                 {
590                         if (tabPageName == null)
591                                 throw new ArgumentNullException ("tabPageName");
592
593                         SelectTab (this.tab_pages [tabPageName]);
594                 }
595
596                 public void SelectTab (int index)
597                 {
598                         if (index < 0 || index > this.tab_pages.Count - 1)
599                                 throw new ArgumentOutOfRangeException ("index");
600                                 
601                         SelectedIndex = index;
602                 }
603
604                 public void DeselectTab (TabPage tabPage)
605                 {
606                         if (tabPage == null)
607                                 throw new ArgumentNullException ("tabPage");
608
609                         DeselectTab (this.tab_pages [tabPage]);
610                 }
611
612                 public void DeselectTab (string tabPageName)
613                 {
614                         if (tabPageName == null)
615                                 throw new ArgumentNullException ("tabPageName");
616
617                         DeselectTab (this.tab_pages [tabPageName]);
618                 }
619
620                 public void DeselectTab (int index)
621                 {
622                         if (index == SelectedIndex) {
623                                 if (index >= 0 && index < this.tab_pages.Count - 1)
624                                         SelectedIndex = ++index;
625                                 else
626                                         SelectedIndex = 0;
627                         }
628                 }
629
630
631                 public override string ToString ()
632                 {
633                         string res = String.Concat (base.ToString (),
634                                         ", TabPages.Count: ",
635                                         TabCount);
636                         if (TabCount > 0)
637                                 res = String.Concat (res, ", TabPages[0]: ",
638                                                 TabPages [0]);
639                         return res;
640                 }
641
642                 #endregion      // Public Instance Methods
643
644                 #region Protected Instance Methods
645
646                 #region Handles
647                 protected override Control.ControlCollection CreateControlsInstance ()
648                 {
649                         return new TabControl.ControlCollection (this);
650                 }
651
652                 protected override void CreateHandle ()
653                 {
654                         base.CreateHandle ();
655                         selected_index = (selected_index >= TabCount ? (TabCount > 0 ? 0 : -1) : selected_index);
656
657                         if (TabCount > 0) {
658                                 if (selected_index > -1)
659                                         this.SelectedTab.SetVisible(true);
660                                 else
661                                         tab_pages[0].SetVisible(true);
662                         }
663                         ResizeTabPages ();
664                 }
665
666                 protected override void OnHandleCreated (EventArgs e)
667                 {
668                         base.OnHandleCreated (e);
669                 }
670
671                 protected override void OnHandleDestroyed (EventArgs e)
672                 {
673                         base.OnHandleDestroyed (e);
674                 }
675
676                 protected override void Dispose (bool disposing)
677                 {
678                         CloseToolTip ();
679                         base.Dispose (disposing);
680                 }
681
682                 #endregion
683
684                 #region Events
685                 protected virtual void OnDrawItem (DrawItemEventArgs e)
686                 {
687                         if (DrawMode != TabDrawMode.OwnerDrawFixed)
688                                 return;
689
690                         DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
691                         if (eh != null)
692                                 eh (this, e);
693                 }
694
695                 internal void OnDrawItemInternal (DrawItemEventArgs e)
696                 {
697                         OnDrawItem (e);
698                 }
699
700                 protected override void OnFontChanged (EventArgs e)
701                 {
702                         base.OnFontChanged (e);
703                         ResizeTabPages ();
704                 }
705
706                 protected override void OnResize (EventArgs e)
707                 {
708                         base.OnResize (e);
709                 }
710
711                 protected override void OnStyleChanged (EventArgs e)
712                 {
713                         base.OnStyleChanged (e);
714                 }
715
716                 protected virtual void OnSelectedIndexChanged (EventArgs e)
717                 {
718                         EventHandler eh = (EventHandler) (Events[SelectedIndexChangedEvent]);
719                         if (eh != null)
720                                 eh (this, e);
721                 }
722
723                 internal override void OnPaintInternal (PaintEventArgs pe)
724                 {
725                         if (GetStyle (ControlStyles.UserPaint))
726                                 return;
727
728                         Draw (pe.Graphics, pe.ClipRectangle);
729                         pe.Handled = true;
730                 }
731
732                 protected override void OnEnter (EventArgs e)
733                 {
734                         base.OnEnter (e);
735                         if (SelectedTab != null)
736                                 SelectedTab.FireEnter ();
737                 }
738
739                 protected override void OnLeave (EventArgs e)
740                 {
741                         if (SelectedTab != null)
742                                 SelectedTab.FireLeave ();
743                         base.OnLeave (e);
744                 }
745
746                 [EditorBrowsable (EditorBrowsableState.Advanced)]
747                 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
748                 {
749                         EventHandler eh = (EventHandler) (Events[RightToLeftLayoutChangedEvent]);
750                         if (eh != null)
751                                 eh (this, e);
752                 }
753
754                 [EditorBrowsable (EditorBrowsableState.Never)]
755                 protected override void ScaleCore (float dx, float dy)
756                 {
757                         base.ScaleCore (dx, dy);
758                 }
759
760                 protected virtual void OnDeselecting (TabControlCancelEventArgs e)
761                 {
762                         TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[DeselectingEvent]);
763                         if (eh != null)
764                                 eh (this, e);
765
766                         if (!e.Cancel)
767                                 OnDeselected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Deselected));
768                 }
769
770                 protected virtual void OnDeselected (TabControlEventArgs e)
771                 {
772                         TabControlEventHandler eh = (TabControlEventHandler) (Events[DeselectedEvent]);
773                         if (eh != null)
774                                 eh (this, e);
775
776                         if (this.SelectedTab != null)
777                                 this.SelectedTab.FireLeave ();
778                 }
779
780                 protected virtual void OnSelecting (TabControlCancelEventArgs e)
781                 {
782                         TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[SelectingEvent]);
783                         if (eh != null)
784                                 eh (this, e);
785
786                         if (!e.Cancel)
787                                 OnSelected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Selected));
788                 }
789
790                 protected virtual void OnSelected (TabControlEventArgs e)
791                 {
792                         TabControlEventHandler eh = (TabControlEventHandler) (Events[SelectedEvent]);
793                         if (eh != null)
794                                 eh (this, e);
795
796                         if (this.SelectedTab != null)
797                                 this.SelectedTab.FireEnter ();
798                 }
799
800                 #endregion
801
802                 #region Keys
803                 protected override bool ProcessKeyPreview (ref Message m)
804                 {
805                         return base.ProcessKeyPreview (ref m);
806                 }
807
808                 protected override void OnKeyDown (KeyEventArgs ke)
809                 {
810                         base.OnKeyDown (ke);
811                         if (ke.Handled)
812                                 return;
813
814                         if (ke.KeyCode == Keys.Tab && (ke.KeyData & Keys.Control) != 0) {
815                                 if ((ke.KeyData & Keys.Shift) == 0)
816                                         SelectedIndex = (SelectedIndex + 1) % TabCount;
817                                 else
818                                         SelectedIndex = (SelectedIndex + TabCount - 1) % TabCount;
819                                 ke.Handled = true;
820                         } else if (ke.KeyCode == Keys.PageUp && (ke.KeyData & Keys.Control) != 0) {
821                                 SelectedIndex = (SelectedIndex + TabCount - 1) % TabCount;
822                                 ke.Handled = true;
823                         } else if (ke.KeyCode == Keys.PageDown && (ke.KeyData & Keys.Control) != 0) {
824                                 SelectedIndex = (SelectedIndex + 1) % TabCount;
825                                 ke.Handled = true;
826                         } else if (ke.KeyCode == Keys.Home) {
827                                 SelectedIndex = 0;
828                                 ke.Handled = true;
829                         } else if (ke.KeyCode == Keys.End) {
830                                 SelectedIndex = TabCount - 1;
831                                 ke.Handled = true;
832                         } else if (NavigateTabs (ke.KeyCode))
833                                 ke.Handled = true;
834                 }
835
836                 protected override bool IsInputKey (Keys keyData)
837                 {
838                         switch (keyData & Keys.KeyCode) {
839                         case Keys.Home:
840                         case Keys.End:
841                         case Keys.Left:
842                         case Keys.Right:
843                         case Keys.Up:
844                         case Keys.Down:
845                                 return true;
846                         }
847                         return base.IsInputKey (keyData);
848                 }
849                 
850                 private bool NavigateTabs (Keys keycode)
851                 {
852                         bool move_left = false;
853                         bool move_right = false;
854                         
855                         if (alignment == TabAlignment.Bottom || alignment == TabAlignment.Top) {
856                                 if (keycode == Keys.Left)
857                                         move_left = true;
858                                 else if (keycode == Keys.Right)
859                                         move_right = true;
860                         } else {
861                                 if (keycode == Keys.Up)
862                                         move_left = true;
863                                 else if (keycode == Keys.Down)
864                                         move_right = true;
865                         }
866                                 
867                         if (move_left) {
868                                 if (SelectedIndex > 0) {
869                                         SelectedIndex--;
870                                         return true;
871                                 }
872                         }
873                         
874                         if (move_right) {
875                                 if (SelectedIndex < TabCount - 1) {
876                                         SelectedIndex++;
877                                         return true;
878                                 }
879                         }
880                         
881                         return false;
882                 }
883                 #endregion
884
885                 #region Pages Collection
886                 protected void RemoveAll ()
887                 {
888                         Controls.Clear ();
889                 }
890
891                 protected virtual object [] GetItems ()
892                 {
893                         TabPage [] pages = new TabPage [Controls.Count];
894                         Controls.CopyTo (pages, 0);
895                         return pages;
896                 }
897
898                 protected virtual object [] GetItems (Type baseType)
899                 {
900                         object[] pages = (object[])Array.CreateInstance (baseType, Controls.Count);
901                         Controls.CopyTo (pages, 0);
902                         return pages;
903                 }
904                 #endregion
905
906                 protected void UpdateTabSelection (bool updateFocus)
907                 {
908                         ResizeTabPages ();
909                 }
910
911                 protected string GetToolTipText (object item)
912                 {
913                         TabPage page = (TabPage) item;
914                         return page.ToolTipText;
915                 }
916
917                 protected override void WndProc (ref Message m)
918                 {
919                         switch ((Msg)m.Msg) {
920                         case Msg.WM_SETFOCUS:
921                                 if (selected_index != -1)
922                                         Invalidate(GetTabRect(selected_index));
923                                 base.WndProc (ref m);
924                                 break;
925                         case Msg.WM_KILLFOCUS:
926                                 if (selected_index != -1)
927                                         Invalidate(GetTabRect(selected_index));
928                                 base.WndProc (ref m);
929                                 break;
930                         default:
931                                 base.WndProc (ref m);
932                                 break;
933                         }
934                 }
935
936                 #endregion      // Protected Instance Methods
937
938                 #region Internal & Private Methods
939                 private bool CanScrollRight {
940                         get {
941                                 return (slider_pos < TabCount - 1);
942                         }
943                 }
944
945                 private bool CanScrollLeft {
946                         get { return slider_pos > 0; }
947                 }
948
949                 private void MouseDownHandler (object sender, MouseEventArgs e)
950                 {
951                         if ((e.Button & MouseButtons.Left) == 0)
952                                 return;
953
954                         if (ShowSlider) {
955                                 Rectangle right = RightScrollButtonArea;
956                                 Rectangle left = LeftScrollButtonArea;
957                                 if (right.Contains (e.X, e.Y)) {
958                                         right_slider_state = PushButtonState.Pressed;
959                                         if (CanScrollRight) {
960                                                 slider_pos++;
961                                                 SizeTabs ();
962
963                                                 // UIA Framework Event: Horizontally Scrolled
964                                                 OnUIAHorizontallyScrolled (EventArgs.Empty);
965
966                                                 switch (this.Alignment) {
967                                                         case TabAlignment.Top:
968                                                                 Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
969                                                                 break;
970                                                         case TabAlignment.Bottom:
971                                                                 Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
972                                                                 break;
973                                                         case TabAlignment.Left:
974                                                                 Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
975                                                                 break;
976                                                         case TabAlignment.Right:
977                                                                 Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
978                                                                 break;
979                                                 }
980                                                 
981                                         } else {
982                                                 Invalidate (right);
983                                         }
984                                         return;
985                                 } else if (left.Contains (e.X, e.Y)) {
986                                         left_slider_state = PushButtonState.Pressed;
987                                         if (CanScrollLeft) {
988                                                 slider_pos--;
989                                                 SizeTabs ();
990
991                                                 // UIA Framework Event: Horizontally Scrolled
992                                                 OnUIAHorizontallyScrolled (EventArgs.Empty);
993
994                                                 switch (this.Alignment) {
995                                                         case TabAlignment.Top:
996                                                                 Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
997                                                                 break;
998                                                         case TabAlignment.Bottom:
999                                                                 Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
1000                                                                 break;
1001                                                         case TabAlignment.Left:
1002                                                                 Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
1003                                                                 break;
1004                                                         case TabAlignment.Right:
1005                                                                 Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
1006                                                                 break;
1007                                                 }
1008                                         } else {
1009                                                 Invalidate (left);
1010                                         }
1011                                         return;
1012                                 }
1013                         }
1014
1015                         int count = Controls.Count;
1016                         for (int i = SliderPos; i < count; i++) {
1017                                 if (!GetTabRect (i).Contains (e.X, e.Y))
1018                                         continue;
1019                                 SelectedIndex = i;
1020                                 mouse_down_on_a_tab_page = true;
1021                                 break;
1022                         }
1023                 }
1024
1025                 private void MouseUpHandler (object sender, MouseEventArgs e)
1026                 {
1027                         mouse_down_on_a_tab_page = false;
1028                         if (ShowSlider && (left_slider_state == PushButtonState.Pressed || right_slider_state == PushButtonState.Pressed)) {
1029                                 Rectangle invalid;
1030                                 if (left_slider_state == PushButtonState.Pressed) {
1031                                         invalid = LeftScrollButtonArea;
1032                                         left_slider_state = GetScrollButtonState (invalid, e.Location);
1033                                 } else {
1034                                         invalid = RightScrollButtonArea;
1035                                         right_slider_state = GetScrollButtonState (invalid, e.Location);
1036                                 }
1037                                 Invalidate (invalid);
1038                         }
1039                 }
1040
1041                 bool HasHotElementStyles {
1042                         get {
1043                                 return ThemeElements.CurrentTheme.TabControlPainter.HasHotElementStyles (this);
1044                         }
1045                 }
1046
1047                 Rectangle LeftScrollButtonArea {
1048                         get {
1049                                 return ThemeElements.CurrentTheme.TabControlPainter.GetLeftScrollRect (this);
1050                         }
1051                 }
1052
1053                 Rectangle RightScrollButtonArea {
1054                         get {
1055                                 return ThemeElements.CurrentTheme.TabControlPainter.GetRightScrollRect (this);
1056                         }
1057                 }
1058
1059                 static PushButtonState GetScrollButtonState (Rectangle scrollButtonArea, Point cursorLocation)
1060                 {
1061                         return scrollButtonArea.Contains (cursorLocation) ? PushButtonState.Hot : PushButtonState.Normal;
1062                 }
1063
1064                 private void SizeChangedHandler (object sender, EventArgs e)
1065                 {
1066                         Redraw ();
1067                 }
1068
1069                 internal int IndexForTabPage (TabPage page)
1070                 {
1071                         for (int i = 0; i < tab_pages.Count; i++) {
1072                                 if (page == tab_pages [i])
1073                                         return i;
1074                         }
1075                         return -1;
1076                 }
1077
1078                 private void ResizeTabPages ()
1079                 {
1080                         CalcTabRows ();
1081                         SizeTabs ();
1082                         Rectangle r = DisplayRectangle;
1083                         foreach (TabPage page in Controls) {
1084                                 page.Bounds = r;
1085                         }
1086                 }
1087
1088                 private int MinimumTabWidth {
1089                         get {
1090                                 return ThemeEngine.Current.TabControlMinimumTabWidth;
1091                         }
1092                 }
1093
1094                 private Size TabSpacing {
1095                         get {
1096                                 return ThemeEngine.Current.TabControlGetSpacing (this);
1097                         }
1098                 }
1099
1100                 private void CalcTabRows ()
1101                 {
1102                         switch (Alignment) {
1103                         case TabAlignment.Right:
1104                         case TabAlignment.Left:
1105                                 CalcTabRows (Height);
1106                                 break;
1107                         default:
1108                                 CalcTabRows (Width);
1109                                 break;
1110                         }
1111                 }
1112
1113                 private void CalcTabRows (int row_width)
1114                 {
1115                         int xpos = 0;
1116                         int ypos = 0;
1117                         Size spacing = TabSpacing;
1118
1119                         if (TabPages.Count > 0)
1120                                 row_count = 1;
1121                         show_slider = false;
1122                         
1123                         CalculateItemSize ();
1124
1125                         for (int i = 0; i < TabPages.Count; i++) {
1126                                 TabPage page = TabPages [i];
1127                                 int aux = 0;
1128                                 SizeTab (page, i, row_width, ref xpos, ref ypos, spacing, 0, ref aux, true);
1129                         }
1130
1131                         if (SelectedIndex != -1 && TabPages.Count > SelectedIndex && TabPages[SelectedIndex].Row != BottomRow)
1132                                 DropRow (TabPages [SelectedIndex].Row);
1133                 }
1134
1135                 // ItemSize per-se is used mostly only to retrieve the Height,
1136                 // since the actual Width of the tabs is computed individually,
1137                 // except when SizeMode is TabSizeMode.Fixed, where Width is used as well.
1138                 private void CalculateItemSize ()
1139                 {
1140                         if (item_size_manual)
1141                                 return;
1142
1143                         SizeF size;
1144                         if (tab_pages.Count > 0) {
1145                                 // .Net uses the first tab page if available.
1146                                 size = TextRenderer.MeasureString (tab_pages [0].Text, Font);
1147
1148                         } else {
1149                                 size = TextRenderer.MeasureString ("a", Font);
1150                                 size.Width = 0;
1151                         }
1152
1153                         if (size_mode == TabSizeMode.Fixed)
1154                                 size.Width = 96;
1155                         if (size.Width < MinimumTabWidth)
1156                                 size.Width = MinimumTabWidth;
1157                         if (image_list != null && image_list.ImageSize.Height > size.Height)
1158                                 size.Height = image_list.ImageSize.Height;
1159
1160                         item_size = size.ToSize ();
1161                 }
1162
1163                 private int BottomRow {
1164                         get { return 1; }
1165                 }
1166
1167                 private int Direction
1168                 {
1169                         get {
1170                                 return 1;
1171                         }
1172                 }
1173
1174                 private void DropRow (int row)
1175                 {
1176                         if (Appearance != TabAppearance.Normal)
1177                                 return;
1178
1179                         int bottom = BottomRow;
1180                         int direction = Direction;
1181
1182                         foreach (TabPage page in TabPages) {
1183                                 if (page.Row == row) {
1184                                         page.Row = bottom;
1185                                 } else if (direction == 1 && page.Row < row) {
1186                                         page.Row += direction;
1187                                 } else if (direction == -1 && page.Row > row) {
1188                                         page.Row += direction;
1189                                 }
1190                         }
1191                 }
1192
1193                 private int CalcYPos ()
1194                 {
1195                         if (Alignment == TabAlignment.Bottom || Alignment == TabAlignment.Left)
1196                                 return ThemeEngine.Current.TabControlGetPanelRect (this).Bottom;
1197
1198                         if (Appearance == TabAppearance.Normal)
1199                                 return this.ClientRectangle.Y + ThemeEngine.Current.TabControlSelectedDelta.Y;
1200
1201                         return this.ClientRectangle.Y;
1202
1203                 }
1204
1205                 private int CalcXPos ()
1206                 {
1207                         if (Alignment == TabAlignment.Right)
1208                                 return ThemeEngine.Current.TabControlGetPanelRect (this).Right;
1209
1210                         if (Appearance == TabAppearance.Normal)
1211                                 return this.ClientRectangle.X + ThemeEngine.Current.TabControlSelectedDelta.X;
1212
1213                         return this.ClientRectangle.X;
1214                 }
1215
1216                 private void SizeTabs ()
1217                 {
1218                         switch (Alignment) {
1219                         case TabAlignment.Right:
1220                         case TabAlignment.Left:
1221                                 SizeTabs (Height, true);
1222                                 break;
1223                         default:
1224                                 SizeTabs (Width, false);
1225                                 break;
1226                         }
1227                 }
1228                 
1229                 private void SizeTabs (int row_width, bool vertical)
1230                 {
1231                         int ypos = 0;
1232                         int xpos = 0;
1233                         int prev_row = 1;
1234                         Size spacing = TabSpacing;
1235                         int begin_prev = 0;
1236
1237                         if (TabPages.Count == 0)
1238                                 return;
1239
1240                         prev_row = TabPages [0].Row;
1241
1242                         // Reset the slider position if the slider isn't needed
1243                         // anymore (ie window size was increased so all tabs are visible)
1244                         if (!show_slider)
1245                                 slider_pos = 0;
1246                         else {
1247                                 // set X = -1 for marking tabs that are not visible due to scrolling
1248                                 for (int i = 0; i < slider_pos; i++) {
1249                                         TabPage page = TabPages[i];
1250                                         Rectangle x = page.TabBounds;
1251                                         x.X = -1;
1252                                         page.TabBounds = x;
1253                                 }
1254                         }
1255                         
1256                         for (int i = slider_pos; i < TabPages.Count; i++) {
1257                                 TabPage page = TabPages[i];
1258                                 SizeTab (page, i, row_width, ref xpos, ref ypos, spacing, prev_row, ref begin_prev, false);
1259                                 prev_row = page.Row;
1260                         }
1261
1262                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
1263                                 FillRow (begin_prev, TabPages.Count - 1,
1264                                                 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Right) / (TabPages.Count - begin_prev)), 
1265                                                 spacing, vertical);
1266                         }
1267
1268                         if (SelectedIndex != -1) {
1269                                 ExpandSelected (TabPages [SelectedIndex], 0, row_width - 1);
1270                         }
1271                 }
1272                 
1273                 private void SizeTab (TabPage page, int i, int row_width, ref int xpos, ref int ypos, 
1274                                                                 Size spacing, int prev_row, ref int begin_prev, bool widthOnly) 
1275                 {                               
1276                         int width, height = 0;
1277
1278                         if (SizeMode == TabSizeMode.Fixed) {
1279                                 width = item_size.Width;
1280                         } else {                        
1281                                 width = MeasureStringWidth (DeviceContext, page.Text, Font);
1282                                 width += (Padding.X * 2) + 2;
1283
1284                                 if (ImageList != null && page.ImageIndex >= 0) {
1285                                         width += ImageList.ImageSize.Width + ThemeEngine.Current.TabControlImagePadding.X;
1286
1287                                         int image_size = ImageList.ImageSize.Height + ThemeEngine.Current.TabControlImagePadding.Y;
1288                                         if (item_size.Height < image_size)
1289                                                 item_size.Height = image_size;
1290                                 }
1291
1292                                 if (width < MinimumTabWidth)
1293                                         width = MinimumTabWidth;
1294                         }
1295
1296                         // Use ItemSize property to recover the padding info as well.
1297                         height = ItemSize.Height - ThemeEngine.Current.TabControlSelectedDelta.Height; // full height only for selected tab
1298
1299                         if (i == SelectedIndex)
1300                                 width += ThemeEngine.Current.TabControlSelectedSpacing;
1301                         
1302                         if (widthOnly) {
1303                                 page.TabBounds = new Rectangle (xpos, 0, width, 0);
1304                                 page.Row = row_count;
1305                                 if (xpos + width > row_width && multiline) {
1306                                         xpos = 0;
1307                                         row_count++;
1308                                 } else if (xpos + width > row_width) {
1309                                         show_slider = true;     
1310                                 }
1311                                 if (i == selected_index && show_slider) {
1312                                         for (int j = i-1; j >= 0; j--) {
1313                                                 if (TabPages [j].TabBounds.Left < xpos + width - row_width) {
1314                                                         slider_pos = j+1;
1315                                                         break;
1316                                                 }
1317                                         }
1318                                 }
1319                         } else {
1320                                 if (page.Row != prev_row) {
1321                                         xpos = 0;
1322                                 }
1323
1324                                 switch (Alignment) {
1325                                         case TabAlignment.Top:
1326                                         case TabAlignment.Bottom:
1327                                                 page.TabBounds = new Rectangle (
1328                                                         xpos + CalcXPos (),
1329                                                         ypos + (height + spacing.Height) * (row_count - page.Row) + CalcYPos (),
1330                                                         width, 
1331                                                         height);
1332                                                 break;
1333                                         case TabAlignment.Left:
1334                                                 if (Appearance == TabAppearance.Normal) {
1335                                                         // tab rows are positioned right to left
1336                                                         page.TabBounds = new Rectangle (
1337                                                                 ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
1338                                                                 xpos,
1339                                                                 height, 
1340                                                                 width);
1341                                                 } else {
1342                                                         // tab rows are positioned left to right
1343                                                         page.TabBounds = new Rectangle (
1344                                                                 ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
1345                                                                 xpos,
1346                                                                 height, 
1347                                                                 width);
1348                                                 }
1349
1350                                                 break;
1351                                         case TabAlignment.Right:
1352                                                 if (Appearance == TabAppearance.Normal) {
1353                                                         // tab rows are positioned left to right
1354                                                         page.TabBounds = new Rectangle (
1355                                                                 ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
1356                                                                 xpos,
1357                                                                 height, 
1358                                                                 width);
1359                                                 } else {
1360                                                         // tab rows are positioned right to left
1361                                                         page.TabBounds = new Rectangle (
1362                                                                 ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
1363                                                                 xpos,
1364                                                                 height, 
1365                                                                 width);
1366                                                 }
1367
1368                                                 break;
1369                                 }
1370                         
1371                                 if (page.Row != prev_row) {
1372                                         if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
1373                                                 bool vertical = alignment == TabAlignment.Right || alignment == TabAlignment.Left;
1374                                                 int offset = vertical ? TabPages [i - 1].TabBounds.Bottom : TabPages [i - 1].TabBounds.Right;
1375                                                 FillRow (begin_prev, i - 1, ((row_width - offset) / (i - begin_prev)), spacing,
1376                                                         vertical);
1377                                         }
1378                                         begin_prev = i;
1379                                 }       
1380                         }
1381
1382                         xpos += width + spacing.Width + ThemeEngine.Current.TabControlColSpacing;
1383                 }
1384
1385                 private void FillRow (int start, int end, int amount, Size spacing, bool vertical) 
1386                 {
1387                         if (vertical)
1388                                 FillRowV (start, end, amount, spacing);
1389                         else
1390                                 FillRow (start, end, amount, spacing);
1391                 }
1392
1393                 private void FillRow (int start, int end, int amount, Size spacing)
1394                 {
1395                         int xpos = TabPages [start].TabBounds.Left;
1396                         for (int i = start; i <= end; i++) {
1397                                 TabPage page = TabPages [i];
1398                                 int left = xpos;
1399                                 int width = (i == end ? Width - left - 3 : page.TabBounds.Width + amount);
1400
1401                                 page.TabBounds = new Rectangle (left, page.TabBounds.Top,
1402                                                 width, page.TabBounds.Height);
1403                                 xpos = page.TabBounds.Right + 1 + spacing.Width;
1404                         }
1405                 }
1406
1407                 private void FillRowV (int start, int end, int amount, Size spacing)
1408                 {
1409                         int ypos = TabPages [start].TabBounds.Top;
1410                         for (int i = start; i <= end; i++) {
1411                                 TabPage page = TabPages [i];
1412                                 int top = ypos;
1413                                 int height = (i == end ? Height - top - 5 : page.TabBounds.Height + amount);
1414
1415                                 page.TabBounds = new Rectangle (page.TabBounds.Left, top,
1416                                                 page.TabBounds.Width, height);
1417                                 ypos = page.TabBounds.Bottom + 1;
1418                         }
1419                 }
1420
1421                 private void ExpandSelected (TabPage page, int left_edge, int right_edge)
1422                 {
1423                         if (Appearance != TabAppearance.Normal)
1424                                 return;
1425
1426                         Rectangle r = page.TabBounds;
1427                         r.Y -= ThemeEngine.Current.TabControlSelectedDelta.Y;
1428                         r.X -= ThemeEngine.Current.TabControlSelectedDelta.X;
1429
1430                         r.Width += ThemeEngine.Current.TabControlSelectedDelta.Width;
1431                         r.Height += ThemeEngine.Current.TabControlSelectedDelta.Height;
1432                         if (r.Left < left_edge)
1433                                 r.X = left_edge;
1434                         // Adjustment can't be used for right alignment, since it is
1435                         // the only one that has a different X origin than 0
1436                         if (r.Right > right_edge && SizeMode != TabSizeMode.Normal &&
1437                                         alignment != TabAlignment.Right)
1438                                 r.Width = right_edge - r.X;
1439                         page.TabBounds = r;
1440                 }
1441
1442                 private void Draw (Graphics dc, Rectangle clip)
1443                 {
1444                         ThemeEngine.Current.DrawTabControl (dc, clip, this);
1445                 }
1446
1447                 private TabPage GetTab (int index)
1448                 {
1449                         return Controls [index] as TabPage;
1450                 }
1451
1452                 private void SetTab (int index, TabPage value)
1453                 {
1454                         if (!tab_pages.Contains (value)) {
1455                                 this.Controls.Add (value);
1456                         }
1457                         this.Controls.RemoveAt (index);
1458                         this.Controls.SetChildIndex (value, index);
1459                         Redraw ();
1460                 }
1461                 private void InsertTab (int index, TabPage value)
1462                 {
1463                         if (!tab_pages.Contains (value)) {
1464                                 this.Controls.Add (value);
1465                         }
1466                         this.Controls.SetChildIndex (value, index);
1467                         Redraw ();
1468                 }
1469                 internal void Redraw ()
1470                 {
1471                         if (!IsHandleCreated)
1472                                 return;
1473
1474                         ResizeTabPages ();
1475                         Refresh ();
1476                 }
1477
1478                 private int MeasureStringWidth (Graphics graphics, string text, Font font) 
1479                 {
1480                         if (text == String.Empty)
1481                                 return 0;
1482                         StringFormat format = new StringFormat();
1483                         RectangleF rect = new RectangleF(0, 0, 1000, 1000);
1484                         CharacterRange[] ranges = { new CharacterRange(0, text.Length) };
1485                         Region[] regions = new Region[1];
1486
1487                         format.SetMeasurableCharacterRanges(ranges);
1488                         format.FormatFlags = StringFormatFlags.NoClip;
1489                         format.FormatFlags |= StringFormatFlags.NoWrap;
1490                         regions = graphics.MeasureCharacterRanges(text + "I", font, rect, format);
1491                         rect = regions[0].GetBounds(graphics);
1492
1493                         return (int)(rect.Width);
1494                 }
1495
1496                 void SetToolTip (string text)
1497                 {
1498                         if (!show_tool_tips)
1499                                 return;
1500
1501                         if (text == null || text.Length == 0) {
1502                                 CloseToolTip ();
1503                                 return;
1504                         }
1505
1506                         if (tooltip == null) {
1507                                 tooltip = new ToolTip ();
1508                                 tooltip_timer = new Timer ();
1509                                 tooltip_timer.Tick += new EventHandler (ToolTipTimerTick);
1510                         }
1511
1512                         CloseToolTip ();
1513
1514                         tooltip_state = ToolTip.TipState.Initial;
1515                         tooltip_timer.Interval = 500;
1516                         tooltip_timer.Start ();
1517                 }
1518
1519                 void CloseToolTip ()
1520                 {
1521                         if (tooltip == null)
1522                                 return;
1523
1524                         tooltip.Hide (this);
1525                         tooltip_timer.Stop ();
1526                         tooltip_state = ToolTip.TipState.Down;
1527                 }
1528
1529                 void ToolTipTimerTick (object o, EventArgs args)
1530                 {
1531                         switch (tooltip_state) {
1532                                 case ToolTip.TipState.Initial:
1533                                         tooltip_timer.Stop ();
1534                                         tooltip_timer.Interval = 5000;
1535                                         tooltip_timer.Start ();
1536                                         tooltip_state = ToolTip.TipState.Show;
1537                                         tooltip.Present (this, GetToolTipText (EnteredTabPage));
1538                                         break;
1539                                 case ToolTip.TipState.Show:
1540                                         CloseToolTip ();
1541                                         break;
1542                         }
1543                 }
1544
1545                 void OnMouseMove (object sender, MouseEventArgs e)
1546                 {
1547                         if (!mouse_down_on_a_tab_page && ShowSlider) {
1548                                 if (LeftSliderState == PushButtonState.Pressed ||
1549                                         RightSliderState == PushButtonState.Pressed)
1550                                         return;
1551                                 if (LeftScrollButtonArea.Contains (e.Location)) {
1552                                         LeftSliderState = PushButtonState.Hot;
1553                                         RightSliderState = PushButtonState.Normal;
1554                                         EnteredTabPage = null;
1555                                         return;
1556                                 }
1557                                 if (RightScrollButtonArea.Contains (e.Location)) {
1558                                         RightSliderState = PushButtonState.Hot;
1559                                         LeftSliderState = PushButtonState.Normal;
1560                                         EnteredTabPage = null;
1561                                         return;
1562                                 }
1563                                 LeftSliderState = PushButtonState.Normal;
1564                                 RightSliderState = PushButtonState.Normal;
1565                         }
1566                         if (EnteredTabPage != null && EnteredTabPage.TabBounds.Contains (e.Location))
1567                                 return;
1568                         for (int index = 0; index < TabCount; index++) {
1569                                 TabPage tab_page = TabPages[index];
1570                                 if (tab_page.TabBounds.Contains (e.Location)) {
1571                                         EnteredTabPage = tab_page;
1572                                         return;
1573                                 }
1574                         }
1575                         EnteredTabPage = null;
1576                 }
1577
1578                 void OnMouseLeave (object sender, EventArgs e)
1579                 {
1580                         if (ShowSlider) {
1581                                 LeftSliderState = PushButtonState.Normal;
1582                                 RightSliderState = PushButtonState.Normal;
1583                         }
1584                         EnteredTabPage = null;
1585                 }
1586                 #endregion      // Internal & Private Methods
1587
1588                 #region Events
1589                 [Browsable(false)]
1590                 [EditorBrowsable(EditorBrowsableState.Never)]
1591                 public new event EventHandler BackColorChanged {
1592                         add { base.BackColorChanged += value; }
1593                         remove { base.BackColorChanged -= value; }
1594                 }
1595
1596                 [Browsable(false)]
1597                 [EditorBrowsable(EditorBrowsableState.Never)]
1598                 public new event EventHandler BackgroundImageChanged {
1599                         add { base.BackgroundImageChanged += value; }
1600                         remove { base.BackgroundImageChanged -= value; }
1601                 }
1602
1603                 [Browsable (false)]
1604                 [EditorBrowsable (EditorBrowsableState.Never)]
1605                 public new event EventHandler BackgroundImageLayoutChanged
1606                 {
1607                         add { base.BackgroundImageLayoutChanged += value; }
1608                         remove { base.BackgroundImageLayoutChanged -= value; }
1609                 }
1610
1611                 [Browsable(false)]
1612                 [EditorBrowsable(EditorBrowsableState.Never)]
1613                 public new event EventHandler ForeColorChanged {
1614                         add { base.ForeColorChanged += value; }
1615                         remove { base.ForeColorChanged -= value; }
1616                 }
1617
1618                 [Browsable(false)]
1619                 [EditorBrowsable(EditorBrowsableState.Never)]
1620                 public new event PaintEventHandler Paint {
1621                         add { base.Paint += value; }
1622                         remove { base.Paint -= value; }
1623                 }
1624
1625                 [Browsable(false)]
1626                 [EditorBrowsable(EditorBrowsableState.Never)]
1627                 public new event EventHandler TextChanged {
1628                         add { base.TextChanged += value; }
1629                         remove { base.TextChanged -= value; }
1630                 }
1631
1632                 static object DrawItemEvent = new object ();
1633                 static object SelectedIndexChangedEvent = new object ();
1634
1635                 public event DrawItemEventHandler DrawItem {
1636                         add { Events.AddHandler (DrawItemEvent, value); }
1637                         remove { Events.RemoveHandler (DrawItemEvent, value); }
1638                 }
1639
1640                 public event EventHandler SelectedIndexChanged {
1641                         add { Events.AddHandler (SelectedIndexChangedEvent, value); }
1642                         remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
1643                 }
1644                 
1645                 static object SelectedEvent = new object ();
1646                 
1647                 public event TabControlEventHandler Selected {
1648                         add { Events.AddHandler (SelectedEvent, value); }
1649                         remove { Events.RemoveHandler (SelectedEvent, value); }
1650                 }
1651
1652                 static object DeselectedEvent = new object ();
1653
1654                 public event TabControlEventHandler Deselected
1655                 {
1656                         add { Events.AddHandler (DeselectedEvent, value); }
1657                         remove { Events.RemoveHandler (DeselectedEvent, value); }
1658                 }
1659
1660                 static object SelectingEvent = new object ();
1661
1662                 public event TabControlCancelEventHandler Selecting
1663                 {
1664                         add { Events.AddHandler (SelectingEvent, value); }
1665                         remove { Events.RemoveHandler (SelectingEvent, value); }
1666                 }
1667
1668                 static object DeselectingEvent = new object ();
1669
1670                 public event TabControlCancelEventHandler Deselecting
1671                 {
1672                         add { Events.AddHandler (DeselectingEvent, value); }
1673                         remove { Events.RemoveHandler (DeselectingEvent, value); }
1674                 }
1675
1676                 static object RightToLeftLayoutChangedEvent = new object ();
1677                 public event EventHandler RightToLeftLayoutChanged
1678                 {
1679                         add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
1680                         remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
1681                 }
1682                 #endregion      // Events
1683
1684
1685                 #region Class TaControl.ControlCollection
1686                 [ComVisible (false)]
1687                 public new class ControlCollection : System.Windows.Forms.Control.ControlCollection {
1688
1689                         private TabControl owner;
1690
1691                         public ControlCollection (TabControl owner) : base (owner)
1692                         {
1693                                 this.owner = owner;
1694                         }
1695
1696                         public override void Add (Control value)
1697                         {
1698                                 TabPage page = value as TabPage;
1699                                 if (page == null)
1700                                         throw new ArgumentException ("Cannot add " +
1701                                                 value.GetType ().Name + " to TabControl. " +
1702                                                 "Only TabPages can be directly added to TabControls.");
1703
1704                                 page.SetVisible (false);
1705                                 base.Add (value);
1706                                 if (owner.TabCount == 1 && owner.selected_index < 0)
1707                                         owner.SelectedIndex = 0;
1708                                 owner.Redraw ();
1709                         }
1710
1711                         public override void Remove (Control value)
1712                         {
1713                                 bool change_index = false;
1714                                 
1715                                 TabPage page = value as TabPage;
1716                                 if (page != null && owner.Controls.Contains (page)) {
1717                                         int index = owner.IndexForTabPage (page);
1718                                         if (index < owner.SelectedIndex || owner.SelectedIndex == Count - 1)
1719                                                 change_index = true;
1720                                 }
1721                                 
1722                                 base.Remove (value);
1723                                 
1724                                 // We don't want to raise SelectedIndexChanged until after we
1725                                 // have removed from the collection, so TabCount will be
1726                                 // correct for the user.
1727                                 if (change_index && Count > 0) {
1728                                         // Clear the selected index internally, to avoid trying to access the previous
1729                                         // selected tab when setting the new one - this is what .net seems to do
1730                                         int prev_selected_index = owner.SelectedIndex;
1731                                         owner.selected_index = -1;
1732
1733                                         owner.SelectedIndex = --prev_selected_index;
1734                                         owner.Invalidate ();
1735                                 } else if (change_index) {
1736                                         owner.selected_index = -1;
1737                                         owner.OnSelectedIndexChanged (EventArgs.Empty);
1738                                         owner.Invalidate ();
1739                                 } else
1740                                         owner.Redraw ();
1741                         }
1742                 }
1743                 #endregion      // Class TabControl.ControlCollection
1744
1745                 #region Class TabPage.TabPageCollection
1746                 public class TabPageCollection  : IList, ICollection, IEnumerable {
1747
1748                         private TabControl owner;
1749
1750                         public TabPageCollection (TabControl owner)
1751                         {
1752                                 if (owner == null)
1753                                         throw new ArgumentNullException ("Value cannot be null.");
1754                                 this.owner = owner;
1755                         }
1756
1757                         [Browsable(false)]
1758                         public int Count {
1759                                 get { return owner.Controls.Count; }
1760                         }
1761
1762                         public bool IsReadOnly {
1763                                 get { return false; }
1764                         }
1765
1766                         public virtual TabPage this [int index] {
1767                                 get {
1768                                         return owner.GetTab (index);
1769                                 }
1770                                 set {
1771                                         owner.SetTab (index, value);
1772                                 }
1773                         }
1774                         public virtual TabPage this [string key] {
1775                                 get {
1776                                         if (string.IsNullOrEmpty (key))
1777                                                 return null;
1778
1779                                         int index = this.IndexOfKey (key);
1780                                         if (index < 0 || index >= this.Count)
1781                                                 return null;
1782                                         
1783                                         return this[index];
1784                                 }
1785                         }
1786
1787                         internal int this[TabPage tabPage] {
1788                                 get {
1789                                         if (tabPage == null)
1790                                                 return -1;
1791
1792                                         for (int i = 0; i < this.Count; i++)
1793                                                 if (this[i].Equals (tabPage))
1794                                                         return i;
1795
1796                                         return -1;
1797                                 }
1798                         }
1799
1800                         bool ICollection.IsSynchronized {
1801                                 get { return false; }
1802                         }
1803
1804                         object ICollection.SyncRoot {
1805                                 get { return this; }
1806                         }
1807
1808                         bool IList.IsFixedSize {
1809                                 get { return false; }
1810                         }
1811
1812                         object IList.this [int index] {
1813                                 get {
1814                                         return owner.GetTab (index);
1815                                 }
1816                                 set {
1817                                         owner.SetTab (index, (TabPage) value);
1818                                 }
1819                         }
1820
1821                         public void Add (TabPage value)
1822                         {
1823                                 if (value == null)
1824                                         throw new ArgumentNullException ("Value cannot be null.");
1825                                 owner.Controls.Add (value);
1826                         }
1827
1828                         public void Add (string text)
1829                         {
1830                                 TabPage page = new TabPage (text);
1831                                 this.Add (page);
1832                         }
1833
1834                         public void Add (string key, string text)
1835                         {
1836                                 TabPage page = new TabPage (text);
1837                                 page.Name = key;
1838                                 this.Add (page);
1839                         }
1840
1841                         public void Add (string key, string text, int imageIndex)
1842                         {
1843                                 TabPage page = new TabPage (text);
1844                                 page.Name = key;
1845                                 page.ImageIndex = imageIndex;
1846                                 this.Add (page);
1847                         }
1848
1849                         // .Net sets the ImageKey, but does not show the image when this is used
1850                         public void Add (string key, string text, string imageKey)
1851                         {
1852                                 TabPage page = new TabPage (text);
1853                                 page.Name = key;
1854                                 page.ImageKey = imageKey;
1855                                 this.Add (page);
1856                         }
1857
1858                         public void AddRange (TabPage [] pages)
1859                         {
1860                                 if (pages == null)
1861                                         throw new ArgumentNullException ("Value cannot be null.");
1862                                 owner.Controls.AddRange (pages);
1863                         }
1864
1865                         public virtual void Clear ()
1866                         {
1867                                 owner.Controls.Clear ();
1868                                 owner.Invalidate ();
1869                         }
1870
1871                         public bool Contains (TabPage page)
1872                         {
1873                                 if (page == null)
1874                                         throw new ArgumentNullException ("Value cannot be null.");
1875                                 return owner.Controls.Contains (page);
1876                         }
1877
1878                         public virtual bool ContainsKey (string key)
1879                         {
1880                                 int index = this.IndexOfKey (key);
1881                                 return (index >= 0 && index < this.Count);
1882                         }
1883
1884                         public IEnumerator GetEnumerator ()
1885                         {
1886                                 return owner.Controls.GetEnumerator ();
1887                         }
1888
1889                         public int IndexOf (TabPage page)
1890                         {
1891                                 return owner.Controls.IndexOf (page);
1892                         }
1893
1894                         public virtual int IndexOfKey(string key)
1895                         {
1896                                 if (string.IsNullOrEmpty (key))
1897                                         return -1;
1898
1899                                 for (int i = 0; i < this.Count; i++) {
1900                                         if (string.Compare (this[i].Name, key, true, 
1901                                                 System.Globalization.CultureInfo.InvariantCulture) == 0) {
1902                                                 return i;
1903                                         }
1904                                 }
1905
1906                                 return -1;
1907                         }
1908
1909                         public void Remove (TabPage value)
1910                         {
1911                                 owner.Controls.Remove (value);
1912                                 owner.Invalidate ();
1913                         }
1914
1915                         public void RemoveAt (int index)
1916                         {
1917                                 owner.Controls.RemoveAt (index);
1918                                 owner.Invalidate ();
1919                         }
1920
1921                         public virtual void RemoveByKey (string key)
1922                         {
1923                                 int index = this.IndexOfKey (key);
1924                                 if (index >= 0 && index < this.Count)
1925                                         this.RemoveAt (index);
1926                         }
1927
1928                         void ICollection.CopyTo (Array dest, int index)
1929                         {
1930                                 owner.Controls.CopyTo (dest, index);
1931                         }
1932
1933                         int IList.Add (object value)
1934                         {
1935                                 TabPage page = value as TabPage;
1936                                 if (value == null)
1937                                         throw new ArgumentException ("value");
1938                                 owner.Controls.Add (page);
1939                                 return owner.Controls.IndexOf (page);
1940                         }
1941
1942                         bool IList.Contains (object page)
1943                         {
1944                                 TabPage tabPage = page as TabPage;
1945                                 if (tabPage == null)
1946                                         return false;
1947                                 return Contains (tabPage);
1948                         }
1949
1950                         int IList.IndexOf (object page)
1951                         {
1952                                 TabPage tabPage = page as TabPage;
1953                                 if (tabPage == null)
1954                                         return -1;
1955                                 return IndexOf (tabPage);
1956                         }
1957
1958                         void IList.Insert (int index, object tabPage)
1959                         {
1960                                 throw new NotSupportedException ();
1961                         }
1962
1963                         public void Insert (int index, string text)
1964                         {
1965                                 owner.InsertTab (index, new TabPage (text));
1966                         }
1967                         
1968                         public void Insert (int index, TabPage tabPage)
1969                         {
1970                                 owner.InsertTab (index, tabPage);
1971                         }
1972
1973                         public void Insert (int index, string key, string text)
1974                         {
1975                                 TabPage page = new TabPage(text);
1976                                 page.Name = key;
1977                                 owner.InsertTab (index, page);
1978                         }
1979
1980                         public void Insert (int index, string key, string text, int imageIndex) 
1981                         {
1982                                 TabPage page = new TabPage(text);
1983                                 page.Name = key;
1984                                 owner.InsertTab (index, page);
1985                                 page.ImageIndex = imageIndex;
1986                         }
1987
1988                         public void Insert (int index, string key, string text, string imageKey) 
1989                         {
1990                                 TabPage page = new TabPage(text);
1991                                 page.Name = key;
1992                                 owner.InsertTab (index, page);
1993                                 page.ImageKey = imageKey;
1994                         }
1995                         void IList.Remove (object value)
1996                         {
1997                                 TabPage page = value as TabPage;
1998                                 if (page == null)
1999                                         return;
2000                                 Remove ((TabPage) value);
2001                         }
2002                 }
2003                 #endregion      // Class TabPage.TabPageCollection
2004         }
2005 }
2006
2007