2007-01-02 Mike Kestner <mkestner@novell.com>
[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 //
25 // Authors:
26 //      Jonathan Pobst (monkey@jpobst.com)
27 //
28
29 #if NET_2_0
30 using System;
31 using System.Runtime.InteropServices;
32 using System.ComponentModel;
33 using System.Drawing;
34 using System.Drawing.Drawing2D;
35
36 namespace System.Windows.Forms
37 {
38         [ComVisibleAttribute (true)]
39         [ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
40         [DefaultEvent ("SplitterMoved")]
41         [Docking (DockingBehavior.AutoDock)]
42         public class SplitContainer : ContainerControl
43         {
44                 #region Local Variables
45                 private FixedPanel fixed_panel;
46                 private int splitter_distance;
47                 private int splitter_width;
48                 private int splitter_increment;
49                 private Orientation orientation;
50                 private bool binding_context_set;
51
52                 private SplitterPanel panel1;
53                 private bool panel1_collapsed;
54                 private int panel1_min_size;
55
56                 private SplitterPanel panel2;
57                 private bool panel2_collapsed;
58                 private int panel2_min_size;
59
60                 private Splitter splitter;
61                 #endregion
62
63                 #region Public Events
64                 static object SplitterMovedEvent = new object ();
65                 static object SplitterMovingEvent = new object ();
66
67                 //[Browsable (false)]
68                 //[EditorBrowsable (EditorBrowsableState.Never)]
69                 //new public event EventHandler AutoSizeChanged;
70
71                 [Browsable (false)]
72                 [EditorBrowsable (EditorBrowsableState.Never)]
73                 public new event EventHandler BackgroundImageChanged {
74                         add { base.BackgroundImageChanged += value; }
75                         remove { base.BackgroundImageChanged -= value; }
76                 }
77
78                 [Browsable (false)]
79                 [EditorBrowsable (EditorBrowsableState.Never)]
80                 public new event EventHandler BackgroundImageLayoutChanged {
81                         add { base.BackgroundImageLayoutChanged += value; }
82                         remove { base.BackgroundImageLayoutChanged -= value; }
83                 }
84
85                 [Browsable (false)]
86                 [EditorBrowsable (EditorBrowsableState.Never)]
87                 public new event ControlEventHandler ControlAdded {
88                         add { base.ControlAdded += value; }
89                         remove { base.ControlAdded -= value; }
90                 }
91
92                 [Browsable (false)]
93                 [EditorBrowsable (EditorBrowsableState.Never)]
94                 public new event ControlEventHandler ControlRemoved {
95                         add { base.ControlRemoved += value; }
96                         remove { base.ControlRemoved -= value; }
97                 }
98
99                 [Browsable (false)]
100                 [EditorBrowsable (EditorBrowsableState.Never)]
101                 public new event EventHandler PaddingChanged {
102                         add { base.PaddingChanged += value; }
103                         remove { base.PaddingChanged -= value; }
104                 }
105                 
106                 public event SplitterEventHandler SplitterMoved {
107                         add { Events.AddHandler (SplitterMovedEvent, value); }
108                         remove { Events.RemoveHandler (SplitterMovedEvent, value); }
109                 }
110
111                 public event SplitterCancelEventHandler SplitterMoving {
112                         add { Events.AddHandler (SplitterMovingEvent, value); }
113                         remove { Events.RemoveHandler (SplitterMovingEvent, value); }
114                 }
115
116                 [Browsable (false)]
117                 [EditorBrowsable (EditorBrowsableState.Never)]
118                 public new event EventHandler TextChanged {
119                         add { base.TextChanged += value; }
120                         remove { base.TextChanged -= value; }
121                 }
122                 #endregion
123
124                 #region Public Constructors
125                 public SplitContainer ()
126                 {
127                         fixed_panel = FixedPanel.None;
128                         orientation = Orientation.Vertical;
129                         splitter_distance = 50;
130                         splitter_width = 4;
131                         splitter_increment = 1;
132                         panel1_collapsed = false;
133                         panel2_collapsed = false;
134                         panel1_min_size = 25;
135                         panel2_min_size = 25;
136                         binding_context_set = false;
137
138                         panel1 = new SplitterPanel (this);
139                         panel2 = new SplitterPanel (this);
140                         splitter = new Splitter ();
141
142                         splitter.TabStop = true;
143                         splitter.Size = new System.Drawing.Size (4, 4);
144                         splitter.SplitterMoved += new SplitterEventHandler (splitter_SplitterMoved);
145                         splitter.SplitterMoving += new SplitterEventHandler (splitter_SplitterMoving);
146
147                         panel1.Size = new Size (50, 50);
148
149                         this.Controls.Add (panel2);
150                         this.Controls.Add (splitter);
151                         this.Controls.Add (panel1);
152
153                         panel1.Dock = DockStyle.Left;
154                         panel2.Dock = DockStyle.Fill;
155                         splitter.Dock = DockStyle.Left;
156                 }
157                 #endregion
158
159                 #region Public Properties
160                 [Browsable (false)]
161                 [EditorBrowsable (EditorBrowsableState.Never)]
162                 [Localizable (true)]
163                 [DefaultValue (false)]
164                 public override bool AutoScroll {
165                         get { return base.AutoScroll; }
166                         set { base.AutoScroll = value; }
167                 }
168
169                 [Browsable (false)]
170                 [EditorBrowsable (EditorBrowsableState.Never)]
171                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
172                 new public Size AutoScrollMargin {
173                         get { return base.AutoScrollMargin; }
174                         set { base.AutoScrollMargin = value; }
175                 }
176
177                 [Browsable (false)]
178                 [EditorBrowsable (EditorBrowsableState.Never)]
179                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
180                 new public Size AutoScrollMinSize {
181                         get { return base.AutoScrollMinSize; }
182                         set { base.AutoScrollMinSize = value; }
183                 }
184
185                 //Uncomment once this has been implemented in Control.cs
186                 //[Browsable (false)]
187                 //[EditorBrowsable (EditorBrowsableState.Never)]
188                 //public override Point AutoScrollOffset {
189                 //        get { return base.AutoScrollOffset; }
190                 //        set { base.AutoScrollOffset = value; }
191                 //}
192
193                 [Browsable (false)]
194                 [EditorBrowsable (EditorBrowsableState.Never)]
195                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
196                 new public Point AutoScrollPosition {
197                         get { return base.AutoScrollPosition; }
198                         set { base.AutoScrollPosition = value; }
199                 }
200
201                 [Browsable (false)]
202                 [EditorBrowsable (EditorBrowsableState.Never)]
203                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
204                 public override bool AutoSize {
205                         get { return base.AutoSize; }
206                         set { base.AutoSize = value; }
207                 }
208
209                 [Browsable (false)]
210                 [EditorBrowsable (EditorBrowsableState.Never)]
211                 public override Image BackgroundImage {
212                         get { return base.BackgroundImage; }
213                         set {
214                                 base.BackgroundImage = value;
215                                 UpdateSplitterBackground ();
216                         }
217                 }
218
219                 [Browsable (false)]
220                 [EditorBrowsable (EditorBrowsableState.Never)]
221                 public override ImageLayout BackgroundImageLayout {
222                         get { return base.BackgroundImageLayout; }
223                         set { base.BackgroundImageLayout = value; }
224                 }
225
226                 [Browsable (false)]
227                 public override BindingContext BindingContext {
228                         get { return binding_context_set ? base.BindingContext : null; }
229                         set {
230                                 binding_context_set = true;
231                                 base.BindingContext = value;
232                         }
233                 }
234
235                 // MSDN says default is Fixed3D, creating a new SplitContainer says otherwise.
236                 [DefaultValue (BorderStyle.None)]
237                 public BorderStyle BorderStyle
238                 {
239                         get { return panel1.BorderStyle; }
240                         set {
241                                 if (!Enum.IsDefined (typeof (BorderStyle), value))
242                                         throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for BorderStyle", value));
243                                         
244                                 panel1.BorderStyle = value;
245                                 panel2.BorderStyle = value;
246                         }
247                 }
248
249                 [EditorBrowsable (EditorBrowsableState.Never)]
250                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
251                 new public ControlCollection Controls { get { return base.Controls; } }
252
253                 new public DockStyle Dock {
254                         get { return base.Dock; }
255                         set { base.Dock = value; }
256                 }
257
258                 [DefaultValue (FixedPanel.None)]
259                 public FixedPanel FixedPanel {
260                         get { return this.fixed_panel; }
261                         set {
262                                 if (!Enum.IsDefined (typeof (FixedPanel), value))
263                                         throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for FixedPanel", value));
264
265                                 this.fixed_panel = value;
266                         }
267                 }
268
269                 [Localizable (true)]
270                 [DefaultValue (false)]
271                 public bool IsSplitterFixed {
272                         get { return !splitter.Enabled; }
273                         set { splitter.Enabled = !value; }
274                 }
275
276                 [Localizable (true)]
277                 [DefaultValue (Orientation.Vertical)]
278                 public Orientation Orientation {
279                         get { return this.orientation; }
280                         set {
281                                 if (!Enum.IsDefined (typeof (Orientation), value))
282                                         throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for Orientation", value));
283
284                                 if (this.orientation != value) {
285                                         this.orientation = value;
286
287                                         switch (value) {
288                                                 case Orientation.Vertical:
289                                                         panel1.Dock = DockStyle.Left;
290                                                         panel2.Dock = DockStyle.Fill;
291                                                         splitter.Dock = DockStyle.Left;
292                                                         splitter.Width = this.splitter_width;
293                                                         panel1.InternalWidth = this.splitter_distance;
294                                                         if (panel2.Width < panel2_min_size)
295                                                                 panel1.InternalWidth = this.Width - this.splitter_width - panel2_min_size;
296                                                         break;
297                                                 case Orientation.Horizontal:
298                                                 default:
299                                                         panel1.Dock = DockStyle.Top;
300                                                         panel2.Dock = DockStyle.Fill;
301                                                         splitter.Dock = DockStyle.Top;
302                                                         splitter.Height = this.splitter_width;
303                                                         panel1.InternalHeight = this.splitter_distance;
304                                                         if (panel2.Height < panel2_min_size)
305                                                                 panel1.InternalHeight = this.Height - this.splitter_width - panel2_min_size;
306                                                         break;
307                                         }
308
309                                         this.PerformLayout ();
310                                 }
311                         }
312                 }
313
314                 [Browsable (false)]
315                 [EditorBrowsable (EditorBrowsableState.Never)]
316                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
317                 new public Padding Padding {
318                         get { return base.Padding; }
319                         set { base.Padding = value; }
320                 }
321
322                 [Localizable (false)]
323                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
324                 public SplitterPanel Panel1 { get { return this.panel1; } }
325
326                 [DefaultValue (false)]
327                 public bool Panel1Collapsed {
328                         get { return this.panel1_collapsed; }
329                         set {
330                                 this.panel1_collapsed = value;
331                                 this.panel1.Visible = !value;
332                                 this.splitter.Visible = !value;
333                         }
334                 }
335
336                 [Localizable (true)]
337                 [DefaultValue (25)]
338                 [RefreshProperties (RefreshProperties.All)]
339                 public int Panel1MinSize {
340                         get { return this.panel1_min_size; }
341                         set { 
342                                 this.panel1_min_size = value; 
343                                 this.splitter.MinSize = value; 
344                         }
345                 }
346
347                 [Localizable (false)]
348                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
349                 public SplitterPanel Panel2 { get { return this.panel2; } }
350
351                 [DefaultValue (false)]
352                 public bool Panel2Collapsed {
353                         get { return this.panel2_collapsed; }
354                         set {
355                                 this.panel2_collapsed = value; 
356                                 this.panel2.Visible = !value;
357                                 this.splitter.Visible = !value;
358                         }
359                 }
360
361                 [Localizable (true)]
362                 [DefaultValue (25)]
363                 [RefreshProperties (RefreshProperties.All)]
364                 public int Panel2MinSize {
365                         get { return this.panel2_min_size; }
366                         set { this.panel2_min_size = value; this.splitter.MinExtra = value; }
367                 }
368
369                 // MSDN says the default is 40, MS's implementation defaults to 50.
370                 [Localizable (true)]
371                 [DefaultValue (50)]
372                 [SettingsBindable (true)]
373                 public int SplitterDistance {
374                         get { return this.splitter_distance; }
375                         set {
376                                 if (value < 0)
377                                         throw new ArgumentOutOfRangeException ();
378
379                                 if (value < this.panel1_min_size)
380                                         value = this.panel1_min_size;
381
382                                 switch (this.orientation) {
383                                         case Orientation.Vertical:
384                                                 if (value > this.Width - this.panel2_min_size - this.splitter_width)
385                                                         value = this.Width - this.panel2_min_size - this.splitter_width;
386                                                 panel1.InternalWidth = value;
387                                                 break;
388                                         case Orientation.Horizontal:
389                                         default:
390                                                 if (value > this.Height - this.panel2_min_size - this.splitter_width)
391                                                         value = this.Height - this.panel2_min_size - this.splitter_width;
392                                                 panel1.InternalHeight = value;
393                                                 break;
394                                 }
395
396                                 this.splitter_distance = value;
397
398                                 UpdateSplitterBackground ();
399                         }
400                 }
401
402                 [Localizable (true)]
403                 [DefaultValue (1)]
404                 [MonoTODO ("Not implemented.")]
405                 public int SplitterIncrement {
406                         get { return this.splitter_increment; }
407                         set { this.splitter_increment = value; }
408                 }
409
410                 [Browsable (false)]
411                 public Rectangle SplitterRectangle { get { return splitter.Bounds; } }
412
413                 [Localizable (true)]
414                 [DefaultValue (4)]
415                 public int SplitterWidth {
416                         get { return this.splitter_width; }
417                         set {
418                                 if (value < 1)
419                                         throw new ArgumentOutOfRangeException ();
420
421                                 this.splitter_width = value;
422
423                                 switch (this.orientation) {
424                                         case Orientation.Horizontal:
425                                                 splitter.Height = value;
426                                                 break;
427                                         case Orientation.Vertical:
428                                         default:
429                                                 splitter.Width = value;
430                                                 break;
431                                 }
432                         }
433                 }
434
435                 [DefaultValue (true)]
436                 new public bool TabStop {
437                         get { return splitter.TabStop; }
438                         set { splitter.TabStop = value; }
439                 }
440
441                 [Browsable (false)]
442                 [EditorBrowsable (EditorBrowsableState.Never)]
443                 [Bindable (false)]
444                 public override string Text {
445                         get { return base.Text; }
446                         set { base.Text = value; }
447                 }
448                 #endregion
449
450                 #region Protected Properties
451                 protected override Size DefaultSize { get { return new Size (150, 100); } }
452                 #endregion
453
454                 #region Public Methods
455                 public void OnSplitterMoved (SplitterEventArgs e)
456                 {
457                         SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovedEvent]);
458                         if (eh != null)
459                                 eh (this, e);
460                 }
461
462                 public void OnSplitterMoving (SplitterCancelEventArgs e)
463                 {
464                         SplitterCancelEventHandler eh = (SplitterCancelEventHandler)(Events [SplitterMovingEvent]);
465                         if (eh != null)
466                                 eh (this, e);
467
468                         if (e.Cancel == true) {
469                                 e.SplitX = splitter.Location.X;
470                                 e.SplitY = splitter.Location.Y;
471                         }
472                 }
473                 #endregion
474
475                 #region Protected Methods
476                 protected override ControlCollection CreateControlsInstance ()
477                 {
478                         return new SplitContainerTypedControlCollection (this);
479                 }
480
481                 [MonoTODO ("Special focus semantics not implemented")]
482                 protected override void OnGotFocus (EventArgs e)
483                 {
484                         base.OnGotFocus (e);
485                 }
486
487                 protected override void OnKeyDown (KeyEventArgs e)
488                 {
489                         base.OnKeyDown (e);
490                 }
491
492                 protected override void OnKeyUp (KeyEventArgs e)
493                 {
494                         base.OnKeyUp (e);
495                 }
496
497                 protected override void OnLayout (LayoutEventArgs levent)
498                 {
499                         base.OnLayout (levent);
500                 }
501
502                 protected override void OnMouseDown (MouseEventArgs e)
503                 {
504                         base.OnMouseDown (e);
505                 }
506
507                 protected override void OnMouseLeave (EventArgs e)
508                 {
509                         base.OnMouseLeave (e);
510                 }
511
512                 protected override void OnMouseMove (MouseEventArgs e)
513                 {
514                         base.OnMouseMove (e);
515                 }
516
517                 protected override void OnMouseUp (MouseEventArgs e)
518                 {
519                         base.OnMouseUp (e);
520                 }
521                 
522                 protected override void OnPaint (PaintEventArgs e)
523                 {
524                         base.OnPaint (e);
525                 }
526
527                 protected override void OnRightToLeftChanged (EventArgs e)
528                 {
529                         base.OnRightToLeftChanged (e);
530                 }
531
532                 [MonoTODO ("Special focus semantics not implemented")]
533                 protected override bool ProcessDialogKey (Keys keyData)
534                 {
535                         return base.ProcessDialogKey (keyData);
536                 }
537
538                 [MonoTODO ("Special focus semantics not implemented")]
539                 protected override bool ProcessTabKey (bool forward)
540                 {
541                         return base.ProcessTabKey (forward);
542                 }
543
544                 [MonoTODO ("Special focus semantics not implemented")]
545                 protected override void Select (bool directed, bool forward)
546                 {
547                         base.Select (directed, forward);
548                 }
549
550                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
551                 {
552                         base.SetBoundsCore (x, y, width, height, specified);
553                 }
554
555                 protected override void WndProc (ref Message m)
556                 {
557                         base.WndProc (ref m);
558                 }
559                 #endregion
560                 
561                 #region Private Methods
562                 private void splitter_SplitterMoving (object sender, SplitterEventArgs e)
563                 {
564                         SplitterCancelEventArgs ea = new SplitterCancelEventArgs (e.X, e.Y, e.SplitX, e.SplitY);
565                         this.OnSplitterMoving (ea);
566                         e.SplitX = ea.SplitX;
567                         e.SplitY = ea.SplitY;
568                 }
569
570                 private void splitter_SplitterMoved (object sender, SplitterEventArgs e)
571                 {
572                         this.OnSplitterMoved (e);
573                 }
574
575                 private void UpdateSplitterBackground ()
576                 {
577                         if (this.BackgroundImage != null) {
578                                 Bitmap b = new Bitmap (splitter.Width, splitter.Height);
579                                 Graphics.FromImage (b).DrawImage (base.BackgroundImage, new Rectangle (0, 0, b.Width, b.Height), this.SplitterRectangle, GraphicsUnit.Pixel);
580                                 splitter.BackgroundImage = b;
581                         }
582                         else
583                                 splitter.BackgroundImage = this.BackgroundImage;
584                 }
585                 #endregion
586
587                 #region Internal Classes
588                 internal class SplitContainerTypedControlCollection : ControlCollection
589                 {
590                         public SplitContainerTypedControlCollection (Control owner) : base (owner)
591                         {
592                         }
593                 }
594                 #endregion
595         }
596 }
597 #endif