* TreeView.cs: Don't draw the selected node when we lose
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ScrollableControl.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004 Novell, Inc.
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25
26
27 // NOT COMPLETE
28
29 using System;
30 using System.ComponentModel;
31 using System.ComponentModel.Design;
32 using System.Drawing;
33
34 namespace System.Windows.Forms {
35         [Designer ("System.Windows.Forms.Design.ScrollableControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
36         public class ScrollableControl : Control {
37                 #region Local Variables
38                 private bool                    auto_vscroll;
39                 private bool                    auto_hscroll;
40                 private bool                    hscroll_visible;
41                 private bool                    vscroll_visible;
42                 private bool                    force_hscroll_visible;
43                 private bool                    force_vscroll_visible;
44                 private bool                    auto_scroll;
45                 private Size                    auto_scroll_margin;
46                 private Size                    auto_scroll_min_size;
47                 private Point                   scroll_position;
48                 private DockPaddingEdges        dock_padding;
49                 private SizeGrip                sizegrip;
50                 private HScrollBar              hscrollbar;
51                 private VScrollBar              vscrollbar;
52                 #endregion      // Local Variables
53
54                 [MonoTODO("Need to use the edge values when performing the layout")]
55                 [TypeConverter(typeof(ScrollableControl.DockPaddingEdgesConverter))]
56                 #region Subclass DockPaddingEdges
57                 public class DockPaddingEdges : ICloneable {
58                         #region DockPaddingEdges Local Variables
59                         private int all;
60                         private int left;
61                         private int right;
62                         private int top;
63                         private int bottom;
64                         #endregion      // DockPaddingEdges Local Variables
65
66                         #region DockPaddingEdges Constructor
67                         internal DockPaddingEdges() {
68                                 all = 0;
69                                 left = 0;
70                                 right = 0;
71                                 top = 0;
72                                 bottom = 0;
73                         }
74                         #endregion      // DockPaddingEdges Constructor
75
76                         #region DockPaddingEdges Public Instance Properties
77                         [RefreshProperties(RefreshProperties.All)]
78                         public int All {
79                                 get {
80                                         return all;
81                                 }
82
83                                 set {
84                                         all = value;
85                                         left = value;
86                                         right = value;
87                                         top = value;
88                                         bottom = value;
89                                 }
90                         }
91
92                         [RefreshProperties(RefreshProperties.All)]
93                         public int Bottom {
94                                 get {
95                                         return bottom;
96                                 }
97
98                                 set {
99                                         bottom = value;
100                                         all = 0;
101                                 }
102                         }
103
104                         [RefreshProperties(RefreshProperties.All)]
105                         public int Left {
106                                 get {
107                                         return left;
108                                 }
109
110                                 set {
111                                         left=value;
112                                         all = 0;
113                                 }
114                         }
115
116                         [RefreshProperties(RefreshProperties.All)]
117                         public int Right {
118                                 get {
119                                         return right;
120                                 }
121
122                                 set {
123                                         right=value;
124                                         all = 0;
125                                 }
126                         }
127
128                         [RefreshProperties(RefreshProperties.All)]
129                         public int Top {
130                                 get {
131                                         return top;
132                                 }
133
134                                 set {
135                                         top=value;
136                                         all = 0;
137                                 }
138                         }
139
140                         #endregion      // DockPaddingEdges Public Instance Properties
141
142                         // Public Instance Methods
143                         public override bool Equals(object other) {
144                                 if (! (other is DockPaddingEdges)) {
145                                         return false;
146                                 }
147
148                                 if (    (this.all == ((DockPaddingEdges)other).all) && (this.left == ((DockPaddingEdges)other).left) &&
149                                         (this.right == ((DockPaddingEdges)other).right) && (this.top == ((DockPaddingEdges)other).top) && 
150                                         (this.bottom == ((DockPaddingEdges)other).bottom)) {
151                                         return true;
152                                 }
153
154                                 return false;
155                         }
156
157                         public override int GetHashCode() {
158                                 return all*top*bottom*right*left;
159                         }
160
161                         public override string ToString() {
162                                 return "All = "+all.ToString()+" Top = "+top.ToString()+" Left = "+left.ToString()+" Bottom = "+bottom.ToString()+" Right = "+right.ToString();
163                         }
164
165                         object ICloneable.Clone() {
166                                 DockPaddingEdges padding_edge;
167
168                                 padding_edge=new DockPaddingEdges();
169
170                                 padding_edge.all=all;
171                                 padding_edge.left=left;
172                                 padding_edge.right=right;
173                                 padding_edge.top=top;
174                                 padding_edge.bottom=bottom;
175
176                                 return padding_edge;
177                         }
178                 }
179                 #endregion      // Subclass DockPaddingEdges
180
181                 #region Subclass DockPaddingEdgesConverter
182                 public class DockPaddingEdgesConverter : System.ComponentModel.TypeConverter {
183                         // Public Constructors
184                         public DockPaddingEdgesConverter() {
185                         }
186
187                         // Public Instance Methods
188                         public override PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, Attribute[] attributes) {
189                                 return TypeDescriptor.GetProperties(typeof(DockPaddingEdges), attributes);
190                         }
191
192                         public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) {
193                                 return true;
194                         }
195                 }
196                 #endregion      // Subclass DockPaddingEdgesConverter
197
198                 #region Public Constructors
199                 public ScrollableControl() {
200                         SetStyle(ControlStyles.ContainerControl, true);
201                         SetStyle(ControlStyles.AllPaintingInWmPaint, false);
202                         auto_scroll = false;
203                         auto_hscroll = false;
204                         auto_vscroll = false;
205                         hscroll_visible = false;
206                         vscroll_visible = false;
207                         force_hscroll_visible = false;
208                         force_vscroll_visible = false;
209                         auto_scroll_margin = new Size(0, 0);
210                         auto_scroll_min_size = new Size(0, 0);
211                         scroll_position = new Point(0, 0);
212                         dock_padding = new DockPaddingEdges();
213                         SizeChanged +=new EventHandler(Recalculate);
214                         VisibleChanged += new EventHandler(Recalculate);
215                 }
216                 #endregion      // Public Constructors
217
218                 #region Protected Static Fields
219                 protected const int ScrollStateAutoScrolling    = 1;
220                 protected const int ScrollStateFullDrag         = 16;
221                 protected const int ScrollStateHScrollVisible   = 2;
222                 protected const int ScrollStateUserHasScrolled  = 8;
223                 protected const int ScrollStateVScrollVisible   = 4;
224                 #endregion      // Protected Static Fields
225
226                 #region Public Instance Properties
227                 [DefaultValue(false)]
228                 [Localizable(true)]
229                 public virtual bool AutoScroll {
230                         get {
231                                 return  auto_scroll;
232                         }
233
234                         set {
235                                 if (auto_scroll == value) {
236                                         return;
237                                 }
238
239                                 auto_scroll = value;
240                                 if (!auto_scroll) {
241                                         SuspendLayout ();
242
243                                         Controls.RemoveImplicit (hscrollbar);
244                                         hscrollbar.Dispose();
245                                         hscrollbar = null;
246                                         hscroll_visible = false;
247
248                                         Controls.RemoveImplicit (vscrollbar);
249                                         vscrollbar.Dispose();
250                                         vscrollbar = null;
251                                         vscroll_visible = false;
252
253                                         Controls.RemoveImplicit (sizegrip);
254                                         sizegrip.Dispose();
255                                         sizegrip = null;
256
257                                         ResumeLayout ();
258                                 } else {
259                                         SuspendLayout ();
260
261                                         hscrollbar = new HScrollBar();
262                                         hscrollbar.Visible = false;
263                                         hscrollbar.ValueChanged += new EventHandler(HandleScrollBar);
264                                         hscrollbar.Height = SystemInformation.HorizontalScrollBarHeight;
265                                         this.Controls.AddImplicit (hscrollbar);
266
267                                         vscrollbar = new VScrollBar();
268                                         vscrollbar.Visible = false;
269                                         vscrollbar.ValueChanged += new EventHandler(HandleScrollBar);
270                                         vscrollbar.Width = SystemInformation.VerticalScrollBarWidth;
271                                         this.Controls.AddImplicit (vscrollbar);
272
273                                         sizegrip = new SizeGrip();
274                                         sizegrip.Visible = false;
275                                         this.Controls.AddImplicit (sizegrip);
276
277                                         ResumeLayout ();
278                                 }
279                         }
280                 }
281
282                 [Localizable(true)]
283                 public Size AutoScrollMargin {
284                         get {
285                                 return auto_scroll_margin;
286                         }
287
288                         set {
289                                 if (value.Width < 0) {
290                                         throw new ArgumentException("Width is assigned less than 0", "value.Width");
291                                 }
292
293                                 if (value.Height < 0) {
294                                         throw new ArgumentException("Height is assigned less than 0", "value.Height");
295                                 }
296
297                                 auto_scroll_margin = value;
298                         }
299                 }
300
301                 [Localizable(true)]
302                 public Size AutoScrollMinSize {
303                         get {
304                                 return auto_scroll_min_size;
305                         }
306
307                         set {
308                                 auto_scroll_min_size = value;
309                         }
310                 }
311
312                 [Browsable(false)]
313                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
314                 public Point AutoScrollPosition {
315                         get {
316                                 return new Point(-scroll_position.X, -scroll_position.Y);
317                         }
318
319                         set {
320                                 if ((value.X != scroll_position.X) || (value.Y != scroll_position.Y)) {
321                                         int     shift_x;
322                                         int     shift_y;
323
324                                         shift_x = 0;
325                                         shift_y = 0;
326                                         if (hscroll_visible) {
327                                                 shift_x = value.X - scroll_position.X;
328                                         }
329
330                                         if (vscroll_visible) {
331                                                 shift_y = value.Y - scroll_position.Y;
332                                         }
333
334                                         ScrollWindow(shift_x, shift_y);
335
336                                         if (hscroll_visible) {
337                                                 hscrollbar.Value = scroll_position.X;
338                                         }
339
340                                         if (vscroll_visible) {
341                                                 vscrollbar.Value = scroll_position.Y;
342                                         }
343
344                                 }
345                         }
346                 }
347
348                 public override Rectangle DisplayRectangle {
349                         get {
350                                 Rectangle rect;
351                                 
352                                 rect = base.DisplayRectangle;
353                                 if (vscroll_visible) {
354                                         rect.Width -= vscrollbar.Width;
355                                         if (rect.Width < 0) {
356                                                 rect.Width = 0;
357                                         }
358                                 }
359                                 
360                                 if (hscroll_visible) {
361                                         rect.Height -= hscrollbar.Height;
362                                         if (rect.Height < 0) {
363                                                 rect.Height = 0;
364                                         }
365                                 }
366                                 return rect;
367                                 //return new Rectangle(-scroll_position.X, -scroll_position.Y, auto_scroll_min_size.Width, auto_scroll_min_size.Height);
368                         }
369                 }
370
371                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
372                 [Localizable(true)]
373                 public DockPaddingEdges DockPadding {
374                         get {
375                                 return dock_padding;
376                         }
377
378                         // DockPadding is documented as 'get' only ( http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsScrollableControlClassAutoScrollTopic.asp )
379                         // but Microsoft's examples on that page show 'set' usage
380 //                      set {
381 //                              dock_padding = value;
382 //                      }
383                 }
384                 #endregion      // Public Instance Properties
385
386                 #region Protected Instance Methods
387                 protected override CreateParams CreateParams {
388                         get {
389                                 return base.CreateParams;
390                         }
391                 }
392
393                 protected bool HScroll {
394                         get {
395                                 return hscroll_visible;
396                         }
397
398                         set {
399                                 if (hscroll_visible != value) {
400                                         force_hscroll_visible = value;
401                                         Recalculate(this, EventArgs.Empty);
402                                 }
403                         }
404                 }
405
406                 protected bool VScroll {
407                         get {
408                                 return vscroll_visible;
409                         }
410
411                         set {
412                                 if (vscroll_visible != value) {
413                                         force_vscroll_visible = value;
414                                         Recalculate(this, EventArgs.Empty);
415                                 }
416                         }
417                 }
418                 #endregion      // Protected Instance Methods
419
420                 #region Public Instance Methods
421                 public void ScrollControlIntoView(Control activeControl) {
422                 }
423
424                 public void SetAutoScrollMargin(int x, int y) {
425                         if (x < 0) {
426                                 x = 0;
427                         }
428
429                         if (y < 0) {
430                                 y = 0;
431                         }
432
433                         auto_scroll_margin = new Size(x, y);
434                         Recalculate(this, EventArgs.Empty);
435                 }
436                 #endregion      // Public Instance Methods
437
438                 #region Protected Instance Methods
439                 [EditorBrowsable(EditorBrowsableState.Advanced)]
440                 protected virtual void AdjustFormScrollbars(bool displayScrollbars) {
441                         // Internal MS
442                 }
443
444                 [EditorBrowsable(EditorBrowsableState.Advanced)]
445                 protected bool GetScrollState(int bit) {
446                         return false;
447                         // Internal MS
448                 }
449
450                 [EditorBrowsable(EditorBrowsableState.Advanced)]
451                 protected override void OnLayout(LayoutEventArgs levent) {
452                         base.OnLayout(levent);
453                 }
454
455                 [EditorBrowsable(EditorBrowsableState.Advanced)]
456                 protected override void OnMouseWheel(MouseEventArgs e) {
457                         if (vscroll_visible) {
458                                 if (e.Delta > 0) {
459                                         if (vscrollbar.Minimum < (vscrollbar.Value - vscrollbar.LargeChange)) {
460                                                 vscrollbar.Value -= vscrollbar.LargeChange;
461                                         } else {
462                                                 vscrollbar.Value = vscrollbar.Minimum;
463                                         }
464                                 } else {
465                                         if (vscrollbar.Maximum > (vscrollbar.Value + vscrollbar.LargeChange)) {
466                                                         vscrollbar.Value += vscrollbar.LargeChange;
467                                                 } else {
468                                                         vscrollbar.Value = vscrollbar.Maximum;
469                                                 }
470                                         }
471                         }
472                         base.OnMouseWheel(e);
473                 }
474
475                 [EditorBrowsable(EditorBrowsableState.Advanced)]
476                 protected override void OnVisibleChanged(EventArgs e) {
477                         base.OnVisibleChanged(e);
478                 }
479
480                 protected override void ScaleCore(float dx, float dy) {
481                         base.ScaleCore(dx, dy);
482                 }
483
484                 protected void SetDisplayRectLocation(int x, int y) {
485                         throw new NotImplementedException();
486                 }
487
488                 protected void SetScrollState(int bit, bool value) {
489                         throw new NotImplementedException();
490                 }
491
492                 [EditorBrowsable(EditorBrowsableState.Advanced)]
493                 protected override void WndProc(ref Message m) {
494                         base.WndProc(ref m);
495                 }
496                 #endregion      // Protected Instance Methods
497
498                 #region Internal & Private Methods
499                 private Size Canvas {
500                         get {
501                                 int     num_of_children;
502                                 int     width;
503                                 int     height;
504
505                                 num_of_children = child_controls.Count;
506                                 width = 0;
507                                 height = 0;
508
509                                 for (int i = 0; i < num_of_children; i++) {
510                                         if ((child_controls[i].Visible == false) || (child_controls[i] == hscrollbar) || (child_controls[i] == vscrollbar) || (child_controls[i] == sizegrip)) {
511                                                 continue;
512                                         }
513                                         if (child_controls[i].Right > width) {
514                                                 width = child_controls[i].Right;
515                                         }
516
517                                         if (child_controls[i].Bottom > height) {
518                                                 height = child_controls[i].Bottom;
519                                         }
520                                 }
521
522                                 return new Size(width, height);
523                         }
524                 }
525
526                 private void Recalculate(object sender, EventArgs e) {
527                         Size    canvas;
528                         Size    client;
529
530                         // FIXME - this whole function begs for optimizations, all the math
531                         // shouldn't have to be done over and over
532
533                         // Check if we need scrollbars
534                         if (!this.auto_scroll && !force_hscroll_visible && !force_vscroll_visible) {
535                                 return;
536                         }
537
538                         canvas = Canvas;
539                         client = ClientRectangle.Size;
540
541                         canvas.Width += auto_scroll_margin.Width + SystemInformation.VerticalScrollBarWidth;
542                         canvas.Height += auto_scroll_margin.Height + SystemInformation.HorizontalScrollBarHeight;
543
544                         //  || (scroll_position.X == 0 && scroll_position.Y == 0)
545
546                         if ((canvas.Width >= client.Width) || (auto_scroll_min_size.Width > client.Width) || force_hscroll_visible) {
547                                 // Need horz
548
549                                 hscrollbar.Left = 0;
550                                 hscrollbar.Top = client.Height - SystemInformation.HorizontalScrollBarHeight;
551                                 hscrollbar.Maximum = Math.Max(0, canvas.Width - client.Width + SystemInformation.VerticalScrollBarWidth);
552
553                                 hscroll_visible = true;
554                         } else {
555                                 hscroll_visible = false;
556                                 scroll_position.X = 0;
557                         }
558
559                         if ((canvas.Height >= client.Height) || (auto_scroll_min_size.Height > client.Height) || force_vscroll_visible) {
560                                 // Need vert
561                                 vscrollbar.Left = client.Width - SystemInformation.VerticalScrollBarWidth;
562                                 vscrollbar.Top = 0;
563
564                                 // FIXME - Working around some scrollbar bugs here; shouldn't have to add the height again (see canvas+= above)
565                                 vscrollbar.Maximum = Math.Max(0, canvas.Height - client.Height + SystemInformation.HorizontalScrollBarHeight);
566                                 vscroll_visible = true;
567                         } else {
568                                 vscroll_visible = false;
569                                 scroll_position.Y = 0;
570                         }
571
572                         if (hscroll_visible && vscroll_visible) {
573                                 hscrollbar.Width = ClientRectangle.Width - SystemInformation.VerticalScrollBarWidth;
574                                 vscrollbar.Height = ClientRectangle.Height - SystemInformation.HorizontalScrollBarHeight;
575
576                                 sizegrip.Left =  hscrollbar.Right;
577                                 sizegrip.Top =  vscrollbar.Bottom;
578                                 sizegrip.Width = SystemInformation.VerticalScrollBarWidth;
579                                 sizegrip.Height = SystemInformation.HorizontalScrollBarHeight;
580
581                                 hscrollbar.Visible = true;
582                                 vscrollbar.Visible = true;
583                                 sizegrip.Visible = true;
584                         } else {
585                                 sizegrip.Visible = false;
586                                 if (hscroll_visible) {
587                                         hscrollbar.Width = ClientRectangle.Width;
588                                         hscrollbar.Visible = true;
589                                 } else {
590                                         hscrollbar.Visible = false;
591                                 }
592
593                                 if (vscroll_visible) {
594                                         vscrollbar.Height = ClientRectangle.Height;
595                                         vscrollbar.Visible = true;
596                                 } else {
597                                         vscrollbar.Visible = false;
598                                 }
599                         }
600                 }
601
602                 private void HandleScrollBar(object sender, EventArgs e) {
603                         if (sender == vscrollbar) {
604                                 ScrollWindow(0, vscrollbar.Value- scroll_position.Y);
605                         } else {
606                                 ScrollWindow(hscrollbar.Value - scroll_position.X, 0);
607                         }
608                 }
609
610                 private void ScrollWindow(int XOffset, int YOffset) {
611                         int     num_of_children;
612
613                         SuspendLayout();
614
615                         num_of_children = child_controls.Count;
616
617                         for (int i = 0; i < num_of_children; i++) {
618                                 if (child_controls[i] == hscrollbar || child_controls[i] == vscrollbar || child_controls[i] == sizegrip) {
619                                         continue;
620                                 }
621                                 child_controls[i].Left -= XOffset;
622                                 child_controls[i].Top -= YOffset;
623                                 // Is this faster? child_controls[i].Location -= new Size(XOffset, YOffset);
624                         }
625
626                         scroll_position.X += XOffset;
627                         scroll_position.Y += YOffset;
628
629                         // Should we call XplatUI.ScrollWindow???
630                         Invalidate();
631                         ResumeLayout();
632                 }
633                 #endregion      // Internal & Private Methods
634
635         }
636 }