2008-12-06 Ivan N. Zlatev <contact@i-nz.net>
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ErrorProvider.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) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    (pbartok@novell.com)
24 //
25 //
26
27 using System;
28 using System.Collections;
29 using System.ComponentModel;
30 using System.Drawing;
31
32 namespace System.Windows.Forms {
33         [ToolboxItemFilter("System.Windows.Forms")]
34         [ProvideProperty("IconAlignment", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)]
35         [ProvideProperty("IconPadding", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)]
36         [ProvideProperty("Error", "System.Windows.Forms.Control, " + Consts.AssemblySystem_Windows_Forms)]
37 #if NET_2_0
38         [ComplexBindingProperties ("DataSource", "DataMember")]
39 #endif
40         public class ErrorProvider : Component, IExtenderProvider
41 #if NET_2_0
42             , ISupportInitialize
43 #endif
44         {
45                 private class ErrorWindow : UserControl
46                 {
47                         public ErrorWindow ()
48                         {
49                                 SetStyle (ControlStyles.Selectable, false);
50                         }
51                 }
52
53                 #region Private Classes
54                 private class ErrorProperty {
55                         public ErrorIconAlignment       alignment;
56                         public int                      padding;
57                         public string                   text;
58                         public Control                  control;
59                         public ErrorProvider            ep;
60                         private ErrorWindow             window;
61                         private bool                    visible;
62                         private int                     blink_count;
63                         private EventHandler            tick;
64                         private System.Windows.Forms.Timer      timer;
65
66                         public ErrorProperty(ErrorProvider ep, Control control) {
67                                 this.ep = ep;
68                                 this.control = control;
69
70                                 alignment = ErrorIconAlignment.MiddleRight;
71                                 padding = 0;
72                                 text = string.Empty;
73                                 blink_count = 0;
74
75                                 tick = new EventHandler(window_Tick);
76
77                                 window = new ErrorWindow ();
78                                 window.Visible = false;
79                                 window.Width = ep.icon.Width;
80                                 window.Height = ep.icon.Height;
81
82 #if NET_2_0
83                                 // UIA Framework: Associate ErrorProvider with Control
84                                 ErrorProvider.OnUIAErrorProviderHookUp (ep, new ControlEventArgs (control));
85
86                                 // UIA Framework: Generate event to associate UserControl with ErrorProvider
87                                 window.VisibleChanged += delegate (object sender, EventArgs args) {
88                                         if (window.Visible == true)
89                                                 ErrorProvider.OnUIAControlHookUp (control, new ControlEventArgs (window));
90                                         else 
91                                                 ErrorProvider.OnUIAControlUnhookUp (control, new ControlEventArgs (window));
92                                 };
93 #endif
94
95                                 if (control.Parent != null) {
96 #if NET_2_0
97                                         // UIA Framework: Generate event to associate UserControl with ErrorProvider
98                                         ErrorProvider.OnUIAControlHookUp (control, new ControlEventArgs (window));
99 #endif
100                                         control.Parent.Controls.Add(window);
101                                         control.Parent.Controls.SetChildIndex(window, control.Parent.Controls.IndexOf (control) + 1);
102                                 }
103
104                                 window.Paint += new PaintEventHandler(window_Paint);
105                                 window.MouseEnter += new EventHandler(window_MouseEnter);
106                                 window.MouseLeave += new EventHandler(window_MouseLeave);
107                                 control.SizeChanged += new EventHandler(control_SizeLocationChanged);
108                                 control.LocationChanged += new EventHandler(control_SizeLocationChanged);
109                                 control.ParentChanged += new EventHandler (control_ParentChanged);
110                                 // Do we want to block mouse clicks? if so we need a few more events handled
111
112                                 CalculateAlignment();
113                         }
114
115                         public string Text {
116                                 get {
117                                         return text;
118                                 }
119
120                                 set {
121                                         if (value == null)
122                                                 value = string.Empty;
123
124                                         bool differentError = text != value;
125                                         text = value;
126
127                                         if (text != String.Empty) {
128                                                 window.Visible = true;
129                                         } else {
130                                                 window.Visible = false;
131                                                 return;
132                                         }
133
134                                         // even if blink style is NeverBlink we need it to allow
135                                         // the timer to elapse at least once to get the icon to 
136                                         // display
137                                         if (differentError || ep.blinkstyle == ErrorBlinkStyle.AlwaysBlink) {
138                                                 if (timer == null) {
139                                                         timer = new System.Windows.Forms.Timer();
140                                                         timer.Tick += tick;
141                                                 }
142                                                 timer.Interval = ep.blinkrate;
143                                                 blink_count = 0;
144                                                 timer.Enabled = true;
145                                         }
146                                 }
147                         }
148
149                         public ErrorIconAlignment Alignment {
150                                 get {
151                                         return alignment;
152                                 }
153
154                                 set {
155                                         if (alignment != value) {
156                                                 alignment = value;
157                                                 CalculateAlignment();
158                                         }
159                                 }
160                         }
161
162                         public int Padding {
163                                 get {
164                                         return padding;
165                                 }
166
167                                 set {
168                                         if (padding != value) {
169                                                 padding = value;
170                                                 CalculateAlignment();
171                                         }
172                                 }
173                         }
174
175                         private void CalculateAlignment() {
176                                 if (visible) {
177                                         visible = false;
178                                         ep.tooltip.Visible = false;
179                                 }
180
181                                 switch (alignment) {
182                                         case ErrorIconAlignment.TopLeft: {
183                                                 window.Left = control.Left - ep.icon.Width - padding;
184                                                 window.Top = control.Top;
185                                                 break;
186                                         }
187
188                                         case ErrorIconAlignment.TopRight: {
189                                                 window.Left = control.Left + control.Width + padding;
190                                                 window.Top = control.Top;
191                                                 break;
192                                         }
193
194                                         case ErrorIconAlignment.MiddleLeft: {
195                                                 window.Left = control.Left - ep.icon.Width - padding;
196                                                 window.Top = control.Top + (control.Height - ep.icon.Height) / 2;
197                                                 break;
198                                         }
199
200                                         case ErrorIconAlignment.MiddleRight: {
201                                                 window.Left = control.Left + control.Width + padding;
202                                                 window.Top = control.Top + (control.Height - ep.icon.Height) / 2;
203                                                 break;
204                                         }
205
206                                         case ErrorIconAlignment.BottomLeft: {
207                                                 window.Left = control.Left - ep.icon.Width - padding;
208                                                 window.Top = control.Top + control.Height - ep.icon.Height;
209                                                 break;
210                                         }
211
212                                         case ErrorIconAlignment.BottomRight: {
213                                                 window.Left = control.Left + control.Width + padding;
214                                                 window.Top = control.Top + control.Height - ep.icon.Height;
215                                                 break;
216                                         }
217                                 }
218                         }
219
220                         private void window_Paint(object sender, PaintEventArgs e) {
221                                 if (text != string.Empty) {
222                                         e.Graphics.DrawIcon(this.ep.icon, 0, 0);
223                                 }
224                         }
225
226                         private void window_MouseEnter(object sender, EventArgs e) {
227                                 if (!visible) {
228                                         Size    size;
229                                         Point   pt;
230
231                                         visible = true;
232
233                                         pt = Control.MousePosition;
234
235                                         size = ThemeEngine.Current.ToolTipSize(ep.tooltip, text);
236                                         ep.tooltip.Width = size.Width;
237                                         ep.tooltip.Height = size.Height;
238                                         ep.tooltip.Text = text;
239
240                                         if ((pt.X + size.Width) < SystemInformation.WorkingArea.Width) {
241                                                 ep.tooltip.Left = pt.X;
242                                         } else {
243                                                 ep.tooltip.Left = pt.X - size.Width;
244                                         }
245
246                                         if ((pt.Y + size.Height) < (SystemInformation.WorkingArea.Height - 16)) {
247                                                 ep.tooltip.Top = pt.Y + 16;
248                                         } else {
249                                                 ep.tooltip.Top = pt.Y - size.Height;
250                                         }
251
252 #if NET_2_0
253                                         // UIA Framework: Associate Control with ToolTip, used on Popup events
254                                         ep.UIAControl = control;
255 #endif
256
257                                         ep.tooltip.Visible = true;
258                                 }
259                         }
260
261                         private void window_MouseLeave(object sender, EventArgs e) {
262                                 if (visible) {
263                                         visible = false;
264                                         ep.tooltip.Visible = false;
265                                 }
266                         }
267
268                         private void control_SizeLocationChanged(object sender, EventArgs e) {
269                                 if (visible) {
270                                         visible = false;
271                                         ep.tooltip.Visible = false;
272                                 }
273                                 CalculateAlignment();
274                         }
275
276                         private void control_ParentChanged (object sender, EventArgs e)
277                         {
278                                 if (control.Parent != null) {
279 #if NET_2_0
280
281                                         // UIA Framework: Generate event to disassociate UserControl with ErrorProvider
282                                         ErrorProvider.OnUIAControlUnhookUp (control, new ControlEventArgs (window));
283 #endif
284                                         control.Parent.Controls.Add (window);
285                                         control.Parent.Controls.SetChildIndex (window, control.Parent.Controls.IndexOf (control) + 1);
286         
287 #if NET_2_0
288                                         // UIA Framework: Generate event to associate UserControl with ErrorProvider
289                                         ErrorProvider.OnUIAControlHookUp (control, new ControlEventArgs (window));
290 #endif
291                                 }
292                         }
293
294                         private void window_Tick(object sender, EventArgs e) {
295                                 if (timer.Enabled && control.IsHandleCreated && control.Visible) {
296                                         Graphics g;
297
298                                         blink_count++;
299
300                                         g = window.CreateGraphics();
301                                         if ((blink_count % 2) == 0) {
302                                                 g.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(window.Parent.BackColor), window.ClientRectangle);
303                                         } else {
304                                                 g.DrawIcon(this.ep.icon, 0, 0);
305                                         }
306                                         g.Dispose();
307
308                                         switch (ep.blinkstyle) {
309                                         case ErrorBlinkStyle.AlwaysBlink:
310                                                 break;
311                                         case ErrorBlinkStyle.BlinkIfDifferentError:
312                                                 if (blink_count > 10)
313                                                         timer.Stop();
314                                                 break;
315                                         case ErrorBlinkStyle.NeverBlink:
316                                                 timer.Stop ();
317                                                 break;
318                                         }
319
320                                         if (blink_count == 11)
321                                                 blink_count = 1;
322                                 }
323                         }
324                 }
325                 #endregion
326
327                 #region Local Variables
328                 private int                     blinkrate;
329                 private ErrorBlinkStyle         blinkstyle;
330                 private string                  datamember;
331                 private object                  datasource;
332                 private ContainerControl        container;
333                 private Icon                    icon;
334                 private Hashtable               controls;
335                 private ToolTip.ToolTipWindow   tooltip;
336
337 #if NET_2_0
338                 private bool right_to_left;
339                 private object tag;
340 #endif
341                 #endregion      // Local Variables
342
343                 #region Public Constructors
344                 public ErrorProvider()
345                 {
346                         controls = new Hashtable();
347
348                         blinkrate = 250;
349                         blinkstyle = ErrorBlinkStyle.BlinkIfDifferentError;
350
351                         icon = ResourceImageLoader.GetIcon ("errorProvider.ico");
352                         tooltip = new ToolTip.ToolTipWindow();
353 #if NET_2_0
354                         //UIA Framework: Event used to indicate the ToolTip is shown/hidden.
355                         tooltip.VisibleChanged += delegate (object sender, EventArgs args) {
356                                 if (tooltip.Visible == true)
357                                         OnUIAPopup (this, new PopupEventArgs (UIAControl, UIAControl, false, Size.Empty));
358                                 else if (tooltip.Visible == false)
359                                         OnUIAUnPopup (this, new PopupEventArgs (UIAControl, UIAControl, false, Size.Empty));
360                         };
361 #endif
362                 }
363
364                 public ErrorProvider(ContainerControl parentControl) : this ()
365                 {
366                         container = parentControl;
367                 }
368                 
369 #if NET_2_0
370                 public ErrorProvider (IContainer container) : this ()
371                 {
372                         container.Add (this);
373                 }
374 #endif
375                 #endregion      // Public Constructors
376
377                 #region Public Instance Properties
378                 [DefaultValue(250)]
379                 [RefreshProperties(RefreshProperties.Repaint)]
380                 public int BlinkRate {
381                         get {
382                                 return blinkrate;
383                         }
384
385                         set {
386                                 blinkrate = value;
387                         }
388                 }
389
390                 [DefaultValue(ErrorBlinkStyle.BlinkIfDifferentError)]
391                 public ErrorBlinkStyle BlinkStyle {
392                         get {
393                                 return blinkstyle;
394                         }
395
396                         set {
397                                 blinkstyle = value;
398                         }
399                 }
400
401                 [DefaultValue(null)]
402                 public ContainerControl ContainerControl {
403                         get {
404                                 return container;
405                         }
406
407                         set {
408                                 container = value;
409                         }
410                 }
411
412                 [MonoTODO]
413                 [DefaultValue(null)]
414                 [Editor ("System.Windows.Forms.Design.DataMemberListEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
415                 public string DataMember {
416                         get {
417                                 return datamember;
418                         }
419
420                         set {
421                                 datamember = value;
422                                 // FIXME - add binding magic and also update BindToDataAndErrors with it
423                         }
424                 }
425
426                 [MonoTODO]
427                 [DefaultValue(null)]
428 #if NET_2_0
429                 [AttributeProvider (typeof (IListSource))]
430 #else
431                 [TypeConverter("System.Windows.Forms.Design.DataSourceConverter, " + Consts.AssemblySystem_Design)]
432 #endif
433                 public object DataSource {
434                         get {
435                                 return datasource;
436                         }
437
438                         set {
439                                 datasource = value;
440                                 // FIXME - add binding magic and also update BindToDataAndErrors with it
441                         }
442                 }
443
444                 [Localizable(true)]
445                 public Icon Icon {
446                         get {
447                                 return icon;
448                         }
449
450                         set {
451                                 if (value != null && (value.Height != 16 || value.Width != 16))
452                                         icon = new Icon (value, 16, 16);
453                                 else
454                                         icon = value;
455                         }
456                 }
457
458                 public override ISite Site {
459                         set {
460                                 base.Site = value;
461                         }
462                 }
463
464 #if NET_2_0
465                 [MonoTODO ("RTL not supported")]
466                 [Localizable (true)]
467                 [DefaultValue (false)]
468                 public virtual bool RightToLeft {
469                         get { return right_to_left; }
470                         set { right_to_left = value; }
471                 }
472
473                 [Localizable (false)]
474                 [Bindable (true)]
475                 [TypeConverter (typeof (StringConverter))]
476                 [DefaultValue (null)]
477                 [MWFCategory ("Data")]
478                 public object Tag {
479                         get { return this.tag; }
480                         set { this.tag = value; }
481                 }
482 #endif
483                 #endregion      // Public Instance Properties
484
485                 #region Public Instance Methods
486                 [MonoTODO]
487                 public void BindToDataAndErrors(object newDataSource, string newDataMember) {
488                         datasource = newDataSource;
489                         datamember = newDataMember;
490                         // FIXME - finish
491                 }
492
493                 public bool CanExtend(object extendee) {
494                         if (!(extendee is Control)) {
495                                 return false;
496                         }
497
498                         if ((extendee is Form) || (extendee is ToolBar)) {
499                                 return false;
500                         }
501
502                         return true;
503                 }
504
505 #if NET_2_0
506                 public void Clear ()
507                 {
508                         foreach (ErrorProperty ep in controls.Values)
509                                 ep.Text = string.Empty;
510                 }
511 #endif
512
513                 [Localizable(true)]
514                 [DefaultValue("")]
515                 public string GetError(Control control) {
516                         return GetErrorProperty(control).Text;
517                 }
518
519                 [Localizable(true)]
520                 [DefaultValue(ErrorIconAlignment.MiddleRight)]
521                 public ErrorIconAlignment GetIconAlignment(Control control) {
522                         return GetErrorProperty(control).Alignment;
523                 }
524
525                 [Localizable(true)]
526                 [DefaultValue(0)]
527                 public int GetIconPadding(Control control) {
528                         return GetErrorProperty(control).padding;
529                 }
530
531                 public void SetError(Control control, string value) {
532                         GetErrorProperty(control).Text = value;
533                 }
534
535                 public void SetIconAlignment(Control control, ErrorIconAlignment value) {
536                         GetErrorProperty(control).Alignment = value;
537                 }
538
539                 public void SetIconPadding(Control control, int padding) {
540                         GetErrorProperty(control).Padding = padding;
541                 }
542
543                 [MonoTODO]
544                 public void UpdateBinding() {
545                 }
546                 #endregion      // Public Instance Methods
547
548                 #region Protected Instance Methods
549                 protected override void Dispose(bool disposing) {
550                         base.Dispose (disposing);
551                 }
552
553 #if NET_2_0
554                 [EditorBrowsableAttribute (EditorBrowsableState.Advanced)]
555                 protected virtual void OnRightToLeftChanged (EventArgs e)
556                 {
557                         EventHandler eh = (EventHandler)(Events[RightToLeftChangedEvent]);
558                         if (eh != null)
559                                 eh (this, e);
560                 }
561 #endif
562                 #endregion      // Protected Instance Methods
563
564                 #region Private Methods
565                 private ErrorProperty GetErrorProperty(Control control) {
566                         ErrorProperty ep = (ErrorProperty)controls[control];
567                         if (ep == null) {
568                                 ep = new ErrorProperty(this, control);
569                                 controls[control] = ep;
570                         }
571                         return ep;
572                 }
573                 #endregion      // Private Methods
574
575 #if NET_2_0
576                 void ISupportInitialize.BeginInit ()
577                 {
578                 }
579
580                 void ISupportInitialize.EndInit ()
581                 {
582                 }
583 #endif
584
585                 #region Public Events
586 #if NET_2_0
587                 static object RightToLeftChangedEvent = new object ();
588
589                 public event EventHandler RightToLeftChanged {
590                         add { Events.AddHandler (RightToLeftChangedEvent, value); }
591                         remove { Events.RemoveHandler (RightToLeftChangedEvent, value); }
592                 }
593 #endif
594                 #endregion
595
596                 #region UIA Framework: Events, Properties and Methods
597 #if NET_2_0
598                 // NOTE: 
599                 //      We are using Reflection to add/remove internal events.
600                 //      Class ToolTipListener uses the events.
601                 //
602                 //      - UIAControlHookUp. Event used to associate UserControl with ErrorProvider
603                 //      - UIAControlUnhookUp. Event used to disassociate UserControl with ErrorProvider
604                 //      - UIAErrorProviderHookUp. Event used to associate Control with ErrorProvider
605                 //      - UIAErrorProviderUnhookUp. Event used to disassociate Control with ErrorProvider
606                 //      - UIAPopup. Event used show Popup
607                 //      - UIAUnPopup. Event used to hide popup.
608
609                 private Control uia_control;
610
611                 internal Control UIAControl {
612                         get { return uia_control; }
613                         set { uia_control = value; }
614                 }
615
616                 internal Rectangle UIAToolTipRectangle {
617                         get { return tooltip.Bounds; }
618                 }
619
620                 internal static event ControlEventHandler UIAControlHookUp;
621                 internal static event ControlEventHandler UIAControlUnhookUp;
622                 internal static event ControlEventHandler UIAErrorProviderHookUp;
623                 internal static event ControlEventHandler UIAErrorProviderUnhookUp;
624                 internal static event PopupEventHandler UIAPopup;
625                 internal static event PopupEventHandler UIAUnPopup;
626
627                 internal static void OnUIAPopup (ErrorProvider sender, PopupEventArgs args)
628                 {
629                         if (UIAPopup != null)
630                                 UIAPopup (sender, args);
631                 }
632
633                 internal static void OnUIAUnPopup (ErrorProvider sender, PopupEventArgs args)
634                 {
635                         if (UIAUnPopup != null)
636                                 UIAUnPopup (sender, args);
637                 }
638
639                 internal static void OnUIAControlHookUp (object sender, ControlEventArgs args)
640                 {
641                         if (UIAControlHookUp != null)
642                                 UIAControlHookUp (sender, args);
643                 }
644
645                 internal static void OnUIAControlUnhookUp (object sender, ControlEventArgs args)
646                 {
647                         if (UIAControlUnhookUp != null)
648                                 UIAControlUnhookUp (sender, args);
649                 }
650
651                 internal static void OnUIAErrorProviderHookUp (object sender, ControlEventArgs args) 
652                 {
653                         if (UIAErrorProviderHookUp != null)
654                                 UIAErrorProviderHookUp (sender, args);
655                 }
656
657                 internal static void OnUIAErrorProviderUnhookUp (object sender, ControlEventArgs args) 
658                 {
659                         if (UIAErrorProviderUnhookUp != null)
660                                 UIAErrorProviderUnhookUp (sender, args);
661                 }
662
663 #endif
664                 #endregion
665         }
666 }