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 		{
ErrorWindow()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 
ErrorProperty(ErrorProvider ep, Control control)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 
CalculateAlignment()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 
window_Paint(object sender, PaintEventArgs e)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 
window_MouseEnter(object sender, EventArgs e)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 
window_MouseLeave(object sender, EventArgs e)250 			private void window_MouseLeave(object sender, EventArgs e) {
251 				if (visible) {
252 					visible = false;
253 					ep.tooltip.Visible = false;
254 				}
255 			}
256 
control_SizeLocationChanged(object sender, EventArgs e)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 
control_ParentChanged(object sender, EventArgs e)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 
window_Tick(object sender, EventArgs e)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
ErrorProvider()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 
ErrorProvider(ContainerControl parentControl)346 		public ErrorProvider(ContainerControl parentControl) : this ()
347 		{
348 			container = parentControl;
349 		}
350 
ErrorProvider(IContainer container)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")]
BindToDataAndErrors(object newDataSource, string newDataMember)461 		public void BindToDataAndErrors (object newDataSource, string newDataMember)
462 		{
463 			datasource = newDataSource;
464 			datamember = newDataMember;
465 			// FIXME - finish
466 		}
467 
CanExtend(object extendee)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 
Clear()480 		public void Clear ()
481 		{
482 			foreach (ErrorProperty ep in controls.Values)
483 				ep.Text = string.Empty;
484 		}
485 
486 		[Localizable(true)]
487 		[DefaultValue("")]
GetError(Control control)488 		public string GetError(Control control) {
489 			return GetErrorProperty(control).Text;
490 		}
491 
492 		[Localizable(true)]
493 		[DefaultValue(ErrorIconAlignment.MiddleRight)]
GetIconAlignment(Control control)494 		public ErrorIconAlignment GetIconAlignment(Control control) {
495 			return GetErrorProperty(control).Alignment;
496 		}
497 
498 		[Localizable(true)]
499 		[DefaultValue(0)]
GetIconPadding(Control control)500 		public int GetIconPadding(Control control) {
501 			return GetErrorProperty(control).padding;
502 		}
503 
SetError(Control control, string value)504 		public void SetError(Control control, string value) {
505 			GetErrorProperty(control).Text = value;
506 		}
507 
SetIconAlignment(Control control, ErrorIconAlignment value)508 		public void SetIconAlignment(Control control, ErrorIconAlignment value) {
509 			GetErrorProperty(control).Alignment = value;
510 		}
511 
SetIconPadding(Control control, int padding)512 		public void SetIconPadding(Control control, int padding) {
513 			GetErrorProperty(control).Padding = padding;
514 		}
515 
516 		[MonoTODO ("Stub, does nothing")]
UpdateBinding()517 		public void UpdateBinding ()
518 		{
519 		}
520 		#endregion	// Public Instance Methods
521 
522 		#region Protected Instance Methods
Dispose(bool disposing)523 		protected override void Dispose(bool disposing) {
524 			base.Dispose (disposing);
525 		}
526 
527 		[EditorBrowsableAttribute (EditorBrowsableState.Advanced)]
OnRightToLeftChanged(EventArgs e)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
GetErrorProperty(Control control)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 
ISupportInitialize.BeginInit()547 		void ISupportInitialize.BeginInit ()
548 		{
549 		}
550 
ISupportInitialize.EndInit()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 
OnUIAPopup(ErrorProvider sender, PopupEventArgs args)594 		internal static void OnUIAPopup (ErrorProvider sender, PopupEventArgs args)
595 		{
596 			if (UIAPopup != null)
597 				UIAPopup (sender, args);
598 		}
599 
OnUIAUnPopup(ErrorProvider sender, PopupEventArgs args)600 		internal static void OnUIAUnPopup (ErrorProvider sender, PopupEventArgs args)
601 		{
602 			if (UIAUnPopup != null)
603 				UIAUnPopup (sender, args);
604 		}
605 
OnUIAControlHookUp(object sender, ControlEventArgs args)606 		internal static void OnUIAControlHookUp (object sender, ControlEventArgs args)
607 		{
608 			if (UIAControlHookUp != null)
609 				UIAControlHookUp (sender, args);
610 		}
611 
OnUIAControlUnhookUp(object sender, ControlEventArgs args)612 		internal static void OnUIAControlUnhookUp (object sender, ControlEventArgs args)
613 		{
614 			if (UIAControlUnhookUp != null)
615 				UIAControlUnhookUp (sender, args);
616 		}
617 
OnUIAErrorProviderHookUp(object sender, ControlEventArgs args)618 		internal static void OnUIAErrorProviderHookUp (object sender, ControlEventArgs args)
619 		{
620 			if (UIAErrorProviderHookUp != null)
621 				UIAErrorProviderHookUp (sender, args);
622 		}
623 
OnUIAErrorProviderUnhookUp(object sender, ControlEventArgs args)624 		internal static void OnUIAErrorProviderUnhookUp (object sender, ControlEventArgs args)
625 		{
626 			if (UIAErrorProviderUnhookUp != null)
627 				UIAErrorProviderUnhookUp (sender, args);
628 		}
629 		#endregion
630 	}
631 }
632