* TabControl.cs: Show the tooltip depending on the value
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / SplitContainer.cs
1 //
2 // SplitContainer.cs
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Copyright (c) 2006 Jonathan Pobst
24 // Copyright (c) 2007 Ivan N. Zlatev
25 //
26 // Authors:
27 //      Jonathan Pobst (monkey@jpobst.com)
28 //      Ivan N. Zlatev (contact@i-nz.net)
29 //
30
31 #if NET_2_0
32 using System;
33 using System.Runtime.InteropServices;
34 using System.ComponentModel;
35 using System.Drawing;
36 using System.Drawing.Drawing2D;
37
38 namespace System.Windows.Forms
39 {
40         [ComVisibleAttribute (true)]
41         [ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
42         [DefaultEvent ("SplitterMoved")]
43         [Docking (DockingBehavior.AutoDock)]
44         [Designer ("System.Windows.Forms.Design.SplitContainerDesigner, " + Consts.AssemblySystem_Design)]
45         public class SplitContainer : ContainerControl
46         {
47                 #region Local Variables
48                 private FixedPanel fixed_panel;
49                 private Orientation orientation;
50
51                 private int splitter_increment;
52                 private Rectangle splitter_rectangle;
53                 private Rectangle splitter_rectangle_moving;
54                 private Rectangle splitter_rectangle_before_move;
55                 private bool splitter_fixed;
56                 private bool splitter_dragging;
57                 private int splitter_prev_move;
58                 private Cursor restore_cursor;
59                 private double fixed_none_ratio;
60
61                 private SplitterPanel panel1;
62                 private bool panel1_collapsed;
63                 private int panel1_min_size;
64
65                 private SplitterPanel panel2;
66                 private bool panel2_collapsed;
67                 private int panel2_min_size;
68                 #endregion
69
70                 #region Public Events
71                 static object SplitterMovedEvent = new object ();
72                 static object SplitterMovingEvent = new object ();
73
74                 [Browsable (false)]
75                 [EditorBrowsable (EditorBrowsableState.Never)]
76                 public new event EventHandler AutoSizeChanged {
77                         add { base.AutoSizeChanged += value; }
78                         remove { base.AutoSizeChanged -= value; }
79                 }
80
81                 [Browsable (true)]
82                 [EditorBrowsable (EditorBrowsableState.Always)]
83                 public new event EventHandler BackgroundImageChanged {
84                         add { base.BackgroundImageChanged += value; }
85                         remove { base.BackgroundImageChanged -= value; }
86                 }
87
88                 [Browsable (false)]
89                 [EditorBrowsable (EditorBrowsableState.Never)]
90                 public new event EventHandler BackgroundImageLayoutChanged {
91                         add { base.BackgroundImageLayoutChanged += value; }
92                         remove { base.BackgroundImageLayoutChanged -= value; }
93                 }
94
95                 [Browsable (false)]
96                 [EditorBrowsable (EditorBrowsableState.Never)]
97                 public new event ControlEventHandler ControlAdded {
98                         add { base.ControlAdded += value; }
99                         remove { base.ControlAdded -= value; }
100                 }
101
102                 [Browsable (false)]
103                 [EditorBrowsable (EditorBrowsableState.Never)]
104                 public new event ControlEventHandler ControlRemoved {
105                         add { base.ControlRemoved += value; }
106                         remove { base.ControlRemoved -= value; }
107                 }
108
109                 [Browsable (false)]
110                 [EditorBrowsable (EditorBrowsableState.Never)]
111                 public new event EventHandler PaddingChanged {
112                         add { base.PaddingChanged += value; }
113                         remove { base.PaddingChanged -= value; }
114                 }
115                 
116                 public event SplitterEventHandler SplitterMoved {
117                         add { Events.AddHandler (SplitterMovedEvent, value); }
118                         remove { Events.RemoveHandler (SplitterMovedEvent, value); }
119                 }
120
121                 public event SplitterCancelEventHandler SplitterMoving {
122                         add { Events.AddHandler (SplitterMovingEvent, value); }
123                         remove { Events.RemoveHandler (SplitterMovingEvent, value); }
124                 }
125
126                 [Browsable (false)]
127                 [EditorBrowsable (EditorBrowsableState.Never)]
128                 public new event EventHandler TextChanged {
129                         add { base.TextChanged += value; }
130                         remove { base.TextChanged -= value; }
131                 }
132                 #endregion
133
134                 #region UIA Framework Events
135 #if NET_2_0
136                 static object UIACanResizeChangedEvent = new object ();
137
138                 internal event EventHandler UIACanResizeChanged {
139                         add { Events.AddHandler (UIACanResizeChangedEvent, value); }
140                         remove { Events.RemoveHandler (UIACanResizeChangedEvent, value); }
141                 }
142
143                 internal void OnUIACanResizeChanged (EventArgs e)
144                 {
145                         EventHandler eh = (EventHandler) Events [UIACanResizeChangedEvent];
146                         if (eh != null)
147                                 eh (this, e);
148                 }
149 #endif
150                 #endregion
151
152                 #region Public Constructors
153                 public SplitContainer ()
154                 {
155                         SetStyle (ControlStyles.SupportsTransparentBackColor, true);
156                         SetStyle (ControlStyles.OptimizedDoubleBuffer, true);
157                         
158                         fixed_panel = FixedPanel.None;
159                         orientation = Orientation.Vertical;
160
161                         splitter_rectangle = new Rectangle (50, 0, 4, this.Height);
162                         splitter_increment = 1;
163                         splitter_prev_move = -1;
164                         restore_cursor = null;
165
166                         splitter_fixed = false;
167                         panel1_collapsed = false;
168                         panel2_collapsed = false;
169                         panel1_min_size = 25;
170                         panel2_min_size = 25;
171
172                         panel1 = new SplitterPanel (this);
173                         panel2 = new SplitterPanel (this);
174                         panel1.Size = new Size (50, 50);
175                         UpdateSplitter ();
176
177                         this.Controls.Add (panel2);
178                         this.Controls.Add (panel1);
179                 }
180                 #endregion
181
182                 #region Public Properties
183                 [Browsable (false)]
184                 [EditorBrowsable (EditorBrowsableState.Never)]
185                 [Localizable (true)]
186                 [DefaultValue (false)]
187                 public override bool AutoScroll {
188                         get { return base.AutoScroll; }
189                         set { base.AutoScroll = value; }
190                 }
191
192                 [Browsable (false)]
193                 [EditorBrowsable (EditorBrowsableState.Never)]
194                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
195                 new public Size AutoScrollMargin {
196                         get { return base.AutoScrollMargin; }
197                         set { base.AutoScrollMargin = value; }
198                 }
199
200                 [Browsable (false)]
201                 [EditorBrowsable (EditorBrowsableState.Never)]
202                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
203                 new public Size AutoScrollMinSize {
204                         get { return base.AutoScrollMinSize; }
205                         set { base.AutoScrollMinSize = value; }
206                 }
207
208                 [Browsable (false)]
209                 [DefaultValue ("{X=0,Y=0}")]
210                 [EditorBrowsable (EditorBrowsableState.Never)]
211                 public override Point AutoScrollOffset {
212                         get { return base.AutoScrollOffset; }
213                         set { base.AutoScrollOffset = value; }
214                 }
215
216                 [Browsable (false)]
217                 [EditorBrowsable (EditorBrowsableState.Never)]
218                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
219                 new public Point AutoScrollPosition {
220                         get { return base.AutoScrollPosition; }
221                         set { base.AutoScrollPosition = value; }
222                 }
223
224                 [Browsable (false)]
225                 [EditorBrowsable (EditorBrowsableState.Never)]
226                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
227                 public override bool AutoSize {
228                         get { return base.AutoSize; }
229                         set { base.AutoSize = value; }
230                 }
231
232                 [Browsable (true)]
233                 [EditorBrowsable (EditorBrowsableState.Always)]
234                 public override Image BackgroundImage {
235                         get { return base.BackgroundImage; }
236                         set { base.BackgroundImage = value; }
237                 }
238
239                 [Browsable (false)]
240                 [EditorBrowsable (EditorBrowsableState.Never)]
241                 public override ImageLayout BackgroundImageLayout {
242                         get { return base.BackgroundImageLayout; }
243                         set { base.BackgroundImageLayout = value; }
244                 }
245
246                 [Browsable (false)]
247                 public override BindingContext BindingContext {
248                         get { return base.BindingContext; }
249                         set { base.BindingContext = value; }
250                 }
251
252                 // MSDN says default is Fixed3D, creating a new SplitContainer says otherwise.
253                 [DefaultValue (BorderStyle.None)]
254                 [DispId (-504)]
255                 public BorderStyle BorderStyle {
256                         get { return panel1.BorderStyle; }
257                         set {
258                                 if (!Enum.IsDefined (typeof (BorderStyle), value))
259                                         throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for BorderStyle", value));
260                                         
261                                 panel1.BorderStyle = value;
262                                 panel2.BorderStyle = value;
263                         }
264                 }
265
266                 [EditorBrowsable (EditorBrowsableState.Never)]
267                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
268                 new public ControlCollection Controls { get { return base.Controls; } }
269
270                 new public DockStyle Dock {
271                         get { return base.Dock; }
272                         set { base.Dock = value; }
273                 }
274
275                 [DefaultValue (FixedPanel.None)]
276                 public FixedPanel FixedPanel {
277                         get { return this.fixed_panel; }
278                         set {
279                                 if (!Enum.IsDefined (typeof (FixedPanel), value))
280                                         throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for FixedPanel", value));
281
282                                 this.fixed_panel = value;
283                         }
284                 }
285
286                 [Localizable (true)]
287                 [DefaultValue (false)]
288                 public bool IsSplitterFixed {
289                         get { return splitter_fixed; }
290                         set { splitter_fixed = value; }
291                 }
292
293                 [Localizable (true)]
294                 [DefaultValue (Orientation.Vertical)]
295                 public Orientation Orientation {
296                         get { return this.orientation; }
297                         set {
298                                 if (!Enum.IsDefined (typeof (Orientation), value))
299                                         throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for Orientation", value));
300
301                                 if (this.orientation != value) {
302                                         if (value == Orientation.Vertical) {
303                                                 splitter_rectangle.Width = splitter_rectangle.Height;
304                                                 splitter_rectangle.X = splitter_rectangle.Y;
305                                         } else {
306                                                 splitter_rectangle.Height = splitter_rectangle.Width;
307                                                 splitter_rectangle.Y = splitter_rectangle.X;
308                                         }
309
310                                         this.orientation = value;
311                                         this.UpdateSplitter ();
312                                 }
313                         }
314                 }
315
316                 [Browsable (false)]
317                 [EditorBrowsable (EditorBrowsableState.Never)]
318                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
319                 new public Padding Padding {
320                         get { return base.Padding; }
321                         set { base.Padding = value; }
322                 }
323
324                 [Localizable (false)]
325                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
326                 public SplitterPanel Panel1 { get { return this.panel1; } }
327
328                 [DefaultValue (false)]
329                 public bool Panel1Collapsed {
330                         get { return this.panel1_collapsed; }
331                         set {
332                                 if (panel1_collapsed != value) {
333                                         this.panel1_collapsed = value;
334                                         panel1.Visible = !value;
335
336 #if NET_2_0
337                                         // UIA Framework Event: CanResize Changed
338                                         OnUIACanResizeChanged (EventArgs.Empty);
339 #endif
340
341                                         PerformLayout ();
342                                 }
343                         }
344                 }
345
346                 [Localizable (true)]
347                 [DefaultValue (25)]
348                 [RefreshProperties (RefreshProperties.All)]
349                 public int Panel1MinSize {
350                         get { return this.panel1_min_size; }
351                         set { 
352                                 this.panel1_min_size = value; 
353                         }
354                 }
355
356                 [Localizable (false)]
357                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
358                 public SplitterPanel Panel2 { get { return this.panel2; } }
359
360                 [DefaultValue (false)]
361                 public bool Panel2Collapsed {
362                         get { return this.panel2_collapsed; }
363                         set {
364                                 if (panel2_collapsed != value) {
365                                         this.panel2_collapsed = value;
366                                         panel2.Visible = !value;
367
368 #if NET_2_0
369                                         // UIA Framework Event: CanResize Changed
370                                         OnUIACanResizeChanged (EventArgs.Empty);
371 #endif
372
373                                         PerformLayout ();
374                                 }
375                         }
376                 }
377
378                 [Localizable (true)]
379                 [DefaultValue (25)]
380                 [RefreshProperties (RefreshProperties.All)]
381                 public int Panel2MinSize {
382                         get { return this.panel2_min_size; }
383                         set { this.panel2_min_size = value; }
384                 }
385
386                 // MSDN says the default is 40, MS's implementation defaults to 50.
387                 [Localizable (true)]
388                 [DefaultValue (50)]
389                 [SettingsBindable (true)]
390                 public int SplitterDistance {
391                         get { 
392                                 if (orientation == Orientation.Vertical)
393                                         return this.splitter_rectangle.X;
394                                 else
395                                         return this.splitter_rectangle.Y;
396                         }
397                         set {
398                                 if (value < 0)
399                                         throw new ArgumentOutOfRangeException ();
400
401                                 if (value < panel1_min_size)
402                                         value = panel1_min_size;
403
404                                 bool updated = true;
405                                 if (orientation == Orientation.Vertical) {
406                                         if (this.Width - (this.SplitterWidth + value) < panel2_min_size)
407                                                 value = this.Width - (this.SplitterWidth + panel2_min_size);
408                                         if (splitter_rectangle.X != value) {
409                                                 splitter_rectangle.X = value;
410                                                 updated = true;
411                                         }
412                                 } else {
413                                         if (this.Height - (this.SplitterWidth + value) < panel2_min_size)
414                                                 value = this.Height - (this.SplitterWidth + panel2_min_size);
415                                         if (splitter_rectangle.Y != value) {
416                                                 splitter_rectangle.Y = value;
417                                                 updated = true;
418                                         }
419                                 }
420                                 if (updated) {
421                                         UpdateSplitter ();
422                                         OnSplitterMoved (new SplitterEventArgs (Left, Top, splitter_rectangle.X, splitter_rectangle.Y));
423                                 }
424                         }
425                 }
426
427                 [Localizable (true)]
428                 [DefaultValue (1)]
429                 [MonoTODO ("Stub, never called")]
430                 public int SplitterIncrement {
431                         get { return this.splitter_increment; }
432                         set { this.splitter_increment = value; }
433                 }
434
435                 [Browsable (false)]
436                 public Rectangle SplitterRectangle { get { return splitter_rectangle; } }
437
438                 [Localizable (true)]
439                 [DefaultValue (4)]
440                 public int SplitterWidth {
441                         get {
442                                 if (orientation == Orientation.Vertical)
443                                         return this.splitter_rectangle.Width;
444                                 else
445                                         return this.splitter_rectangle.Height;
446                         }
447                         set {
448                                 if (value < 1)
449                                         throw new ArgumentOutOfRangeException ();
450
451                                 if (orientation == Orientation.Vertical)
452                                         this.splitter_rectangle.Width = value;
453                                 else
454                                         this.splitter_rectangle.Height = value;
455                                 UpdateSplitter ();
456                         }
457                 }
458
459                 [DispId (-516)]
460                 [DefaultValue (true)]
461                 [MonoTODO ("Stub, never called")]
462                 new public bool TabStop {
463                         get { return false; }
464                         set { }
465                 }
466
467                 [Browsable (false)]
468                 [EditorBrowsable (EditorBrowsableState.Never)]
469                 [Bindable (false)]
470                 public override string Text {
471                         get { return base.Text; }
472                         set { base.Text = value; }
473                 }
474                 #endregion
475
476                 #region Protected Properties
477                 protected override Size DefaultSize { get { return new Size (150, 100); } }
478                 #endregion
479
480                 #region Public Methods
481                 public void OnSplitterMoved (SplitterEventArgs e)
482                 {
483                         SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovedEvent]);
484                         if (eh != null)
485                                 eh (this, e);
486                 }
487
488                 public void OnSplitterMoving (SplitterCancelEventArgs e)
489                 {
490                         SplitterCancelEventHandler eh = (SplitterCancelEventHandler)(Events [SplitterMovingEvent]);
491                         if (eh != null)
492                                 eh (this, e);
493                 }
494                 #endregion
495
496                 #region Protected Methods
497                 [EditorBrowsable (EditorBrowsableState.Advanced)]
498                 protected override ControlCollection CreateControlsInstance ()
499                 {
500                         return new SplitContainerTypedControlCollection (this);
501                 }
502
503                 protected override void OnGotFocus (EventArgs e)
504                 {
505                         base.OnGotFocus (e);
506                 }
507
508                 protected override void OnKeyDown (KeyEventArgs e)
509                 {
510                         base.OnKeyDown (e);
511                 }
512
513                 protected override void OnKeyUp (KeyEventArgs e)
514                 {
515                         base.OnKeyUp (e);
516                 }
517
518                 protected override void OnLayout (LayoutEventArgs e)
519                 {
520                         UpdateLayout ();
521                         base.OnLayout (e);
522                 }
523
524                 protected override void OnLostFocus (EventArgs e)
525                 {
526                         base.OnLostFocus (e);
527                 }
528
529                 protected override void OnMouseCaptureChanged (EventArgs e)
530                 {
531                         base.OnMouseCaptureChanged (e);
532                 }
533                 
534                 protected override void OnMouseDown (MouseEventArgs e)
535                 {
536                         base.OnMouseDown (e);
537                         if (!splitter_fixed && SplitterHitTest (e.Location)) {
538                                 splitter_dragging = true;
539                                 SplitterBeginMove (e.Location);
540                         }
541                 }
542
543                 protected override void OnMouseLeave (EventArgs e)
544                 {
545                         base.OnMouseLeave (e);
546                         SplitterRestoreCursor ();
547                 }
548
549                 [EditorBrowsable (EditorBrowsableState.Advanced)]
550                 protected override void OnMouseMove (MouseEventArgs e)
551                 {
552                         base.OnMouseMove (e);
553
554                         if (splitter_dragging)
555                                 SplitterMove (e.Location);
556
557                         if (!splitter_fixed && SplitterHitTest (e.Location))
558                                 SplitterSetCursor (orientation);
559                         
560                 }
561
562                 protected override void OnMouseUp (MouseEventArgs e)
563                 {
564                         base.OnMouseUp (e);
565                         if (splitter_dragging) {
566                                 SplitterEndMove (e.Location, false);
567                                 SplitterRestoreCursor ();
568                                 splitter_dragging = false;
569                         }
570                 }
571                 
572                 protected override void OnPaint (PaintEventArgs e)
573                 {
574                         base.OnPaint (e);
575                 }
576
577                 [EditorBrowsable (EditorBrowsableState.Advanced)]
578                 protected override void OnRightToLeftChanged (EventArgs e)
579                 {
580                         base.OnRightToLeftChanged (e);
581                 }
582
583                 protected override bool ProcessDialogKey (Keys keyData)
584                 {
585                         return base.ProcessDialogKey (keyData);
586                 }
587
588                 protected override bool ProcessTabKey (bool forward)
589                 {
590                         return base.ProcessTabKey (forward);
591                 }
592
593                 [EditorBrowsable (EditorBrowsableState.Advanced)]
594                 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
595                 {
596                         base.ScaleControl (factor, specified);
597                 }
598                 
599                 protected override void Select (bool directed, bool forward)
600                 {
601                         base.Select (directed, forward);
602                 }
603
604                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
605                 {
606                         base.SetBoundsCore (x, y, width, height, specified);
607                 }
608
609                 protected override void WndProc (ref Message msg)
610                 {                       
611                         base.WndProc (ref msg);
612                 }
613                 #endregion
614                 
615                 #region Private Methods
616
617                 private bool SplitterHitTest (Point location)
618                 {
619                         if (location.X >= splitter_rectangle.X &&
620                                 location.X <= splitter_rectangle.X + splitter_rectangle.Width &&
621                                 location.Y >= splitter_rectangle.Y &&
622                                 location.Y <= splitter_rectangle.Y + splitter_rectangle.Height) {
623                                 return true;
624                         }
625                         return false;                              
626                 }
627
628                 private void SplitterBeginMove (Point location)
629                 {
630                         splitter_prev_move = orientation == Orientation.Vertical ? location.X : location.Y;
631                         splitter_rectangle_moving = splitter_rectangle;
632                         splitter_rectangle_before_move = splitter_rectangle;
633                 }
634
635                 private void SplitterMove (Point location)
636                 {
637                         int currentMove = orientation == Orientation.Vertical ? location.X : location.Y;
638                         int delta = currentMove - splitter_prev_move;
639                         Rectangle prev_location = splitter_rectangle_moving;
640                         bool moved = false;
641
642                         if (orientation == Orientation.Vertical) {
643                                 int min = panel1_min_size;
644                                 int max = panel2.Location.X + (panel2.Width - this.panel2_min_size) - splitter_rectangle_moving.Width;
645
646                                 if (splitter_rectangle_moving.X + delta > min && splitter_rectangle_moving.X + delta < max) {
647                                         splitter_rectangle_moving.X += delta;
648                                         moved = true;
649                                 } else {
650                                         // Ensure that the splitter is set to minimum or maximum position, 
651                                         // even if the mouse "skips".
652                                         //
653                                         if (splitter_rectangle_moving.X + delta <= min && splitter_rectangle_moving.X != min) {
654                                                 splitter_rectangle_moving.X = min;
655                                                 moved = true;
656                                         } else if (splitter_rectangle_moving.X + delta >= max && splitter_rectangle_moving.X != max) {
657                                                 splitter_rectangle_moving.X = max;
658                                                 moved = true;
659                                         }
660                                 }
661                         } else if (orientation == Orientation.Horizontal) {
662                                 int min = panel1_min_size;
663                                 int max = panel2.Location.Y + (panel2.Height - this.panel2_min_size) - splitter_rectangle_moving.Height;
664
665                                 if (splitter_rectangle_moving.Y + delta > min && splitter_rectangle_moving.Y + delta < max) {
666                                         splitter_rectangle_moving.Y += delta;
667                                         moved = true;
668                                 } else {
669                                         // Ensure that the splitter is set to minimum or maximum position, 
670                                         // even if the mouse "skips".
671                                         //
672                                         if (splitter_rectangle_moving.Y + delta <= min && splitter_rectangle_moving.Y != min) {
673                                                 splitter_rectangle_moving.Y = min;
674                                                 moved = true;
675                                         } else if (splitter_rectangle_moving.Y + delta >= max && splitter_rectangle_moving.Y != max) {
676                                                 splitter_rectangle_moving.Y = max;
677                                                 moved = true;
678                                         }
679                                 }
680                         }
681
682                         if (moved) {
683                                 splitter_prev_move = currentMove;
684                                 OnSplitterMoving (new SplitterCancelEventArgs (location.X, location.Y, 
685                                                                                splitter_rectangle.X, splitter_rectangle.Y));
686                                 XplatUI.DrawReversibleRectangle (this.Handle, prev_location, 1);
687                                 XplatUI.DrawReversibleRectangle (this.Handle, splitter_rectangle_moving, 1);
688                         }
689                 }
690
691                 private void SplitterEndMove (Point location, bool cancel)
692                 {
693                         if (!cancel) {
694                                 // Prevent updating the splitter distance if the user changes it in e.g. the
695                                 // DoubleClick handler, but no delta move has happened in our drag-handling. 
696                                 // We don't compare to splitter_rectangle for exactly that reason here 
697                                 // (if it gets changed externally) and compare to a cached value.
698                                 // 
699                                 if (splitter_rectangle_before_move != splitter_rectangle_moving) {
700                                         splitter_rectangle = splitter_rectangle_moving;
701                                         UpdateSplitter ();
702                                 }
703                         }
704                         SplitterEventArgs args = new SplitterEventArgs (location.X, location.Y, 
705                                                                         splitter_rectangle.X, splitter_rectangle.Y);
706                         OnSplitterMoved (args);
707                 }
708
709                 private void SplitterSetCursor (Orientation orientation)
710                 {
711                         if (restore_cursor == null)
712                                 restore_cursor = this.Cursor;
713                         this.Cursor = orientation == Orientation.Vertical ? Cursors.VSplit : Cursors.HSplit;
714                 }
715
716                 private void SplitterRestoreCursor ()
717                 {
718                         if (restore_cursor != null) {
719                                 this.Cursor = restore_cursor;
720                                 restore_cursor = null;
721                         }
722                 }
723
724                 private void UpdateSplitter ()
725                 {
726                         this.SuspendLayout ();
727                         panel1.SuspendLayout ();
728                         panel2.SuspendLayout ();
729
730                         if (panel1_collapsed) {
731                                 panel2.Size = this.Size;
732                                 panel2.Location = new Point (0, 0);
733                         } else if (panel2_collapsed) {
734                                 panel1.Size = this.Size;
735                                 panel1.Location = new Point (0, 0);
736                         } else {
737                                 panel1.Location = new Point (0, 0);
738                                 if (orientation == Orientation.Vertical) {
739                                         splitter_rectangle.Y = 0;
740                                         panel1.InternalHeight = panel2.InternalHeight = this.Height;
741                                         panel1.InternalWidth = Math.Max (this.SplitterDistance, panel1_min_size);
742                                         panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
743                                         panel2.InternalWidth = Math.Max (this.Width - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
744                                         fixed_none_ratio = (double) this.Width / (double)this.SplitterDistance;
745                                 } else if (orientation == Orientation.Horizontal) {
746                                         splitter_rectangle.X = 0;
747                                         panel1.InternalWidth = panel2.InternalWidth = this.Width;
748                                         panel1.InternalHeight =  Math.Max (this.SplitterDistance, panel1_min_size);
749                                         panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
750                                         panel2.InternalHeight =  Math.Max (this.Height - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
751                                         fixed_none_ratio = (double) this.Height / (double)this.SplitterDistance;
752                                 }
753                         }
754                         panel1.ResumeLayout ();
755                         panel2.ResumeLayout ();
756                         this.ResumeLayout ();
757                 }
758
759                 private void UpdateLayout ()
760                 {
761                         panel1.SuspendLayout ();
762                         panel2.SuspendLayout ();
763
764                         if (panel1_collapsed) {
765                                 panel2.Size = this.Size;
766                                 panel2.Location = new Point (0, 0);
767                         } else if (panel2_collapsed) {
768                                 panel1.Size = this.Size;
769                                 panel1.Location = new Point (0, 0);
770                         } else {
771                                 panel1.Location = new Point (0, 0);
772                                 if (orientation == Orientation.Vertical) {
773                                         panel1.Location = new Point (0, 0);
774                                         panel1.InternalHeight = panel2.InternalHeight = this.Height;
775                                         splitter_rectangle.Height = this.Height;
776         
777                                         if (fixed_panel == FixedPanel.None) {
778                                                 splitter_rectangle.X = Math.Max ((int)Math.Floor (((double)this.Width) / fixed_none_ratio), panel1_min_size); //set distance
779                                                 panel1.InternalWidth = this.SplitterDistance;
780                                                 panel2.InternalWidth = this.Width - (this.SplitterWidth + this.SplitterDistance);
781                                                 panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
782                                         } else if (fixed_panel == FixedPanel.Panel1) {
783                                                 panel1.InternalWidth = this.SplitterDistance;
784                                                 panel2.InternalWidth = Math.Max (this.Width - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
785                                                 panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
786                                         } else if (fixed_panel == FixedPanel.Panel2) {
787                                                 splitter_rectangle.X = Math.Max (this.Width - (this.SplitterWidth + panel2.Width), panel1_min_size); //set distance
788                                                 panel1.InternalWidth = this.SplitterDistance;
789                                                 panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
790                                         }
791                                 } else if (orientation == Orientation.Horizontal) {
792                                         panel1.Location = new Point (0, 0);
793                                         panel1.InternalWidth = panel2.InternalWidth = this.Width;
794                                         splitter_rectangle.Width = this.Width;
795
796                                         if (fixed_panel == FixedPanel.None) {
797                                                 splitter_rectangle.Y = Math.Max ((int) Math.Floor ((double)this.Height / fixed_none_ratio), panel1_min_size); //set distance
798                                                 panel1.InternalHeight = this.SplitterDistance;
799                                                 panel2.InternalHeight = this.Height - (this.SplitterWidth + this.SplitterDistance);
800                                                 panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
801                                         } else if (fixed_panel == FixedPanel.Panel1) {
802                                                 panel1.InternalHeight = this.SplitterDistance;
803                                                 panel2.InternalHeight = Math.Max (this.Height - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
804                                                 panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
805                                         } else if (fixed_panel == FixedPanel.Panel2) {
806                                                 splitter_rectangle.Y =  Math.Max (this.Height - (this.SplitterWidth + panel2.Height), panel1_min_size); //set distance
807                                                 panel1.InternalHeight = this.SplitterDistance;
808                                                 panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
809                                         }
810                                 }
811                         }
812
813                         panel1.ResumeLayout ();
814                         panel2.ResumeLayout ();
815                 }
816
817                 #endregion
818
819                 #region Internal Classes
820                 internal class SplitContainerTypedControlCollection : ControlCollection
821                 {
822                         public SplitContainerTypedControlCollection (Control owner) : base (owner)
823                         {
824                         }
825                 }
826                 #endregion
827         }
828 }
829 #endif