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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005-2008 Novell, Inc. (http://www.novell.com)
23 // Peter Dennis Bartok (pbartok@novell.com)
24 // Ivan N. Zlatev (contact i-nz.net)
33 using System
.ComponentModel
;
35 using System
.Reflection
;
36 using System
.Runtime
.InteropServices
;
38 namespace System
.Windows
.Forms
{
41 [ClassInterface (ClassInterfaceType
.AutoDispatch
)]
43 [DefaultEvent("SplitterMoved")]
44 [Designer("System.Windows.Forms.Design.SplitterDesigner, " + Consts
.AssemblySystem_Design
, "System.ComponentModel.Design.IDesigner")]
45 [DefaultProperty("Dock")]
46 public class Splitter
: Control
51 #region Local Variables
52 static private Cursor splitter_ns
;
53 static private Cursor splitter_we
;
54 // XXX this "new" shouldn't be here. Control shouldn't define border_style as internal.
55 new private BorderStyle border_style
;
56 private int min_extra
;
59 private int splitter_size
; // Size (width or height) of our splitter control
60 private bool horizontal
; // true if we've got a horizontal splitter
61 private Control affected
; // The control that the splitter resizes
62 private int split_requested
; // If the user requests a position before we have ever laid out the doc
63 private int splitter_prev_move
;
64 private Rectangle splitter_rectangle_moving
;
65 #endregion // Local Variables
69 splitter_ns
= Cursors
.HSplit
;
70 splitter_we
= Cursors
.VSplit
;
81 SetStyle(ControlStyles
.Selectable
, false);
82 Anchor
= AnchorStyles
.None
;
83 Dock
= DockStyle
.Left
;
85 Layout
+= new LayoutEventHandler(LayoutSplitter
);
86 this.ParentChanged
+= new EventHandler(ReparentSplitter
);
89 #endregion // Constructors
91 #region Public Instance Properties
93 [EditorBrowsable(EditorBrowsableState
.Never
)]
94 public override bool AllowDrop
{
96 return base.AllowDrop
;
100 base.AllowDrop
= value;
105 [DefaultValue(AnchorStyles
.None
)]
106 [EditorBrowsable(EditorBrowsableState
.Never
)]
107 public override AnchorStyles Anchor
{
109 return AnchorStyles
.None
;
113 ; // MS doesn't set it
118 [EditorBrowsable(EditorBrowsableState
.Never
)]
119 public override Image BackgroundImage
{
121 return base.BackgroundImage
;
125 base.BackgroundImage
= value;
131 [EditorBrowsable (EditorBrowsableState
.Never
)]
132 public override ImageLayout BackgroundImageLayout
{
133 get { return base.BackgroundImageLayout; }
134 set { base.BackgroundImageLayout = value; }
139 [DefaultValue (BorderStyle
.None
)]
140 [MWFDescription("Sets the border style for the splitter")]
141 [MWFCategory("Appearance")]
142 public BorderStyle BorderStyle
{
148 border_style
= value;
151 case BorderStyle
.FixedSingle
:
152 splitter_size
= 4; // We don't get motion events for 1px wide windows on X11. sigh.
155 case BorderStyle
.Fixed3D
:
156 value = BorderStyle
.None
;
160 case BorderStyle
.None
:
165 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for BorderStyle", value));
168 base.InternalBorderStyle
= value;
172 [DefaultValue(DockStyle
.Left
)]
174 public override DockStyle Dock
{
180 if (!Enum
.IsDefined (typeof (DockStyle
), value) || (value == DockStyle
.None
) || (value == DockStyle
.Fill
)) {
181 throw new ArgumentException("Splitter must be docked left, top, bottom or right");
184 if ((value == DockStyle
.Top
) || (value == DockStyle
.Bottom
)) {
186 Cursor
= splitter_ns
;
189 Cursor
= splitter_we
;
196 [EditorBrowsable(EditorBrowsableState
.Never
)]
197 public override Font Font
{
208 [EditorBrowsable(EditorBrowsableState
.Never
)]
209 public override Color ForeColor
{
211 return base.ForeColor
;
215 base.ForeColor
= value;
220 [EditorBrowsable(EditorBrowsableState
.Never
)]
221 public new ImeMode ImeMode
{
227 base.ImeMode
= value;
233 [MWFDescription("Sets minimum size of undocked window")]
234 [MWFCategory("Behaviour")]
235 public int MinExtra
{
247 [MWFDescription("Sets minimum size of the resized control")]
248 [MWFCategory("Behaviour")]
259 private int MaxSize
{
261 if (this.Parent
== null)
264 if (affected
== null)
265 affected
= AffectedControl
;
269 foreach (Control c
in this.Parent
.Controls
) {
273 case DockStyle
.Right
:
277 case DockStyle
.Bottom
:
285 return Parent
.ClientSize
.Height
- heights
- MinExtra
;
287 return Parent
.ClientSize
.Width
- widths
- MinExtra
;
292 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
293 [MWFDescription("Current splitter position")]
294 [MWFCategory("Layout")]
295 public int SplitPosition
{
297 affected
= AffectedControl
;
298 if (affected
== null) {
303 return CalculateSplitPosition();
307 return affected
.Height
;
309 return affected
.Width
;
319 affected
= AffectedControl
;
320 if (affected
== null)
321 split_requested
= value;
324 affected
.Height
= value;
326 affected
.Width
= value;
327 OnSplitterMoved (new SplitterEventArgs (Left
, Top
, value, value));
333 [EditorBrowsable(EditorBrowsableState
.Never
)]
334 public new bool TabStop
{
335 get { return base.TabStop; }
336 set { base.TabStop = value; }
341 [DesignerSerializationVisibility(DesignerSerializationVisibility
.Hidden
)]
342 [EditorBrowsable(EditorBrowsableState
.Never
)]
343 public override string Text
{
353 #endregion // Public Instance Properties
355 #region Protected Instance Properties
356 protected override CreateParams CreateParams
{
358 return base.CreateParams
;
363 protected override Cursor DefaultCursor
{
364 get { return base.DefaultCursor; }
368 protected override ImeMode DefaultImeMode
{
370 return ImeMode
.Disable
;
374 protected override Size DefaultSize
{
376 return new Size (3, 3);
379 #endregion // Protected Instance Properties
381 #region Public Instance Methods
383 public bool PreFilterMessage(ref Message m
) {
388 public override string ToString() {
389 return base.ToString () + String
.Format(", MinExtra: {0}, MinSize: {1}", min_extra
, min_size
);
391 #endregion // Public Instance Methods
393 #region Protected Instance Methods
394 protected override void OnKeyDown(KeyEventArgs e
) {
396 if (Capture
&& (e
.KeyCode
== Keys
.Escape
)) {
398 SplitterEndMove (Point
.Empty
, true);
402 protected override void OnMouseDown(MouseEventArgs e
) {
403 base.OnMouseDown (e
);
405 // Only allow if we are set up properly
406 if (affected
== null)
407 affected
= AffectedControl
;
410 if (affected
== null || e
.Button
!= MouseButtons
.Left
)
414 SplitterBeginMove (Parent
.PointToClient (PointToScreen (new Point (e
.X
, e
.Y
))));
417 protected override void OnMouseMove(MouseEventArgs e
) {
418 base.OnMouseMove (e
);
420 if (!Capture
|| e
.Button
!= MouseButtons
.Left
|| affected
== null)
423 // We need our mouse coordinates relative to our parent
424 SplitterMove (Parent
.PointToClient (PointToScreen (new Point (e
.X
, e
.Y
))));
427 protected override void OnMouseUp(MouseEventArgs e
) {
428 if (!Capture
|| e
.Button
!= MouseButtons
.Left
|| affected
== null) {
435 SplitterEndMove (Parent
.PointToClient (PointToScreen (new Point (e
.X
, e
.Y
))), false);
438 private void SplitterBeginMove (Point location
)
440 splitter_rectangle_moving
= new Rectangle (Bounds
.X
, Bounds
.Y
,
442 splitter_prev_move
= horizontal
? location
.Y
: location
.X
;
445 private void SplitterMove (Point location
)
447 int currentMove
= horizontal
? location
.Y
: location
.X
;
448 int delta
= currentMove
- splitter_prev_move
;
449 Rectangle prev_location
= splitter_rectangle_moving
;
451 int min
= this.MinSize
;
455 if (splitter_rectangle_moving
.Y
+ delta
> min
&& splitter_rectangle_moving
.Y
+ delta
< max
) {
456 splitter_rectangle_moving
.Y
+= delta
;
459 // Ensure that the splitter is set to minimum or maximum position,
460 // even if the mouse "skips".
462 if (splitter_rectangle_moving
.Y
+ delta
<= min
&& splitter_rectangle_moving
.Y
!= min
) {
463 splitter_rectangle_moving
.Y
= min
;
465 } else if (splitter_rectangle_moving
.Y
+ delta
>= max
&& splitter_rectangle_moving
.Y
!= max
) {
466 splitter_rectangle_moving
.Y
= max
;
471 if (splitter_rectangle_moving
.X
+ delta
> min
&& splitter_rectangle_moving
.X
+ delta
< max
) {
472 splitter_rectangle_moving
.X
+= delta
;
475 // Ensure that the splitter is set to minimum or maximum position,
476 // even if the mouse "skips".
478 if (splitter_rectangle_moving
.X
+ delta
<= min
&& splitter_rectangle_moving
.X
!= min
) {
479 splitter_rectangle_moving
.X
= min
;
481 } else if (splitter_rectangle_moving
.X
+ delta
>= max
&& splitter_rectangle_moving
.X
!= max
) {
482 splitter_rectangle_moving
.X
= max
;
489 splitter_prev_move
= currentMove
;
490 OnSplitterMoving (new SplitterEventArgs (location
.X
, location
.Y
,
491 splitter_rectangle_moving
.X
,
492 splitter_rectangle_moving
.Y
));
493 XplatUI
.DrawReversibleRectangle (this.Parent
.Handle
, prev_location
, 1);
494 XplatUI
.DrawReversibleRectangle (this.Parent
.Handle
, splitter_rectangle_moving
, 1);
498 private void SplitterEndMove (Point location
, bool cancel
)
501 // Resize the affected window
503 affected
.Height
= CalculateSplitPosition();
505 affected
.Width
= CalculateSplitPosition();
508 this.Parent
.Refresh (); // to clean up the drag handle artifacts from all controls
509 SplitterEventArgs args
= new SplitterEventArgs (location
.X
, location
.Y
,
510 splitter_rectangle_moving
.X
,
511 splitter_rectangle_moving
.Y
);
512 OnSplitterMoved (args
);
515 protected virtual void OnSplitterMoved(SplitterEventArgs sevent
) {
516 SplitterEventHandler eh
= (SplitterEventHandler
)(Events
[SplitterMovedEvent
]);
521 protected virtual void OnSplitterMoving(SplitterEventArgs sevent
) {
522 SplitterEventHandler eh
= (SplitterEventHandler
)(Events
[SplitterMovingEvent
]);
527 protected override void SetBoundsCore(int x
, int y
, int width
, int height
, BoundsSpecified specified
) {
528 // enforce our width / height
530 splitter_size
= height
;
531 if (splitter_size
< 1) {
534 base.SetBoundsCore (x
, y
, width
, splitter_size
, specified
);
536 splitter_size
= width
;
537 if (splitter_size
< 1) {
540 base.SetBoundsCore (x
, y
, splitter_size
, height
, specified
);
543 #endregion // Protected Instance Methods
545 #region Private Properties and Methods
546 private Control AffectedControl
{
551 // Doc says the first control preceeding us in the zorder
552 for (int i
= Parent
.Controls
.GetChildIndex(this) + 1; i
< Parent
.Controls
.Count
; i
++) {
555 if (Top
== Parent
.Controls
[i
].Bottom
)
556 return Parent
.Controls
[i
];
558 case DockStyle
.Bottom
:
559 if (Bottom
== Parent
.Controls
[i
].Top
)
560 return Parent
.Controls
[i
];
563 if (Left
== Parent
.Controls
[i
].Right
)
564 return Parent
.Controls
[i
];
566 case DockStyle
.Right
:
567 if (Right
== Parent
.Controls
[i
].Left
)
568 return Parent
.Controls
[i
];
576 private int CalculateSplitPosition() {
578 if (Dock
== DockStyle
.Top
)
579 return splitter_rectangle_moving
.Y
- affected
.Top
;
581 return affected
.Bottom
- splitter_rectangle_moving
.Y
- splitter_size
;
583 if (Dock
== DockStyle
.Left
)
584 return splitter_rectangle_moving
.X
- affected
.Left
;
586 return affected
.Right
- splitter_rectangle_moving
.X
- splitter_size
;
590 internal override void OnPaintInternal (PaintEventArgs e
) {
591 e
.Graphics
.FillRectangle(ThemeEngine
.Current
.ResPool
.GetSolidBrush(this.BackColor
), e
.ClipRectangle
);
594 private void LayoutSplitter(object sender
, LayoutEventArgs e
) {
595 affected
= AffectedControl
;
596 if (split_requested
!= -1) {
597 SplitPosition
= split_requested
;
598 split_requested
= -1;
602 private void ReparentSplitter(object sender
, EventArgs e
) {
606 #endregion // Private Properties and Methods
610 [EditorBrowsable(EditorBrowsableState
.Never
)]
611 public new event EventHandler BackgroundImageChanged
{
612 add { base.BackgroundImageChanged += value; }
613 remove { base.BackgroundImageChanged -= value; }
618 [EditorBrowsable (EditorBrowsableState
.Never
)]
619 public new event EventHandler BackgroundImageLayoutChanged
621 add { base.BackgroundImageLayoutChanged += value; }
622 remove { base.BackgroundImageLayoutChanged -= value; }
628 [EditorBrowsable(EditorBrowsableState
.Never
)]
629 public new event EventHandler Enter
{
630 add { base.Enter += value; }
631 remove { base.Enter -= value; }
635 [EditorBrowsable(EditorBrowsableState
.Never
)]
636 public new event EventHandler FontChanged
{
637 add { base.FontChanged += value; }
638 remove { base.FontChanged -= value; }
642 [EditorBrowsable(EditorBrowsableState
.Never
)]
643 public new event EventHandler ForeColorChanged
{
644 add { base.ForeColorChanged += value; }
645 remove { base.ForeColorChanged -= value; }
649 [EditorBrowsable(EditorBrowsableState
.Never
)]
650 public new event EventHandler ImeModeChanged
{
651 add { base.ImeModeChanged += value; }
652 remove { base.ImeModeChanged -= value; }
656 [EditorBrowsable(EditorBrowsableState
.Never
)]
657 public new event KeyEventHandler KeyDown
{
658 add { base.KeyDown += value; }
659 remove { base.KeyDown -= value; }
663 [EditorBrowsable(EditorBrowsableState
.Never
)]
664 public new event KeyPressEventHandler KeyPress
{
665 add { base.KeyPress += value; }
666 remove { base.KeyPress -= value; }
670 [EditorBrowsable(EditorBrowsableState
.Never
)]
671 public new event KeyEventHandler KeyUp
{
672 add { base.KeyUp += value; }
673 remove { base.KeyUp -= value; }
677 [EditorBrowsable(EditorBrowsableState
.Never
)]
678 public new event EventHandler Leave
{
679 add { base.Leave += value; }
680 remove { base.Leave -= value; }
684 [EditorBrowsable(EditorBrowsableState
.Never
)]
685 public new event EventHandler TabStopChanged
{
686 add { base.TabStopChanged += value; }
687 remove { base.TabStopChanged -= value; }
691 [EditorBrowsable(EditorBrowsableState
.Never
)]
692 public new event EventHandler TextChanged
{
693 add { base.TextChanged += value; }
694 remove { base.TextChanged -= value; }
697 static object SplitterMovedEvent
= new object ();
698 static object SplitterMovingEvent
= new object ();
700 public event SplitterEventHandler SplitterMoved
{
701 add { Events.AddHandler (SplitterMovedEvent, value); }
702 remove { Events.RemoveHandler (SplitterMovedEvent, value); }
705 public event SplitterEventHandler SplitterMoving
{
706 add { Events.AddHandler (SplitterMovingEvent, value); }
707 remove { Events.RemoveHandler (SplitterMovingEvent, value); }