Fix bug #395
[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 using System;
32 using System.Runtime.InteropServices;
33 using System.ComponentModel;
34 using System.Drawing;
35 using System.Drawing.Drawing2D;
36
37 namespace System.Windows.Forms
38 {
39         [ComVisibleAttribute (true)]
40         [ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
41         [DefaultEvent ("SplitterMoved")]
42         [Docking (DockingBehavior.AutoDock)]
43         [Designer ("System.Windows.Forms.Design.SplitContainerDesigner, " + Consts.AssemblySystem_Design)]
44         public class SplitContainer : ContainerControl
45 #if NET_4_0
46                 , ISupportInitialize
47 #endif
48         {
49                 #region Local Variables
50                 private FixedPanel fixed_panel;
51                 private Orientation orientation;
52
53                 private int splitter_increment;
54                 private Rectangle splitter_rectangle;
55                 private Rectangle splitter_rectangle_moving;
56                 private Rectangle splitter_rectangle_before_move;
57                 private bool splitter_fixed;
58                 private bool splitter_dragging;
59                 private int splitter_prev_move;
60                 private Cursor restore_cursor;
61                 private double fixed_none_ratio;
62
63                 private SplitterPanel panel1;
64                 private bool panel1_collapsed;
65                 private int panel1_min_size;
66
67                 private SplitterPanel panel2;
68                 private bool panel2_collapsed;
69                 private int panel2_min_size;
70                 #endregion
71
72                 #region Public Events
73                 static object SplitterMovedEvent = new object ();
74                 static object SplitterMovingEvent = new object ();
75
76                 [Browsable (false)]
77                 [EditorBrowsable (EditorBrowsableState.Never)]
78                 public new event EventHandler AutoSizeChanged {
79                         add { base.AutoSizeChanged += value; }
80                         remove { base.AutoSizeChanged -= value; }
81                 }
82
83                 [Browsable (true)]
84                 [EditorBrowsable (EditorBrowsableState.Always)]
85                 public new event EventHandler BackgroundImageChanged {
86                         add { base.BackgroundImageChanged += value; }
87                         remove { base.BackgroundImageChanged -= value; }
88                 }
89
90                 [Browsable (false)]
91                 [EditorBrowsable (EditorBrowsableState.Never)]
92                 public new event EventHandler BackgroundImageLayoutChanged {
93                         add { base.BackgroundImageLayoutChanged += value; }
94                         remove { base.BackgroundImageLayoutChanged -= value; }
95                 }
96
97                 [Browsable (false)]
98                 [EditorBrowsable (EditorBrowsableState.Never)]
99                 public new event ControlEventHandler ControlAdded {
100                         add { base.ControlAdded += value; }
101                         remove { base.ControlAdded -= value; }
102                 }
103
104                 [Browsable (false)]
105                 [EditorBrowsable (EditorBrowsableState.Never)]
106                 public new event ControlEventHandler ControlRemoved {
107                         add { base.ControlRemoved += value; }
108                         remove { base.ControlRemoved -= value; }
109                 }
110
111                 [Browsable (false)]
112                 [EditorBrowsable (EditorBrowsableState.Never)]
113                 public new event EventHandler PaddingChanged {
114                         add { base.PaddingChanged += value; }
115                         remove { base.PaddingChanged -= value; }
116                 }
117                 
118                 public event SplitterEventHandler SplitterMoved {
119                         add { Events.AddHandler (SplitterMovedEvent, value); }
120                         remove { Events.RemoveHandler (SplitterMovedEvent, value); }
121                 }
122
123                 public event SplitterCancelEventHandler SplitterMoving {
124                         add { Events.AddHandler (SplitterMovingEvent, value); }
125                         remove { Events.RemoveHandler (SplitterMovingEvent, value); }
126                 }
127
128                 [Browsable (false)]
129                 [EditorBrowsable (EditorBrowsableState.Never)]
130                 public new event EventHandler TextChanged {
131                         add { base.TextChanged += value; }
132                         remove { base.TextChanged -= value; }
133                 }
134                 #endregion
135
136                 #region UIA Framework Events
137                 static object UIACanResizeChangedEvent = new object ();
138
139                 internal event EventHandler UIACanResizeChanged {
140                         add { Events.AddHandler (UIACanResizeChangedEvent, value); }
141                         remove { Events.RemoveHandler (UIACanResizeChangedEvent, value); }
142                 }
143
144                 internal void OnUIACanResizeChanged (EventArgs e)
145                 {
146                         EventHandler eh = (EventHandler) Events [UIACanResizeChangedEvent];
147                         if (eh != null)
148                                 eh (this, e);
149                 }
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                                         // UIA Framework Event: CanResize Changed
337                                         OnUIACanResizeChanged (EventArgs.Empty);
338
339                                         PerformLayout ();
340                                 }
341                         }
342                 }
343
344                 [Localizable (true)]
345                 [DefaultValue (25)]
346                 [RefreshProperties (RefreshProperties.All)]
347                 public int Panel1MinSize {
348                         get { return this.panel1_min_size; }
349                         set { 
350                                 this.panel1_min_size = value; 
351                         }
352                 }
353
354                 [Localizable (false)]
355                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
356                 public SplitterPanel Panel2 { get { return this.panel2; } }
357
358                 [DefaultValue (false)]
359                 public bool Panel2Collapsed {
360                         get { return this.panel2_collapsed; }
361                         set {
362                                 if (panel2_collapsed != value) {
363                                         this.panel2_collapsed = value;
364                                         panel2.Visible = !value;
365
366                                         // UIA Framework Event: CanResize Changed
367                                         OnUIACanResizeChanged (EventArgs.Empty);
368
369                                         PerformLayout ();
370                                 }
371                         }
372                 }
373
374                 [Localizable (true)]
375                 [DefaultValue (25)]
376                 [RefreshProperties (RefreshProperties.All)]
377                 public int Panel2MinSize {
378                         get { return this.panel2_min_size; }
379                         set { this.panel2_min_size = value; }
380                 }
381
382                 // MSDN says the default is 40, MS's implementation defaults to 50.
383                 [Localizable (true)]
384                 [DefaultValue (50)]
385                 [SettingsBindable (true)]
386                 public int SplitterDistance {
387                         get { 
388                                 if (orientation == Orientation.Vertical)
389                                         return this.splitter_rectangle.X;
390                                 else
391                                         return this.splitter_rectangle.Y;
392                         }
393                         set {
394                                 if (value < 0)
395                                         throw new ArgumentOutOfRangeException ();
396
397                                 if (value < panel1_min_size)
398                                         value = panel1_min_size;
399
400                                 bool updated = true;
401                                 if (orientation == Orientation.Vertical) {
402                                         if (this.Width - (this.SplitterWidth + value) < panel2_min_size)
403                                                 value = this.Width - (this.SplitterWidth + panel2_min_size);
404                                         if (splitter_rectangle.X != value) {
405                                                 splitter_rectangle.X = value;
406                                                 updated = true;
407                                         }
408                                 } else {
409                                         if (this.Height - (this.SplitterWidth + value) < panel2_min_size)
410                                                 value = this.Height - (this.SplitterWidth + panel2_min_size);
411                                         if (splitter_rectangle.Y != value) {
412                                                 splitter_rectangle.Y = value;
413                                                 updated = true;
414                                         }
415                                 }
416                                 if (updated) {
417                                         UpdateSplitter ();
418                                         OnSplitterMoved (new SplitterEventArgs (Left, Top, splitter_rectangle.X, splitter_rectangle.Y));
419                                 }
420                         }
421                 }
422
423                 [Localizable (true)]
424                 [DefaultValue (1)]
425                 [MonoTODO ("Stub, never called")]
426                 public int SplitterIncrement {
427                         get { return this.splitter_increment; }
428                         set { this.splitter_increment = value; }
429                 }
430
431                 [Browsable (false)]
432                 public Rectangle SplitterRectangle { get { return splitter_rectangle; } }
433
434                 [Localizable (true)]
435                 [DefaultValue (4)]
436                 public int SplitterWidth {
437                         get {
438                                 if (orientation == Orientation.Vertical)
439                                         return this.splitter_rectangle.Width;
440                                 else
441                                         return this.splitter_rectangle.Height;
442                         }
443                         set {
444                                 if (value < 1)
445                                         throw new ArgumentOutOfRangeException ();
446
447                                 if (orientation == Orientation.Vertical)
448                                         this.splitter_rectangle.Width = value;
449                                 else
450                                         this.splitter_rectangle.Height = value;
451                                 UpdateSplitter ();
452                         }
453                 }
454
455                 [DispId (-516)]
456                 [DefaultValue (true)]
457                 [MonoTODO ("Stub, never called")]
458                 new public bool TabStop {
459                         get { return false; }
460                         set { }
461                 }
462
463                 [Browsable (false)]
464                 [EditorBrowsable (EditorBrowsableState.Never)]
465                 [Bindable (false)]
466                 public override string Text {
467                         get { return base.Text; }
468                         set { base.Text = value; }
469                 }
470                 #endregion
471
472                 #region Protected Properties
473                 protected override Size DefaultSize { get { return new Size (150, 100); } }
474                 #endregion
475
476                 #region Public Methods
477 #if NET_4_0
478                 [MonoTODO]
479                 public void BeginInit ()
480                 {
481                 }
482                 
483                 [MonoTODO]
484                 public void EndInit ()
485                 {
486                 }
487 #endif
488                 
489                 public void OnSplitterMoved (SplitterEventArgs e)
490                 {
491                         SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovedEvent]);
492                         if (eh != null)
493                                 eh (this, e);
494                 }
495
496                 public void OnSplitterMoving (SplitterCancelEventArgs e)
497                 {
498                         SplitterCancelEventHandler eh = (SplitterCancelEventHandler)(Events [SplitterMovingEvent]);
499                         if (eh != null)
500                                 eh (this, e);
501                 }
502                 #endregion
503
504                 #region Protected Methods
505                 [EditorBrowsable (EditorBrowsableState.Advanced)]
506                 protected override ControlCollection CreateControlsInstance ()
507                 {
508                         return new SplitContainerTypedControlCollection (this);
509                 }
510
511                 protected override void OnGotFocus (EventArgs e)
512                 {
513                         base.OnGotFocus (e);
514                 }
515
516                 protected override void OnKeyDown (KeyEventArgs e)
517                 {
518                         base.OnKeyDown (e);
519                 }
520
521                 protected override void OnKeyUp (KeyEventArgs e)
522                 {
523                         base.OnKeyUp (e);
524                 }
525
526                 protected override void OnLayout (LayoutEventArgs e)
527                 {
528                         UpdateLayout ();
529                         base.OnLayout (e);
530                 }
531
532                 protected override void OnLostFocus (EventArgs e)
533                 {
534                         base.OnLostFocus (e);
535                 }
536
537                 protected override void OnMouseCaptureChanged (EventArgs e)
538                 {
539                         base.OnMouseCaptureChanged (e);
540                 }
541                 
542                 protected override void OnMouseDown (MouseEventArgs e)
543                 {
544                         base.OnMouseDown (e);
545                         if (!splitter_fixed && SplitterHitTest (e.Location)) {
546                                 splitter_dragging = true;
547                                 SplitterBeginMove (e.Location);
548                         }
549                 }
550
551                 protected override void OnMouseLeave (EventArgs e)
552                 {
553                         base.OnMouseLeave (e);
554                         SplitterRestoreCursor ();
555                 }
556
557                 [EditorBrowsable (EditorBrowsableState.Advanced)]
558                 protected override void OnMouseMove (MouseEventArgs e)
559                 {
560                         base.OnMouseMove (e);
561
562                         if (splitter_dragging)
563                                 SplitterMove (e.Location);
564
565                         if (!splitter_fixed && SplitterHitTest (e.Location))
566                                 SplitterSetCursor (orientation);
567                         
568                 }
569
570                 protected override void OnMouseUp (MouseEventArgs e)
571                 {
572                         base.OnMouseUp (e);
573                         if (splitter_dragging) {
574                                 SplitterEndMove (e.Location, false);
575                                 SplitterRestoreCursor ();
576                                 splitter_dragging = false;
577                         }
578                 }
579                 
580                 protected override void OnPaint (PaintEventArgs e)
581                 {
582                         base.OnPaint (e);
583                 }
584
585                 [EditorBrowsable (EditorBrowsableState.Advanced)]
586                 protected override void OnRightToLeftChanged (EventArgs e)
587                 {
588                         base.OnRightToLeftChanged (e);
589                 }
590
591                 protected override bool ProcessDialogKey (Keys keyData)
592                 {
593                         return base.ProcessDialogKey (keyData);
594                 }
595
596                 protected override bool ProcessTabKey (bool forward)
597                 {
598                         return base.ProcessTabKey (forward);
599                 }
600
601                 [EditorBrowsable (EditorBrowsableState.Advanced)]
602                 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
603                 {
604                         base.ScaleControl (factor, specified);
605                 }
606                 
607                 protected override void Select (bool directed, bool forward)
608                 {
609                         base.Select (directed, forward);
610                 }
611
612                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
613                 {
614                         base.SetBoundsCore (x, y, width, height, specified);
615                 }
616
617                 protected override void WndProc (ref Message msg)
618                 {                       
619                         base.WndProc (ref msg);
620                 }
621                 #endregion
622                 
623                 #region Private Methods
624
625                 private bool SplitterHitTest (Point location)
626                 {
627                         if (location.X >= splitter_rectangle.X &&
628                                 location.X <= splitter_rectangle.X + splitter_rectangle.Width &&
629                                 location.Y >= splitter_rectangle.Y &&
630                                 location.Y <= splitter_rectangle.Y + splitter_rectangle.Height) {
631                                 return true;
632                         }
633                         return false;                              
634                 }
635
636                 private void SplitterBeginMove (Point location)
637                 {
638                         splitter_prev_move = orientation == Orientation.Vertical ? location.X : location.Y;
639                         splitter_rectangle_moving = splitter_rectangle;
640                         splitter_rectangle_before_move = splitter_rectangle;
641                 }
642
643                 private void SplitterMove (Point location)
644                 {
645                         int currentMove = orientation == Orientation.Vertical ? location.X : location.Y;
646                         int delta = currentMove - splitter_prev_move;
647                         Rectangle prev_location = splitter_rectangle_moving;
648                         bool moved = false;
649
650                         if (orientation == Orientation.Vertical) {
651                                 int min = panel1_min_size;
652                                 int max = panel2.Location.X + (panel2.Width - this.panel2_min_size) - splitter_rectangle_moving.Width;
653
654                                 if (splitter_rectangle_moving.X + delta > min && splitter_rectangle_moving.X + delta < max) {
655                                         splitter_rectangle_moving.X += delta;
656                                         moved = true;
657                                 } else {
658                                         // Ensure that the splitter is set to minimum or maximum position, 
659                                         // even if the mouse "skips".
660                                         //
661                                         if (splitter_rectangle_moving.X + delta <= min && splitter_rectangle_moving.X != min) {
662                                                 splitter_rectangle_moving.X = min;
663                                                 moved = true;
664                                         } else if (splitter_rectangle_moving.X + delta >= max && splitter_rectangle_moving.X != max) {
665                                                 splitter_rectangle_moving.X = max;
666                                                 moved = true;
667                                         }
668                                 }
669                         } else if (orientation == Orientation.Horizontal) {
670                                 int min = panel1_min_size;
671                                 int max = panel2.Location.Y + (panel2.Height - this.panel2_min_size) - splitter_rectangle_moving.Height;
672
673                                 if (splitter_rectangle_moving.Y + delta > min && splitter_rectangle_moving.Y + delta < max) {
674                                         splitter_rectangle_moving.Y += delta;
675                                         moved = true;
676                                 } else {
677                                         // Ensure that the splitter is set to minimum or maximum position, 
678                                         // even if the mouse "skips".
679                                         //
680                                         if (splitter_rectangle_moving.Y + delta <= min && splitter_rectangle_moving.Y != min) {
681                                                 splitter_rectangle_moving.Y = min;
682                                                 moved = true;
683                                         } else if (splitter_rectangle_moving.Y + delta >= max && splitter_rectangle_moving.Y != max) {
684                                                 splitter_rectangle_moving.Y = max;
685                                                 moved = true;
686                                         }
687                                 }
688                         }
689
690                         if (moved) {
691                                 splitter_prev_move = currentMove;
692                                 OnSplitterMoving (new SplitterCancelEventArgs (location.X, location.Y, 
693                                                                                splitter_rectangle.X, splitter_rectangle.Y));
694                                 XplatUI.DrawReversibleRectangle (this.Handle, prev_location, 1);
695                                 XplatUI.DrawReversibleRectangle (this.Handle, splitter_rectangle_moving, 1);
696                         }
697                 }
698
699                 private void SplitterEndMove (Point location, bool cancel)
700                 {
701                         if (!cancel) {
702                                 // Prevent updating the splitter distance if the user changes it in e.g. the
703                                 // DoubleClick handler, but no delta move has happened in our drag-handling. 
704                                 // We don't compare to splitter_rectangle for exactly that reason here 
705                                 // (if it gets changed externally) and compare to a cached value.
706                                 // 
707                                 if (splitter_rectangle_before_move != splitter_rectangle_moving) {
708                                         splitter_rectangle = splitter_rectangle_moving;
709                                         UpdateSplitter ();
710                                 }
711                         }
712                         SplitterEventArgs args = new SplitterEventArgs (location.X, location.Y, 
713                                                                         splitter_rectangle.X, splitter_rectangle.Y);
714                         OnSplitterMoved (args);
715                 }
716
717                 private void SplitterSetCursor (Orientation orientation)
718                 {
719                         if (restore_cursor == null)
720                                 restore_cursor = this.Cursor;
721                         this.Cursor = orientation == Orientation.Vertical ? Cursors.VSplit : Cursors.HSplit;
722                 }
723
724                 private void SplitterRestoreCursor ()
725                 {
726                         if (restore_cursor != null) {
727                                 this.Cursor = restore_cursor;
728                                 restore_cursor = null;
729                         }
730                 }
731
732                 private void UpdateSplitter ()
733                 {
734                         this.SuspendLayout ();
735                         panel1.SuspendLayout ();
736                         panel2.SuspendLayout ();
737
738                         if (panel1_collapsed) {
739                                 panel2.Size = this.Size;
740                                 panel2.Location = new Point (0, 0);
741                         } else if (panel2_collapsed) {
742                                 panel1.Size = this.Size;
743                                 panel1.Location = new Point (0, 0);
744                         } else {
745                                 panel1.Location = new Point (0, 0);
746                                 if (orientation == Orientation.Vertical) {
747                                         splitter_rectangle.Y = 0;
748                                         panel1.InternalHeight = panel2.InternalHeight = this.Height;
749                                         panel1.InternalWidth = Math.Max (this.SplitterDistance, panel1_min_size);
750                                         panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
751                                         panel2.InternalWidth = Math.Max (this.Width - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
752                                         fixed_none_ratio = (double) this.Width / (double)this.SplitterDistance;
753                                 } else if (orientation == Orientation.Horizontal) {
754                                         splitter_rectangle.X = 0;
755                                         panel1.InternalWidth = panel2.InternalWidth = this.Width;
756                                         panel1.InternalHeight =  Math.Max (this.SplitterDistance, panel1_min_size);
757                                         panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
758                                         panel2.InternalHeight =  Math.Max (this.Height - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
759                                         fixed_none_ratio = (double) this.Height / (double)this.SplitterDistance;
760                                 }
761                         }
762                         panel1.ResumeLayout ();
763                         panel2.ResumeLayout ();
764                         this.ResumeLayout ();
765                 }
766
767                 private void UpdateLayout ()
768                 {
769                         panel1.SuspendLayout ();
770                         panel2.SuspendLayout ();
771
772                         if (panel1_collapsed) {
773                                 panel2.Size = this.Size;
774                                 panel2.Location = new Point (0, 0);
775                         } else if (panel2_collapsed) {
776                                 panel1.Size = this.Size;
777                                 panel1.Location = new Point (0, 0);
778                         } else {
779                                 panel1.Location = new Point (0, 0);
780                                 if (orientation == Orientation.Vertical) {
781                                         panel1.Location = new Point (0, 0);
782                                         panel1.InternalHeight = panel2.InternalHeight = this.Height;
783                                         splitter_rectangle.Height = this.Height;
784         
785                                         if (fixed_panel == FixedPanel.None) {
786                                                 splitter_rectangle.X = Math.Max ((int)Math.Floor (((double)this.Width) / fixed_none_ratio), panel1_min_size); //set distance
787                                                 panel1.InternalWidth = this.SplitterDistance;
788                                                 panel2.InternalWidth = this.Width - (this.SplitterWidth + this.SplitterDistance);
789                                                 panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
790                                         } else if (fixed_panel == FixedPanel.Panel1) {
791                                                 panel1.InternalWidth = this.SplitterDistance;
792                                                 panel2.InternalWidth = Math.Max (this.Width - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
793                                                 panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
794                                         } else if (fixed_panel == FixedPanel.Panel2) {
795                                                 splitter_rectangle.X = Math.Max (this.Width - (this.SplitterWidth + panel2.Width), panel1_min_size); //set distance
796                                                 panel1.InternalWidth = this.SplitterDistance;
797                                                 panel2.Location = new Point (this.SplitterWidth + this.SplitterDistance, 0);
798                                         }
799                                 } else if (orientation == Orientation.Horizontal) {
800                                         panel1.Location = new Point (0, 0);
801                                         panel1.InternalWidth = panel2.InternalWidth = this.Width;
802                                         splitter_rectangle.Width = this.Width;
803
804                                         if (fixed_panel == FixedPanel.None) {
805                                                 splitter_rectangle.Y = Math.Max ((int) Math.Floor ((double)this.Height / fixed_none_ratio), panel1_min_size); //set distance
806                                                 panel1.InternalHeight = this.SplitterDistance;
807                                                 panel2.InternalHeight = this.Height - (this.SplitterWidth + this.SplitterDistance);
808                                                 panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
809                                         } else if (fixed_panel == FixedPanel.Panel1) {
810                                                 panel1.InternalHeight = this.SplitterDistance;
811                                                 panel2.InternalHeight = Math.Max (this.Height - (this.SplitterWidth + this.SplitterDistance), panel2_min_size);
812                                                 panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
813                                         } else if (fixed_panel == FixedPanel.Panel2) {
814                                                 splitter_rectangle.Y =  Math.Max (this.Height - (this.SplitterWidth + panel2.Height), panel1_min_size); //set distance
815                                                 panel1.InternalHeight = this.SplitterDistance;
816                                                 panel2.Location = new Point (0, this.SplitterWidth + this.SplitterDistance);
817                                         }
818                                 }
819                         }
820
821                         panel1.ResumeLayout ();
822                         panel2.ResumeLayout ();
823                 }
824
825                 #endregion
826
827                 #region Internal Classes
828                 internal class SplitContainerTypedControlCollection : ControlCollection
829                 {
830                         public SplitContainerTypedControlCollection (Control owner) : base (owner)
831                         {
832                         }
833                 }
834                 #endregion
835         }
836 }