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