Merge pull request #268 from pcc/menudeactivate
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / PictureBox.cs
index 08f12f2d1bb13053932a6cd242858fc8b20da210..53b1dcce1795c77abd7da32822ce4db0af666846 100644 (file)
@@ -31,27 +31,49 @@ using System.ComponentModel.Design;
 using System.Drawing;
 using System.Drawing.Imaging;
 using System.Runtime.InteropServices;
+using System.IO;
+using System.Net;
 
 namespace System.Windows.Forms {
        [DefaultProperty("Image")]
-       [Designer("System.Windows.Forms.Design.PictureBoxDesigner, " + Consts.AssemblySystem_Design)]
-       public class PictureBox : Control {
-
-               private Image image;
+       [Designer("System.Windows.Forms.Design.PictureBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
+       [Docking (DockingBehavior.Ask)]
+       [ClassInterface (ClassInterfaceType.AutoDispatch)]
+       [ComVisible (true)]
+       [DefaultBindingProperty ("Image")]
+       public class PictureBox : Control, ISupportInitialize
+       {
+               #region Fields
+               private Image   image;
                private PictureBoxSizeMode size_mode;
-               private bool redraw;
-               private bool recalc;
-               private bool allow_drop;
+               private Image   error_image;
+               private string  image_location;
+               private Image   initial_image;
+               private bool    wait_on_load;
+               private WebClient image_download;
+               private bool image_from_url;
+               private int     no_update;
+               #endregion      // Fields
 
                private EventHandler frame_handler;
 
+               #region Public Constructor
                public PictureBox ()
                {
-                       redraw = true;
-                       recalc = true;
-                       allow_drop = false;
+                       //recalc = true;
+                       no_update = 0;
+
+                       SetStyle (ControlStyles.OptimizedDoubleBuffer, true);
+                       SetStyle (ControlStyles.Opaque, false);
+                       SetStyle (ControlStyles.Selectable, false);
+                       SetStyle (ControlStyles.SupportsTransparentBackColor, true);
+                       HandleCreated += new EventHandler(PictureBox_HandleCreated);
+                       initial_image = ResourceImageLoader.Get ("image-x-generic.png");
+                       error_image = ResourceImageLoader.Get ("image-missing.png");
                }
+               #endregion      // Public Constructor
 
+               #region Public Properties
                [DefaultValue(PictureBoxSizeMode.Normal)]
                [Localizable(true)]
                [RefreshProperties(RefreshProperties.Repaint)]
@@ -61,40 +83,36 @@ namespace System.Windows.Forms {
                                if (size_mode == value)
                                        return;
                                size_mode = value;
+                               
+                               if (size_mode == PictureBoxSizeMode.AutoSize) {
+                                       AutoSize = true;
+                                       SetAutoSizeMode (AutoSizeMode.GrowAndShrink);
+                               } else {
+                                       AutoSize = false;
+                                       SetAutoSizeMode (AutoSizeMode.GrowOnly);
+                               }
+
                                UpdateSize ();
-                               Redraw (true);
-                               Invalidate ();
+                               if (no_update == 0) {
+                                       Invalidate ();
+                               }
 
                                OnSizeModeChanged (EventArgs.Empty);
                        }
                }
 
-               [DefaultValue(null)]
+               [Bindable (true)]
                [Localizable(true)]
                public Image Image {
                        get { return image; }
-                       set {
-                               StopAnimation ();
-
-                               image = value;
-                               UpdateSize ();
-                               if (image != null && ImageAnimator.CanAnimate (image)) {
-                                       frame_handler = new EventHandler (OnAnimateImage);
-                                       ImageAnimator.Animate (image, frame_handler);
-                               }
-                               Redraw (true);
-                               Invalidate ();
-                       }
+                       set { ChangeImage (value, false); }
                }
 
                [DefaultValue(BorderStyle.None)]
                [DispId(-504)]
                public BorderStyle BorderStyle {
-                       get { return border_style; }
-                       set {
-                               border_style = value;
-                               Redraw (true);
-                       }
+                       get { return InternalBorderStyle; }
+                       set { InternalBorderStyle = value; }
                }
 
                [Browsable(false)]
@@ -104,6 +122,45 @@ namespace System.Windows.Forms {
                        set { base.CausesValidation = value; }
                }
 
+               [Localizable (true)]
+               [RefreshProperties (RefreshProperties.All)]
+               public Image ErrorImage {
+                       get { return error_image; }
+                       set { error_image = value; }
+               }
+               
+               [RefreshProperties (RefreshProperties.All)]
+               [Localizable(true)]
+               public Image InitialImage {
+                       get { return initial_image; }
+                       set { initial_image = value; }
+               }
+               
+               [Localizable (true)]
+               [DefaultValue (null)]
+               [RefreshProperties (RefreshProperties.All)]
+               public string ImageLocation {
+                       get { return image_location; }
+                       set {
+                               image_location = value;
+
+                               if (!string.IsNullOrEmpty (value)) {
+                                       if (WaitOnLoad)
+                                               Load (value);
+                                       else
+                                               LoadAsync (value);
+                               } else if (image_from_url)
+                                       ChangeImage (null, true);
+                       }
+               }
+               
+               [Localizable (true)]
+               [DefaultValue (false)]
+               public bool WaitOnLoad {
+                       get { return wait_on_load; }
+                       set { wait_on_load = value; }
+               }
+
                [Browsable(false)]
                [EditorBrowsable(EditorBrowsableState.Never)]
                public new ImeMode ImeMode {
@@ -167,15 +224,10 @@ namespace System.Windows.Forms {
                [Browsable(false)]
                [EditorBrowsable(EditorBrowsableState.Never)]
                public override bool AllowDrop {
-                       get {
-                               return allow_drop;
-                       }
-                       set {
-                               if (allow_drop != value) {
-                                       allow_drop = value;
-                               }
-                       }
+                       get { return base.AllowDrop; }
+                       set { base.AllowDrop = value; }
                }
+               #endregion      // Public Properties
 
                #region Protected Instance Methods
                protected override Size DefaultSize {
@@ -186,33 +238,29 @@ namespace System.Windows.Forms {
                {
                        if (image != null) {
                                StopAnimation ();
-                               image.Dispose ();
                                image = null;
                        }
+                       initial_image = null;
+
                        base.Dispose (disposing);
                }
 
                protected override void OnPaint (PaintEventArgs pe)
                {
+                       ThemeEngine.Current.DrawPictureBox (pe.Graphics, pe.ClipRectangle, this);
                        base.OnPaint (pe);
-
-                       if (this.Width <= 0 || this.Height <=  0 || this.Visible == false)
-                               return;
-
-                       Draw ();
-                       pe.Graphics.DrawImage (this.ImageBuffer, pe.ClipRectangle, pe.ClipRectangle, GraphicsUnit.Pixel);
                }
 
                protected override void OnVisibleChanged (EventArgs e)
                {
                        base.OnVisibleChanged (e);
-                       Redraw (true);
                }
 
                protected virtual void OnSizeModeChanged (EventArgs e)
                {
-                       if (SizeModeChanged != null)
-                               SizeModeChanged (this, e);
+                       EventHandler eh = (EventHandler)(Events [SizeModeChangedEvent]);
+                       if (eh != null)
+                               eh (this, e);
                }
 
                protected override void OnEnabledChanged (EventArgs e)
@@ -220,6 +268,32 @@ namespace System.Windows.Forms {
                        base.OnEnabledChanged (e);
                }
 
+               [EditorBrowsable (EditorBrowsableState.Advanced)]
+               protected override void OnHandleCreated (EventArgs e)
+               {
+                       base.OnHandleCreated (e);
+               }
+
+               [EditorBrowsable (EditorBrowsableState.Advanced)]
+               protected override void OnHandleDestroyed (EventArgs e)
+               {
+                       base.OnHandleDestroyed (e);
+               }
+               
+               protected virtual void OnLoadCompleted (AsyncCompletedEventArgs e)
+               {
+                       AsyncCompletedEventHandler eh = (AsyncCompletedEventHandler)(Events[LoadCompletedEvent]);
+                       if (eh != null)
+                               eh (this, e);
+               }
+               
+               protected virtual void OnLoadProgressChanged (ProgressChangedEventArgs e)
+               {
+                       ProgressChangedEventHandler eh = (ProgressChangedEventHandler)(Events[LoadProgressChangedEvent]);
+                       if (eh != null)
+                               eh (this, e);
+               }
+
                protected override void OnParentChanged (EventArgs e)
                {
                        base.OnParentChanged (e);
@@ -228,23 +302,66 @@ namespace System.Windows.Forms {
                protected override void OnResize (EventArgs e)
                {
                        base.OnResize (e);
-                       redraw = true;
+                       
+                       Invalidate ();
+               }
+
+               internal override Size GetPreferredSizeCore (Size proposedSize)
+               {
+                       if (image == null)
+                               return base.GetPreferredSizeCore (proposedSize);
+                       else
+                               return image.Size;
+               }
+               #endregion      // Protected Instance Methods
+
+               #region ISupportInitialize Interface
+               void System.ComponentModel.ISupportInitialize.BeginInit() {
+                       no_update++;
+               }
 
-                       if (size_mode == PictureBoxSizeMode.CenterImage || size_mode == PictureBoxSizeMode.StretchImage)
-                               Refresh ();
+               void System.ComponentModel.ISupportInitialize.EndInit() {
+                       if (no_update > 0) {
+                               no_update--;
+                       }
+                       if (no_update == 0) {
+                               Invalidate ();
+                       }
                }
+               #endregion      // ISupportInitialize Interface
+
+               #region Private Properties
+               private WebClient ImageDownload {
+                       get { 
+                               if (image_download == null)
+                                       image_download = new WebClient ();
+                                       
+                               return image_download;
+                       }
+               }
+               #endregion
+               
+               #region Private Methods
 
-               protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
+               private void ChangeImage (Image value, bool from_url)
                {
-                       if (size_mode == PictureBoxSizeMode.AutoSize && image != null) {
-                               width = image.Width;
-                               height = image.Height;
+                       StopAnimation ();
+
+                       image_from_url = from_url;
+                       image = value;
+
+                       if (IsHandleCreated) {
+                               UpdateSize ();
+                               if (image != null && ImageAnimator.CanAnimate (image)) {
+                                       frame_handler = new EventHandler (OnAnimateImage);
+                                       ImageAnimator.Animate (image, frame_handler);
+                               }
+                               if (no_update == 0) {
+                                       Invalidate ();
+                               }
                        }
-                       base.SetBoundsCore (x, y, width, height, specified);
                }
-               #endregion      // Protected Instance Methods
 
-               #region Private Methods
                private void StopAnimation ()
                {
                        if (frame_handler == null)
@@ -257,43 +374,129 @@ namespace System.Windows.Forms {
                {
                        if (image == null)
                                return;
-                       if (size_mode == PictureBoxSizeMode.AutoSize)
-                               ClientSize = image.Size; 
-               }
 
-               private void Redraw (bool recalc)
-               {
-                       redraw = true;
-                       this.recalc = recalc;
+                       if (Parent != null)
+                               Parent.PerformLayout (this, "AutoSize");
                }
 
                private void OnAnimateImage (object sender, EventArgs e)
                {
                        // This is called from a worker thread,BeginInvoke is used
                        // so the control is updated from the correct thread
+                       
+                       // Check if we have a handle again, since it may have gotten
+                       // destroyed since the last time we checked.
+                       if (!IsHandleCreated)
+                               return;
+                       
                        BeginInvoke (new EventHandler (UpdateAnimatedImage), new object [] { this, e });
                }
 
                private void UpdateAnimatedImage (object sender, EventArgs e)
                {
+                       // Check if we have a handle again, since it may have gotten
+                       // destroyed since the last time we checked.
+                       if (!IsHandleCreated)
+                               return;
+                               
                        ImageAnimator.UpdateFrames (image);
-                       Redraw (false);
                        Refresh ();
                }
 
-               [MonoTODO ("Borders and stuff, and move into the Theme")]
-               private void Draw ()
-               {
-                       if (redraw) {
-                               ThemeEngine.Current.DrawPictureBox (DeviceContext, this);
+               private void PictureBox_HandleCreated(object sender, EventArgs e) {
+                       UpdateSize ();
+                       if (image != null && ImageAnimator.CanAnimate (image)) {
+                               frame_handler = new EventHandler (OnAnimateImage);
+                               ImageAnimator.Animate (image, frame_handler);
+                       }
+                       if (no_update == 0) {
+                               Invalidate ();
                        }
-                       redraw = false;
+               }
+
+               void ImageDownload_DownloadDataCompleted (object sender, DownloadDataCompletedEventArgs e)
+               {
+                       if (e.Error != null && !e.Cancelled)
+                               Image = error_image;
+                       else if (e.Error == null && !e.Cancelled)
+                               using (MemoryStream ms = new MemoryStream (e.Result))
+                                       Image = Image.FromStream (ms);
+                                       
+                       ImageDownload.DownloadProgressChanged -= new DownloadProgressChangedEventHandler (ImageDownload_DownloadProgressChanged);
+                       ImageDownload.DownloadDataCompleted -= new DownloadDataCompletedEventHandler (ImageDownload_DownloadDataCompleted);
+                       image_download = null;
+                       
+                       OnLoadCompleted (e);
+               }
+
+               private void ImageDownload_DownloadProgressChanged (object sender, DownloadProgressChangedEventArgs e)
+               {
+                       OnLoadProgressChanged (new ProgressChangedEventArgs (e.ProgressPercentage, e.UserState));
                }
                #endregion      // Private Methods
 
                #region Public Instance Methods
+               public void CancelAsync ()
+               {
+                       if (image_download != null)
+                               image_download.CancelAsync ();
+               }
+               
+               public void Load ()
+               {
+                       Load (image_location);
+               }
+               
+               public void Load (string url)
+               {
+                       if (string.IsNullOrEmpty (url))
+                               throw new InvalidOperationException ("ImageLocation not specified.");
+                       
+                       image_location = url;
+                       
+                       if (url.Contains ("://"))
+                               using (Stream s = ImageDownload.OpenRead (url))
+                                       ChangeImage (Image.FromStream (s), true);
+                       else
+                               ChangeImage (Image.FromFile (url), true);
+               }
+               
+               public void LoadAsync ()
+               {
+                       LoadAsync (image_location);
+               }
+               
+               public void LoadAsync (string url)
+               {
+                       // If WaitOnLoad is true, do not do async
+                       if (wait_on_load) {
+                               Load (url);
+                               return;
+                       }
+
+                       if (string.IsNullOrEmpty (url))
+                               throw new InvalidOperationException ("ImageLocation not specified.");
+
+                       image_location = url;
+                       ChangeImage (InitialImage, true);
+                       
+                       if (ImageDownload.IsBusy)
+                               ImageDownload.CancelAsync ();
+
+                       Uri uri = null;
+                       try {
+                               uri = new Uri (url);
+                       } catch (UriFormatException) {
+                               uri = new Uri (Path.GetFullPath (url));
+                       }
+
+                       ImageDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler (ImageDownload_DownloadProgressChanged);
+                       ImageDownload.DownloadDataCompleted += new DownloadDataCompletedEventHandler (ImageDownload_DownloadDataCompleted);
+                       ImageDownload.DownloadDataAsync (uri);
+               }
+
                public override string ToString() {
-                       return base.ToString ();
+                       return String.Format("{0}, SizeMode: {1}", base.ToString (), SizeMode);
                }
                #endregion
 
@@ -361,6 +564,19 @@ namespace System.Windows.Forms {
                        remove { base.Leave -= value; }
                }
 
+               static object LoadCompletedEvent = new object ();
+               static object LoadProgressChangedEvent = new object ();
+
+               public event AsyncCompletedEventHandler LoadCompleted {
+                       add { Events.AddHandler (LoadCompletedEvent, value); }
+                       remove { Events.RemoveHandler (LoadCompletedEvent, value); }
+               }
+
+               public event ProgressChangedEventHandler LoadProgressChanged {
+                       add { Events.AddHandler (LoadProgressChangedEvent, value); }
+                       remove { Events.RemoveHandler (LoadProgressChangedEvent, value); }
+               }
+
                [Browsable(false)]
                [EditorBrowsable(EditorBrowsableState.Never)]
                public new event EventHandler RightToLeftChanged {
@@ -389,9 +605,13 @@ namespace System.Windows.Forms {
                        remove { base.TextChanged -= value; }
                }
 
-               public event EventHandler SizeModeChanged;
-               #endregion      // Events
+               static object SizeModeChangedEvent = new object ();
+               public event EventHandler SizeModeChanged {
+                       add { Events.AddHandler (SizeModeChangedEvent, value); }
+                       remove { Events.RemoveHandler (SizeModeChangedEvent, value); }
+               }
 
+               #endregion      // Events
        }
 }