#region Disclaimer / License // Copyright (C) 2009, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using OSGeo.MapGuide.MaestroAPI; namespace OSGeo.MapGuide.Maestro { /// /// Form that displays progress for a lengthy operation. /// This implementation is really messy, and should be rewritten, /// using a BackgroundWorker. /// public class LengthyOperation : System.Windows.Forms.Form { public enum OperationType : int { MoveResource, MoveFolder, CopyFolder, Other, } private enum ProgressState { InitialOperation, ReferenceOperation, UpdateOperation } private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Button OKBtn; private System.Windows.Forms.Button CancelBtn; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Panel ProgressPanel; private System.Windows.Forms.Label label2; private System.Windows.Forms.ImageList progressImages; private System.Windows.Forms.Timer animationTimer; private System.ComponentModel.IContainer components; private System.Threading.Thread m_runner = null; private OSGeo.MapGuide.MaestroAPI.ServerConnectionI m_connection; private System.Reflection.MethodInfo m_callbackEnabledMethod; private object[] m_arguments; private ListViewItem m_currentItem = null; private System.Windows.Forms.ListView workList; private System.Windows.Forms.Label OperationDescription; private System.Threading.ManualResetEvent m_event; private System.Windows.Forms.ColumnHeader columnHeader1; private System.Windows.Forms.ProgressBar ProgressBar; private bool m_cancel = false; private System.Windows.Forms.Timer MarqueeTimer; private OperationType m_operation; private ProgressState m_state = ProgressState.InitialOperation; private bool m_cancelRequested = false; private bool m_waitingForUser = true; private object m_callbackobject = null; private bool m_noCancelConfirmation = false; private bool m_waitForAccept = true; public LengthyOperation() { // // Required for Windows Form Designer support // InitializeComponent(); this.Icon = FormMain.MaestroIcon; } /// /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LengthyOperation)); this.workList = new System.Windows.Forms.ListView(); this.columnHeader1 = new System.Windows.Forms.ColumnHeader(); this.progressImages = new System.Windows.Forms.ImageList(this.components); this.OperationDescription = new System.Windows.Forms.Label(); this.panel1 = new System.Windows.Forms.Panel(); this.CancelBtn = new System.Windows.Forms.Button(); this.OKBtn = new System.Windows.Forms.Button(); this.panel2 = new System.Windows.Forms.Panel(); this.ProgressPanel = new System.Windows.Forms.Panel(); this.label2 = new System.Windows.Forms.Label(); this.ProgressBar = new System.Windows.Forms.ProgressBar(); this.animationTimer = new System.Windows.Forms.Timer(this.components); this.MarqueeTimer = new System.Windows.Forms.Timer(this.components); this.panel1.SuspendLayout(); this.panel2.SuspendLayout(); this.ProgressPanel.SuspendLayout(); this.SuspendLayout(); // // workList // this.workList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.columnHeader1}); resources.ApplyResources(this.workList, "workList"); this.workList.FullRowSelect = true; this.workList.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; this.workList.Name = "workList"; this.workList.SmallImageList = this.progressImages; this.workList.UseCompatibleStateImageBehavior = false; this.workList.View = System.Windows.Forms.View.Details; this.workList.Resize += new System.EventHandler(this.workList_Resize); // // progressImages // this.progressImages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("progressImages.ImageStream"))); this.progressImages.TransparentColor = System.Drawing.Color.Transparent; this.progressImages.Images.SetKeyName(0, ""); this.progressImages.Images.SetKeyName(1, ""); this.progressImages.Images.SetKeyName(2, ""); this.progressImages.Images.SetKeyName(3, ""); this.progressImages.Images.SetKeyName(4, ""); this.progressImages.Images.SetKeyName(5, ""); this.progressImages.Images.SetKeyName(6, ""); this.progressImages.Images.SetKeyName(7, ""); this.progressImages.Images.SetKeyName(8, ""); this.progressImages.Images.SetKeyName(9, ""); this.progressImages.Images.SetKeyName(10, ""); // // OperationDescription // resources.ApplyResources(this.OperationDescription, "OperationDescription"); this.OperationDescription.Name = "OperationDescription"; // // panel1 // this.panel1.Controls.Add(this.CancelBtn); this.panel1.Controls.Add(this.OKBtn); resources.ApplyResources(this.panel1, "panel1"); this.panel1.Name = "panel1"; // // CancelBtn // resources.ApplyResources(this.CancelBtn, "CancelBtn"); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); // // OKBtn // resources.ApplyResources(this.OKBtn, "OKBtn"); this.OKBtn.Name = "OKBtn"; this.OKBtn.Click += new System.EventHandler(this.OKBtn_Click); // // panel2 // this.panel2.Controls.Add(this.OperationDescription); resources.ApplyResources(this.panel2, "panel2"); this.panel2.Name = "panel2"; // // ProgressPanel // this.ProgressPanel.Controls.Add(this.label2); this.ProgressPanel.Controls.Add(this.ProgressBar); resources.ApplyResources(this.ProgressPanel, "ProgressPanel"); this.ProgressPanel.Name = "ProgressPanel"; // // label2 // resources.ApplyResources(this.label2, "label2"); this.label2.Name = "label2"; // // ProgressBar // resources.ApplyResources(this.ProgressBar, "ProgressBar"); this.ProgressBar.Name = "ProgressBar"; // // animationTimer // this.animationTimer.Interval = 500; this.animationTimer.Tick += new System.EventHandler(this.animationTimer_Tick); // // MarqueeTimer // this.MarqueeTimer.Interval = 500; this.MarqueeTimer.Tick += new System.EventHandler(this.MarqueeTimer_Tick); // // LengthyOperation // resources.ApplyResources(this, "$this"); this.Controls.Add(this.workList); this.Controls.Add(this.ProgressPanel); this.Controls.Add(this.panel2); this.Controls.Add(this.panel1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; this.Name = "LengthyOperation"; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; this.Load += new System.EventHandler(this.LengthyOperation_Load); this.panel1.ResumeLayout(false); this.panel2.ResumeLayout(false); this.ProgressPanel.ResumeLayout(false); this.ResumeLayout(false); } #endregion private void animationTimer_Tick(object sender, System.EventArgs e) { if (m_currentItem == null) return; if (m_currentItem.ImageIndex < 8 && m_currentItem.ImageIndex >= 0) m_currentItem.ImageIndex = (m_currentItem.ImageIndex + 1) % 8; } public System.Reflection.MethodInfo CallbackEnabledMethod { get { return m_callbackEnabledMethod; } set { m_callbackEnabledMethod = value; } } public object CallbackObject { get { return m_callbackobject; } set { m_callbackobject = value; } } public void InitializeDialog(OSGeo.MapGuide.MaestroAPI.ServerConnectionI connection, string argument1, string argument2, OperationType operation, bool waitForAccept) { m_connection = connection; OSGeo.MapGuide.MaestroAPI.LengthyOperationCallBack callback = new OSGeo.MapGuide.MaestroAPI.LengthyOperationCallBack(CallbackMethod); OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressCallBack pgcallback = new OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressCallBack(WaitOperationCallBack); m_event = new System.Threading.ManualResetEvent(false); m_operation = operation; m_waitForAccept = waitForAccept; switch(operation) { case OperationType.CopyFolder: m_callbackEnabledMethod = typeof(OSGeo.MapGuide.MaestroAPI.ServerConnectionI).GetMethod("CopyFolderWithReferences"); m_arguments = new object[] { argument1, argument2, callback, pgcallback }; OperationDescription.Text = Strings.LengthyOperation.CopyFolderCopyConfirmation; m_callbackobject = connection; break; case OperationType.MoveFolder: m_callbackEnabledMethod = typeof(OSGeo.MapGuide.MaestroAPI.ServerConnectionI).GetMethod("MoveFolderWithReferences"); m_arguments = new object[] { argument1, argument2, callback, pgcallback }; OperationDescription.Text = Strings.LengthyOperation.MoveFolderConfirmation; m_callbackobject = connection; break; case OperationType.MoveResource: m_callbackEnabledMethod = typeof(OSGeo.MapGuide.MaestroAPI.ServerConnectionI).GetMethod("MoveResourceWithReferences"); m_arguments = new object[] { argument1, argument2, callback, pgcallback }; OperationDescription.Text = Strings.LengthyOperation.MoveResourceConfirmation; m_callbackobject = connection; break; case OperationType.Other: this.Text = Strings.LengthyOperation.OtherOperationConfirmation; m_arguments = new object[] { argument1, argument2, callback, pgcallback }; m_noCancelConfirmation = true; break; default: throw new Exception(string.Format(Strings.LengthyOperation.UnknownOperationInternalError, operation.ToString())); } if (m_callbackEnabledMethod == null) throw new MissingMethodException(Strings.LengthyOperation.MissingMethodInternalError); } private void ThreadRunner() { bool retry = true; while(retry) { retry = false; try { try { m_callbackEnabledMethod.Invoke(m_callbackobject, m_arguments); } catch (System.Reflection.TargetInvocationException tex) { Exception ex = tex.InnerException; string msg = NestedExceptionMessageProcessor.GetFullMessage(ex); switch (MessageBox.Show(string.Format(Strings.LengthyOperation.ErrorOccuredRetryQuestion, msg), Application.ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)) { case DialogResult.No: throw; case DialogResult.Yes: retry = true; break; } } } catch (Exception ex) { if (ex.GetType() == typeof(System.Reflection.TargetInvocationException)) ex = ex.InnerException; string msg = NestedExceptionMessageProcessor.GetFullMessage(ex); MessageBox.Show(string.Format(Strings.LengthyOperation.OperationFailedError, msg), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } this.Invoke(new System.Threading.ThreadStart(CloseDialog)); } private void WaitOperationCallBack(object sender, OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressArgs args) { if (this.InvokeRequired) this.Invoke(new OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressCallBack(WaitOperationCallBack), new object[] { sender, args } ); else { OperationDescription.Text = args.StatusMessage; if (args.Progress == -1) { MarqueeTimer.Enabled = true; } else { AskCancel(); args.Cancel |= m_cancel; ProgressBar.Value = args.Progress; MarqueeTimer.Enabled = false; if (args.Progress == 100) { m_state = (ProgressState)((int)m_state) + 1; ProgressBar.Value = 0; CancelBtn.Enabled = true; } } } } private void AskCancel() { if (m_cancelRequested && !m_cancel) if (m_noCancelConfirmation || MessageBox.Show(this, Strings.LengthyOperation.CancelNotRecomendedConfirmation, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes) m_cancel = true; m_cancelRequested = false; } private void CloseDialog() { if (m_cancel) this.DialogResult = DialogResult.Cancel; else this.DialogResult = DialogResult.OK; this.Close(); } private void CallbackMethod(object sender, OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs args) { this.Invoke(new UpdateDisplayDelegate(UpdateDisplay), new object[] { args }); m_event.WaitOne(); m_waitingForUser = false; AskCancel(); args.Cancel |= m_cancel; } private delegate void UpdateDisplayDelegate(OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs args); private void UpdateDisplay(OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs args) { if (!m_event.WaitOne(1, true)) { m_waitingForUser = true; workList.Items.Clear(); foreach(OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem item in args.Items) workList.Items.Add(new ListViewItem(item.Itempath, 10)); switch(m_operation) { case OperationType.CopyFolder: OperationDescription.Text = Strings.LengthyOperation.CopyFolderUpdateConfirmation; break; case OperationType.MoveFolder: OperationDescription.Text = Strings.LengthyOperation.MoveFolderConfirmation; break; case OperationType.MoveResource: OperationDescription.Text = Strings.LengthyOperation.MoveResourceConfirmation; break; } ProgressBar.Value = 0; this.Height = 432; ProgressPanel.Visible = false; OKBtn.Enabled = true; if (workList.Items.Count == 0) m_event.Set(); else { if (m_waitForAccept) this.Focus(); //Flash it else OKBtn_Click(OKBtn, null); } } else { animationTimer.Enabled = false; m_currentItem = workList.Items[args.Index]; ProgressBar.Value = Math.Min(100, (int)((args.Index / (double)args.Items.Length) * 100)); switch(args.Items[args.Index].Status) { case OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Pending: m_currentItem.ImageIndex = 0; break; case OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Success: m_currentItem.ImageIndex = 8; break; case OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Failure: m_currentItem.ImageIndex = 9; break; default: /*OSGeo.MapGuide.MaestroAPI.LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.None:*/ m_currentItem.ImageIndex = 10; break; } if (m_currentItem.ImageIndex == 0) animationTimer.Enabled = true; } } private void LengthyOperation_Load(object sender, System.EventArgs e) { if (m_callbackEnabledMethod == null) throw new Exception(Strings.LengthyOperation.NotInitializedInternalError); m_runner = new System.Threading.Thread(new System.Threading.ThreadStart(ThreadRunner)); m_runner.IsBackground = true; m_runner.Start(); } private void OKBtn_Click(object sender, System.EventArgs e) { OKBtn.Enabled = false; ProgressPanel.Visible = true; m_event.Set(); } private void CancelBtn_Click(object sender, System.EventArgs e) { m_cancelRequested = true; if (m_waitingForUser) { AskCancel(); if (m_cancel) m_event.Set(); } } private void MarqueeTimer_Tick(object sender, System.EventArgs e) { ProgressBar.Value = ProgressBar.Value % (ProgressBar.Maximum + 1); } private void workList_Resize(object sender, System.EventArgs e) { workList.Columns[0].Width = workList.Width - 40; } } }