Browse Source

Rewritten IProgressMonitor:

- allow nested progress monitors
 - use .NET 4 cancellation framework

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5483 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Daniel Grunwald 16 years ago
parent
commit
fb3fa4dac7
  1. 15
      src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs
  2. 28
      src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/ResourceRefactoringService.cs
  3. 2
      src/AddIns/Misc/SearchAndReplace/Project/Engine/Search.cs
  4. 2
      src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs
  5. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  6. 89
      src/Main/Base/Project/Src/Gui/Components/StatusBar/SdStatusBar.cs
  7. 366
      src/Main/Base/Project/Src/Gui/Dialogs/AsynchronousWaitDialog.cs
  8. 122
      src/Main/Base/Project/Src/Gui/IProgressMonitor.cs
  9. 335
      src/Main/Base/Project/Src/Gui/ProgressCollector.cs
  10. 152
      src/Main/Base/Project/Src/Project/BuildEngine.cs
  11. 29
      src/Main/Base/Project/Src/Project/Converter/LanguageConverter.cs
  12. 7
      src/Main/Base/Project/Src/Project/IBuildFeedbackSink.cs
  13. 18
      src/Main/Base/Project/Src/Project/ProjectLoadInformation.cs
  14. 22
      src/Main/Base/Project/Src/Project/Solution/Solution.cs
  15. 79
      src/Main/Base/Project/Src/Services/ParserService/LoadSolutionProjects.cs
  16. 16
      src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
  17. 12
      src/Main/Base/Project/Src/Services/ProjectBinding/ProjectBindingService.cs
  18. 4
      src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
  19. 35
      src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs
  20. 130
      src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs

15
src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs

@ -80,24 +80,27 @@ namespace ICSharpCode.Profiler.AddIn @@ -80,24 +80,27 @@ namespace ICSharpCode.Profiler.AddIn
void FinishSession()
{
using (AsynchronousWaitDialog dlg = AsynchronousWaitDialog.ShowWaitDialog(StringParser.Parse("${res:AddIns.Profiler.Messages.PreparingForAnalysis}"), true)) {
try {
try {
using (AsynchronousWaitDialog dlg = AsynchronousWaitDialog.ShowWaitDialog(StringParser.Parse("${res:AddIns.Profiler.Messages.PreparingForAnalysis}"), true)) {
profiler.Dispose();
WorkbenchSingleton.SafeThreadAsyncCall(() => { controlWindow.AllowClose = true; this.controlWindow.Close(); });
if (database != null) {
database.WriteTo(writer, progress => !dlg.IsCancelled);
database.WriteTo(writer, progress => {
dlg.Progress = progress;
return !dlg.CancellationToken.IsCancellationRequested;
});
writer.Close();
database.Close();
} else {
writer.Close();
}
if (!dlg.IsCancelled)
if (!dlg.CancellationToken.IsCancellationRequested)
OnRunFinished(EventArgs.Empty);
} catch (Exception ex) {
Debug.Print(ex.ToString());
}
} catch (Exception ex) {
MessageService.ShowException(ex);
}
}

28
src/AddIns/Misc/ResourceToolkit/Project/Src/Refactoring/ResourceRefactoringService.cs

@ -73,12 +73,14 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -73,12 +73,14 @@ namespace Hornung.ResourceToolkit.Refactoring
ICollection<string> files = GetPossibleFiles(scope);
if (monitor != null) {
monitor.BeginTask("${res:SharpDevelop.Refactoring.FindingReferences}", files.Count, true);
monitor.TaskName = StringParser.Parse("${res:SharpDevelop.Refactoring.FindingReferences}");
}
double workDone = 0;
foreach (string fileName in files) {
if (monitor != null && monitor.IsCancelled) {
if (monitor != null)
monitor.Progress = workDone / files.Count;
workDone += 1;
if (monitor != null && monitor.CancellationToken.IsCancellationRequested) {
return null;
}
@ -91,13 +93,11 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -91,13 +93,11 @@ namespace Hornung.ResourceToolkit.Refactoring
} catch (FileNotFoundException) {
}
if (doc == null) {
if (monitor != null) ++monitor.WorkDone;
continue;
}
string fileContent = doc.Text;
if (String.IsNullOrEmpty(fileContent)) {
if (monitor != null) ++monitor.WorkDone;
continue;
}
@ -139,15 +139,12 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -139,15 +139,12 @@ namespace Hornung.ResourceToolkit.Refactoring
}
}
if (monitor != null) ++monitor.WorkDone;
}
LoggingService.Info("ResourceToolkit: FindReferences finished in "+(DateTime.UtcNow - startTime).TotalSeconds.ToString(System.Globalization.CultureInfo.CurrentCulture)+"s");
} finally {
NRefactoryAstCacheService.DisableCache();
if (monitor != null) monitor.Done();
}
return references;
@ -210,10 +207,6 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -210,10 +207,6 @@ namespace Hornung.ResourceToolkit.Refactoring
return null;
}
if (monitor != null) {
monitor.BeginTask(null, 0, false);
}
List<ResourceItem> unused = new List<ResourceItem>();
// Get a list of all referenced resource files.
@ -257,8 +250,6 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -257,8 +250,6 @@ namespace Hornung.ResourceToolkit.Refactoring
}
}
if (monitor != null) monitor.Done();
return unused.AsReadOnly();
}
@ -302,10 +293,6 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -302,10 +293,6 @@ namespace Hornung.ResourceToolkit.Refactoring
return;
}
if (monitor != null) {
monitor.BeginTask(null, 0, false);
}
try {
// rename definition (if present)
if (rrr.ResourceFileContent.ContainsKey(rrr.Key)) {
@ -319,7 +306,6 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -319,7 +306,6 @@ namespace Hornung.ResourceToolkit.Refactoring
if (monitor != null) monitor.ShowingDialog = true;
MessageService.ShowWarningFormatted("${res:Hornung.ResourceToolkit.ErrorProcessingResourceFile}" + Environment.NewLine + ex.Message, rrr.ResourceFileContent.FileName);
if (monitor != null) monitor.ShowingDialog = false;
if (monitor != null) monitor.Done();
// Do not rename the references when renaming the definition failed.
return;
}
@ -340,8 +326,6 @@ namespace Hornung.ResourceToolkit.Refactoring @@ -340,8 +326,6 @@ namespace Hornung.ResourceToolkit.Refactoring
if (monitor != null) monitor.ShowingDialog = false;
}
}
if (monitor != null) monitor.Done();
}
// ********************************************************************************************************************************

2
src/AddIns/Misc/SearchAndReplace/Project/Engine/Search.cs

@ -89,7 +89,7 @@ namespace SearchAndReplace @@ -89,7 +89,7 @@ namespace SearchAndReplace
Debug.Assert(documentIterator != null);
Debug.Assert(textIteratorBuilder != null);
if (monitor != null && monitor.IsCancelled)
if (monitor != null && monitor.CancellationToken.IsCancellationRequested)
return null;
if (info != null && textIterator != null && documentIterator.CurrentFileName != null) {

2
src/AddIns/Misc/SearchAndReplace/Project/Engine/SearchReplaceManager.cs

@ -352,7 +352,7 @@ namespace SearchAndReplace @@ -352,7 +352,7 @@ namespace SearchAndReplace
static void ShowNotFoundMessage(IProgressMonitor monitor)
{
if (monitor != null && monitor.IsCancelled)
if (monitor != null && monitor.CancellationToken.IsCancellationRequested)
return;
if (monitor != null) monitor.ShowingDialog = true;
MessageBox.Show(WorkbenchSingleton.MainWin32Window,

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -206,6 +206,7 @@ @@ -206,6 +206,7 @@
<Compile Include="Src\Gui\Pads\ProjectBrowser\TreeNodes\DirectoryNodeFactory.cs" />
<Compile Include="Src\Gui\Pads\TaskList\TaskListPadCommands.cs" />
<Compile Include="Src\Gui\Pads\ToolsPad.cs" />
<Compile Include="Src\Gui\ProgressCollector.cs" />
<Compile Include="Src\Gui\Workbench\FullScreenEnabledWindow.cs" />
<Compile Include="Src\Gui\Workbench\Layouts\AvalonDockLayout.cs" />
<Compile Include="Src\Gui\Workbench\Layouts\AvalonPadContent.cs" />

89
src/Main/Base/Project/Src/Gui/Components/StatusBar/SdStatusBar.cs

@ -10,6 +10,7 @@ using System.Windows; @@ -10,6 +10,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using ICSharpCode.Core;
@ -42,6 +43,10 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -42,6 +43,10 @@ namespace ICSharpCode.SharpDevelop.Gui
{
cursorStatusBarPanel.Width = 150;
modeStatusBarPanel.Width = 25;
statusProgressBar.Minimum = 0;
statusProgressBar.Maximum = 1;
statusProgressBarItem.Visibility = Visibility.Hidden;
statusProgressBarItem.Width = 100;
statusProgressBarItem.Content = statusProgressBar;
@ -103,52 +108,56 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -103,52 +108,56 @@ namespace ICSharpCode.SharpDevelop.Gui
bool statusProgressBarIsVisible;
string currentTaskName;
OperationStatus currentStatus;
SolidColorBrush progressForegroundBrush;
public void DisplayProgress(string taskName, int workDone, int totalWork)
public void DisplayProgress(string taskName, double workDone, OperationStatus status)
{
if (taskName == null)
taskName = "";
if (totalWork < 0)
totalWork = 0;
if (workDone < 0)
workDone = 0;
if (workDone > totalWork)
workDone = totalWork;
WorkbenchSingleton.SafeThreadAsyncCall(
delegate {
if (!statusProgressBarIsVisible) {
statusProgressBarItem.Visibility = Visibility.Visible;
statusProgressBarIsVisible = true;
}
if (totalWork == 0) {
statusProgressBar.IsIndeterminate = true;
} else {
statusProgressBar.IsIndeterminate = false;
if (statusProgressBar.Maximum != totalWork) {
if (statusProgressBar.Value > totalWork)
statusProgressBar.Value = 0;
statusProgressBar.Maximum = totalWork;
}
statusProgressBar.Value = workDone;
}
if (currentTaskName != taskName) {
currentTaskName = taskName;
jobNamePanel.Content = StringParser.Parse(taskName);
}
});
if (!statusProgressBarIsVisible) {
statusProgressBarItem.Visibility = Visibility.Visible;
statusProgressBarIsVisible = true;
}
if (double.IsNaN(workDone)) {
statusProgressBar.IsIndeterminate = true;
status = OperationStatus.Normal; // indeterminate doesn't support foreground color
} else {
statusProgressBar.IsIndeterminate = false;
statusProgressBar.Value = workDone;
}
if (status != currentStatus) {
if (progressForegroundBrush == null) {
SolidColorBrush defaultForeground = statusProgressBar.Foreground as SolidColorBrush;
progressForegroundBrush = new SolidColorBrush(defaultForeground != null ? defaultForeground.Color : Colors.Blue);
}
if (status == OperationStatus.Error) {
statusProgressBar.Foreground = progressForegroundBrush;
progressForegroundBrush.BeginAnimation(SolidColorBrush.ColorProperty, new ColorAnimation(
Colors.Red, new Duration(TimeSpan.FromSeconds(0.6)), FillBehavior.HoldEnd));
} else if (status == OperationStatus.Warning) {
statusProgressBar.Foreground = progressForegroundBrush;
progressForegroundBrush.BeginAnimation(SolidColorBrush.ColorProperty, new ColorAnimation(
Colors.YellowGreen, new Duration(TimeSpan.FromSeconds(0.6)), FillBehavior.HoldEnd));
} else {
statusProgressBar.ClearValue(ProgressBar.ForegroundProperty);
progressForegroundBrush = null;
}
currentStatus = status;
}
if (currentTaskName != taskName) {
currentTaskName = taskName;
jobNamePanel.Content = taskName;
}
}
public void HideProgress()
{
WorkbenchSingleton.SafeThreadAsyncCall(
delegate {
statusProgressBarIsVisible = false;
statusProgressBarItem.Visibility = Visibility.Collapsed;
jobNamePanel.Content = currentTaskName = "";
});
statusProgressBarIsVisible = false;
statusProgressBarItem.Visibility = Visibility.Collapsed;
jobNamePanel.Content = currentTaskName = "";
}
}
}

366
src/Main/Base/Project/Src/Gui/Dialogs/AsynchronousWaitDialog.cs

@ -6,20 +6,30 @@ @@ -6,20 +6,30 @@
// </file>
using System;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.SharpDevelop.Gui
{
internal sealed partial class AsynchronousWaitDialogForm
{
internal AsynchronousWaitDialogForm()
internal AsynchronousWaitDialogForm(bool allowCancel)
{
InitializeComponent();
cancelButton.Text = ResourceService.GetString("Global.CancelButtonText");
if (allowCancel) {
cancelButton.Visible = true;
progressBar.Width = cancelButton.Left - 8 - progressBar.Left;
} else {
cancelButton.Visible = false;
progressBar.Width = cancelButton.Right - progressBar.Left;
}
}
}
@ -30,27 +40,26 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -30,27 +40,26 @@ namespace ICSharpCode.SharpDevelop.Gui
/// long_running_action();
/// }
/// or:
/// using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("title")) {
/// using (IProgressMonitor monitor = AsynchronousWaitDialog.ShowWaitDialog("title")) {
/// long_running_action(monitor);
/// }
/// </summary>
public sealed class AsynchronousWaitDialog : IProgressMonitor, IDisposable
public sealed class AsynchronousWaitDialog : IProgressMonitor
{
/// <summary>
/// Delay until the wait dialog becomes visible, in ms.
/// </summary>
public const int ShowWaitDialogDelay = 500;
readonly object lockObject = new object();
bool disposed;
readonly string titleName;
readonly string defaultTaskName;
readonly CancellationTokenSource cancellation;
readonly ProgressCollector collector;
readonly SynchronizationHelper synchronizationHelper = new SynchronizationHelper();
AsynchronousWaitDialogForm dlg;
volatile int totalWork;
volatile string titleName;
volatile string taskName;
volatile int workDone;
volatile bool cancelled;
volatile bool allowCancel;
#region Constructors
/// <summary>
/// Shows a wait dialog.
/// </summary>
@ -59,11 +68,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -59,11 +68,7 @@ namespace ICSharpCode.SharpDevelop.Gui
/// To close the wait dialog, call Dispose() on the AsynchronousWaitDialog object</returns>
public static AsynchronousWaitDialog ShowWaitDialog(string titleName)
{
if (titleName == null)
throw new ArgumentNullException("titleName");
AsynchronousWaitDialog h = new AsynchronousWaitDialog(titleName, false);
h.Start();
return h;
return ShowWaitDialog(titleName, null, false);
}
/// <summary>
@ -74,20 +79,109 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -74,20 +79,109 @@ namespace ICSharpCode.SharpDevelop.Gui
/// <returns>AsynchronousWaitDialog object - you can use it to access the wait dialog's properties.
/// To close the wait dialog, call Dispose() on the AsynchronousWaitDialog object</returns>
public static AsynchronousWaitDialog ShowWaitDialog(string titleName, bool allowCancel)
{
return ShowWaitDialog(titleName, null, allowCancel);
}
/// <summary>
/// Shows a wait dialog that does not support cancelling.
/// </summary>
/// <param name="titleName">Title of the wait dialog</param>
/// <param name="allowCancel">Specifies whether a cancel button should be shown.</param>
/// <param name="defaultTaskName">The default description text, if no named task is active.</param>
/// <returns>AsynchronousWaitDialog object - you can use it to access the wait dialog's properties.
/// To close the wait dialog, call Dispose() on the AsynchronousWaitDialog object</returns>
public static AsynchronousWaitDialog ShowWaitDialog(string titleName, string defaultTaskName, bool allowCancel)
{
if (titleName == null)
throw new ArgumentNullException("titleName");
AsynchronousWaitDialog h = new AsynchronousWaitDialog(titleName, allowCancel);
AsynchronousWaitDialog h = new AsynchronousWaitDialog(titleName, defaultTaskName, allowCancel);
h.Start();
return h;
}
private AsynchronousWaitDialog(string titleName, bool allowCancel)
/// <summary>
/// Shows a wait dialog that does supports cancelling.
/// </summary>
/// <param name="titleName">Title of the wait dialog</param>
/// <param name="defaultTaskName">The default description text, if no named task is active.</param>
/// <param name="action">The action to run within the wait dialog</param>
public static void RunInCancellableWaitDialog(string titleName, string defaultTaskName, Action<AsynchronousWaitDialog> action)
{
if (titleName == null)
throw new ArgumentNullException("titleName");
using (AsynchronousWaitDialog h = new AsynchronousWaitDialog(titleName, defaultTaskName, true)) {
h.Start();
try {
action(h);
} catch (OperationCanceledException ex) {
// consume OperationCanceledException
if (ex.CancellationToken != h.CancellationToken)
throw;
}
}
}
private AsynchronousWaitDialog(string titleName, string defaultTaskName, bool allowCancel)
{
this.titleName = StringParser.Parse(titleName);
this.defaultTaskName = StringParser.Parse(defaultTaskName ?? "${res:Global.PleaseWait}");
if (allowCancel)
this.cancellation = new CancellationTokenSource();
this.collector = new ProgressCollector(synchronizationHelper, allowCancel ? cancellation.Token : CancellationToken.None);
}
#endregion
#region SynchronizationHelper
// this class works around the issue that we don't initially have an ISynchronizeInvoke implementation
// for the target thread, we only create it after ShowWaitDialogDelay.
sealed class SynchronizationHelper : ISynchronizeInvoke
{
this.titleName = titleName;
Done(); // set default values for titleName
this.allowCancel = allowCancel;
volatile ISynchronizeInvoke targetSynchronizeInvoke;
public bool InvokeRequired {
get {
ISynchronizeInvoke si = targetSynchronizeInvoke;
return si != null && si.InvokeRequired;
}
}
public IAsyncResult BeginInvoke(Delegate method, object[] args)
{
ISynchronizeInvoke si = targetSynchronizeInvoke;
if (si != null)
return si.BeginInvoke(method, args);
else {
// When target is not available, invoke method on current thread, but use a lock
// to ensure we don't run multiple methods concurrently.
lock (this) {
method.DynamicInvoke(args);
return null;
}
// yes this is morally questionable - maybe it would be better to enqueue all invocations and run them later?
// ProgressCollector would have to avoid enqueuing stuff multiple times for all kinds of updates
// (currently it does this only with updates to the Progress property)
}
}
public void SetTarget(ISynchronizeInvoke targetSynchronizeInvoke)
{
lock (this) {
this.targetSynchronizeInvoke = targetSynchronizeInvoke;
}
}
public object EndInvoke(IAsyncResult result)
{
throw new NotSupportedException();
}
public object Invoke(Delegate method, object[] args)
{
throw new NotSupportedException();
}
}
#endregion
#region Start waiting thread
/// <summary>
@ -106,19 +200,29 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -106,19 +200,29 @@ namespace ICSharpCode.SharpDevelop.Gui
void Run()
{
Thread.Sleep(ShowWaitDialogDelay);
bool isShowingDialog;
lock (lockObject) {
if (disposed)
return;
dlg = new AsynchronousWaitDialogForm();
dlg.Text = StringParser.Parse(titleName);
dlg.cancelButton.Click += CancelButtonClick;
UpdateTask();
dlg.CreateControl();
IntPtr h = dlg.Handle; // force handle creation
isShowingDialog = showingDialog;
if (collector.ProgressMonitorIsDisposed)
return;
dlg = new AsynchronousWaitDialogForm(cancellation != null);
dlg.Text = titleName;
dlg.cancelButton.Click += CancelButtonClick;
dlg.CreateControl();
IntPtr h = dlg.Handle; // force handle creation
// ensure events occur on this thread, then register event handlers
synchronizationHelper.SetTarget(dlg);
collector.ProgressMonitorDisposed += progress_ProgressMonitorDisposed;
collector.PropertyChanged += progress_PropertyChanged;
// check IsDisposed once again (we might have missed an event while we initialized the dialog):
if (collector.ProgressMonitorIsDisposed) {
dlg.Dispose();
return;
}
if (isShowingDialog) {
progress_PropertyChanged(null, new System.ComponentModel.PropertyChangedEventArgs("TaskName"));
if (collector.ShowingDialog) {
Application.Run();
} else {
Application.Run(dlg);
@ -129,166 +233,92 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -129,166 +233,92 @@ namespace ICSharpCode.SharpDevelop.Gui
/// <summary>
/// Closes the wait dialog.
/// </summary>
public void Dispose()
{
lock (lockObject) {
if (disposed) return;
disposed = true;
if (dlg != null) {
dlg.BeginInvoke(new MethodInvoker(DisposeInvoked));
}
}
}
void DisposeInvoked()
void progress_ProgressMonitorDisposed(object sender, EventArgs e)
{
dlg.Dispose();
Application.ExitThread();
}
public int WorkDone {
get {
return workDone;
}
set {
if (workDone != value) {
lock (lockObject) {
workDone = value;
if (dlg != null && disposed == false) {
dlg.BeginInvoke(new MethodInvoker(UpdateProgress));
}
}
}
}
}
bool reshowTimerRunning = false;
public string TaskName {
get {
lock (lockObject) {
return taskName;
}
}
set {
if (taskName != value) {
lock (lockObject) {
taskName = value;
if (dlg != null && disposed == false) {
dlg.BeginInvoke(new MethodInvoker(UpdateTask));
}
}
void progress_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// show/hide dialog as required by ShowingDialog
if (dlg.Visible == collector.ShowingDialog) {
if (collector.ShowingDialog) {
dlg.Hide();
} else if (!reshowTimerRunning) {
reshowTimerRunning = true;
var timer = new System.Windows.Forms.Timer();
timer.Interval = 100;
timer.Tick += delegate {
timer.Dispose();
reshowTimerRunning = false;
if (!collector.ShowingDialog)
dlg.Show();
};
timer.Start();
}
}
}
/// <summary>
/// Begins a new task with the specified name and total amount of work.
/// </summary>
/// <param name="name">Name of the task. Use null to display "please wait..." message</param>
/// <param name="totalWork">Total amount of work in work units. Use 0 for unknown amount of work.</param>
/// <param name="allowCancel">Specifies whether the task can be cancelled.</param>
public void BeginTask(string name, int totalWork, bool allowCancel)
{
if (name == null)
name = "${res:Global.PleaseWait}";
if (totalWork < 0)
totalWork = 0;
lock (lockObject) {
this.allowCancel = allowCancel;
this.totalWork = totalWork;
this.taskName = name;
if (dlg != null && disposed == false) {
dlg.BeginInvoke(new MethodInvoker(UpdateTask));
}
// update task name and progress
if (e.PropertyName == "TaskName")
dlg.taskLabel.Text = collector.TaskName ?? defaultTaskName;
if (double.IsNaN(collector.Progress)) {
dlg.progressBar.Style = ProgressBarStyle.Marquee;
} else {
dlg.progressBar.Style = ProgressBarStyle.Continuous;
dlg.progressBar.Value = Math.Max(0, Math.Min(100, (int)(collector.Progress * 100)));
}
}
/// <summary>
/// Resets the task to a generic "please wait" with marquee progress bar.
/// </summary>
public void Done()
void CancelButtonClick(object sender, EventArgs e)
{
workDone = 0;
BeginTask(null, 0, false);
dlg.cancelButton.Enabled = false;
if (cancellation != null)
cancellation.Cancel();
}
void UpdateTask()
{
int totalWork = this.totalWork;
dlg.taskLabel.Text = StringParser.Parse(taskName);
if (allowCancel) {
dlg.cancelButton.Visible = true;
dlg.progressBar.Width = dlg.cancelButton.Left - 8 - dlg.progressBar.Left;
} else {
dlg.cancelButton.Visible = false;
dlg.progressBar.Width = dlg.cancelButton.Right - dlg.progressBar.Left;
}
#region IProgressMonitor interface impl (forwards to progress.ProgressMonitor)
/// <inheritdoc/>
public string TaskName {
get { return collector.ProgressMonitor.TaskName; }
set { collector.ProgressMonitor.TaskName = value; }
}
if (totalWork > 0) {
if (dlg.progressBar.Value > totalWork) {
dlg.progressBar.Value = 0;
}
dlg.progressBar.Maximum = totalWork + 1;
dlg.progressBar.Style = ProgressBarStyle.Continuous;
} else {
dlg.progressBar.Style = ProgressBarStyle.Marquee;
}
UpdateProgress();
/// <inheritdoc/>
public double Progress {
get { return collector.ProgressMonitor.Progress; }
set { collector.ProgressMonitor.Progress = value; }
}
void UpdateProgress()
{
int workDone = this.workDone;
if (workDone < 0) workDone = 0;
if (workDone > dlg.progressBar.Maximum)
workDone = dlg.progressBar.Maximum;
dlg.progressBar.Value = workDone;
/// <inheritdoc/>
public bool ShowingDialog {
get { return collector.ProgressMonitor.ShowingDialog; }
set { collector.ProgressMonitor.ShowingDialog = value; }
}
bool showingDialog;
/// <inheritdoc/>
public OperationStatus Status {
get { return collector.ProgressMonitor.Status; }
set { collector.ProgressMonitor.Status = value; }
}
public bool ShowingDialog {
get { return showingDialog; }
set {
if (showingDialog != value) {
lock (lockObject) {
showingDialog = value;
if (dlg != null && disposed == false) {
if (value) {
dlg.BeginInvoke(new MethodInvoker(dlg.Hide));
} else {
dlg.BeginInvoke(new MethodInvoker(delegate {
Thread.Sleep(100);
if (showingDialog) {
dlg.Show();
}
}));
}
}
}
}
}
/// <inheritdoc/>
public CancellationToken CancellationToken {
get { return collector.ProgressMonitor.CancellationToken; }
}
void CancelButtonClick(object sender, EventArgs e)
/// <inheritdoc/>
public IProgressMonitor CreateSubTask(double workAmount)
{
dlg.cancelButton.Enabled = false;
if (!cancelled) {
cancelled = true;
EventHandler eh = Cancelled;
if (eh != null) {
eh(this, e);
}
}
return collector.ProgressMonitor.CreateSubTask(workAmount);
}
public bool IsCancelled {
get {
return cancelled;
}
public void Dispose()
{
collector.ProgressMonitor.Dispose();
}
public event EventHandler Cancelled;
#endregion
}
}

122
src/Main/Base/Project/Src/Gui/IProgressMonitor.cs

@ -6,67 +6,88 @@ @@ -6,67 +6,88 @@
// </file>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
namespace ICSharpCode.SharpDevelop.Gui
{
/// <summary>
/// This is a basic interface to a "progress bar" type of
/// control.
/// Represents a target where an active task reports progress to.
/// </summary>
public interface IProgressMonitor
/// <remarks>
/// This interface is not thread-safe, but it can be used to report progress from background threads which will be
/// automatically sent to the correct GUI thread.
/// Using a progress monitor from multiple threads is possible if the user synchronizes the access.
/// </remarks>
public interface IProgressMonitor : IDisposable
{
/// <summary>
/// Begins a new task with the specified name and total amount of work.
/// Gets/Sets the amount of work already done within this task.
/// Always uses a scale from 0 to 1 local to this progress monitor.
/// </summary>
/// <param name="name">Name of the task. Use null to display a default message</param>
/// <param name="totalWork">Total amount of work in work units. Use 0 for unknown amount of work.</param>
/// <param name="allowCancel">Specifies whether the task can be cancelled.</param>
void BeginTask(string name, int totalWork, bool allowCancel);
double Progress { get; set; }
/// <summary>
/// Gets/Sets the amount of work already done
/// Creates a nested task.
/// </summary>
int WorkDone {
get;
set;
}
/// <param name="workAmount">The amount of work this sub-task performs in relation to the work of this task.
/// That means, this parameter is used as a scaling factor for work performed within the subtask.</param>
/// <returns>A new progress monitor representing the sub-task.
/// Multiple child progress monitors can be used at once; even concurrently on multiple threads.</returns>
IProgressMonitor CreateSubTask(double workAmount);
/// <summary>
/// Marks the current task as Done.
/// Gets/Sets the name to show while the task is active.
/// </summary>
void Done();
string TaskName { get; set; }
/// <summary>
/// Gets/Sets the current task name.
/// Gets/sets if the task current shows a modal dialog. Set this property to true to make progress
/// dialogs windows temporarily invisible while your modal dialog is showing.
/// </summary>
string TaskName {
bool ShowingDialog { // TODO: get rid of this. Don't mix calculations and UI!
get;
set;
}
/// <summary>
/// Gets/sets if the task current shows a modal dialog. Set this property to true to make progress
/// dialogs windows temporarily invisible while your modal dialog is showing.
/// Gets the cancellation token.
/// </summary>
bool ShowingDialog {
get;
set;
}
CancellationToken CancellationToken { get; }
/// <summary>
/// Gets whether the user has cancelled the operation.
/// Gets/Sets the operation status.
///
/// Note: the status of the whole operation is the most severe status of all nested monitors.
/// The more severe value persists even if the child monitor gets disposed.
/// </summary>
bool IsCancelled {
get;
}
OperationStatus Status { get; set; }
}
/// <summary>
/// Represents the status of a operation with progress monitor.
/// </summary>
public enum OperationStatus : byte
{
/// <summary>
/// Occurs when the user cancels the operation.
/// This event could be raised on any thread.
/// Everything is normal.
/// </summary>
event EventHandler Cancelled;
Normal,
/// <summary>
/// There was at least one warning.
/// </summary>
Warning,
/// <summary>
/// There was at least one error.
/// </summary>
Error
}
/// <summary>
/// Adapter IDomProgressMonitor -> IProgressMonitor
/// </summary>
public sealed class DomProgressMonitor : Dom.IDomProgressMonitor
{
IProgressMonitor monitor;
@ -92,41 +113,30 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -92,41 +113,30 @@ namespace ICSharpCode.SharpDevelop.Gui
}
}
internal class DummyProgressMonitor : IProgressMonitor
/// <summary>
/// Dummy progress monitor implementation that does not report the progress anywhere.
/// </summary>
public sealed class DummyProgressMonitor : IProgressMonitor
{
int workDone;
string taskName;
bool showingDialog;
public string TaskName { get; set; }
public int WorkDone {
get { return workDone; }
set { workDone = value; }
}
public bool ShowingDialog { get; set; }
public string TaskName {
get { return taskName; }
set { taskName = value; }
}
public OperationStatus Status { get; set; }
public void BeginTask(string name, int totalWork, bool allowCancel)
{
taskName = name;
workDone = 0;
public CancellationToken CancellationToken {
get { return CancellationToken.None; }
}
public void Done()
{
}
public double Progress { get; set; }
public bool IsCancelled {
get { return false; }
public IProgressMonitor CreateSubTask(double workAmount)
{
return new DummyProgressMonitor();
}
public bool ShowingDialog {
get { return showingDialog; }
set { showingDialog = value; }
public void Dispose()
{
}
public event EventHandler Cancelled { add { } remove { } }
}
}

335
src/Main/Base/Project/Src/Gui/ProgressCollector.cs

@ -0,0 +1,335 @@ @@ -0,0 +1,335 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
namespace ICSharpCode.SharpDevelop.Gui
{
/// <summary>
/// Collects progress using nested IProgressMonitors and provides it to a different thread using events.
/// </summary>
public sealed class ProgressCollector : INotifyPropertyChanged
{
readonly ISynchronizeInvoke eventThread;
readonly CancellationToken cancellationToken;
readonly MonitorImpl root;
readonly LinkedList<string> namedMonitors = new LinkedList<string>();
readonly object updateLock = new object();
string taskName;
double progress;
OperationStatus status;
bool showingDialog;
bool rootMonitorIsDisposed;
public ProgressCollector(ISynchronizeInvoke eventThread, CancellationToken cancellationToken)
{
if (eventThread == null)
throw new ArgumentNullException("eventThread");
this.eventThread = eventThread;
this.cancellationToken = cancellationToken;
this.root = new MonitorImpl(this, null, 1);
}
public event EventHandler ProgressMonitorDisposed;
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public double Progress {
get { return progress; }
private set {
Debug.Assert(!eventThread.InvokeRequired);
if (progress != value) {
progress = value;
// Defensive programming: parallel processes like the build could change properites even
// after the monitor is disposed (they shouldn't do that, but it could happen),
// and we don't want to confuse consumers like the status bar by
// raising events from disposed monitors.
if (!rootMonitorIsDisposed)
OnPropertyChanged("Progress");
}
}
}
public bool ShowingDialog {
get { return showingDialog; }
set {
Debug.Assert(!eventThread.InvokeRequired);
if (showingDialog != value) {
showingDialog = value;
if (!rootMonitorIsDisposed)
OnPropertyChanged("ShowingDialog");
}
}
}
public string TaskName {
get { return taskName; }
private set {
Debug.Assert(!eventThread.InvokeRequired);
if (taskName != value) {
taskName = value;
if (!rootMonitorIsDisposed)
OnPropertyChanged("TaskName");
}
}
}
public OperationStatus Status {
get { return status; }
private set {
Debug.Assert(!eventThread.InvokeRequired);
if (status != value) {
status = value;
if (!rootMonitorIsDisposed)
OnPropertyChanged("Status");
}
}
}
public IProgressMonitor ProgressMonitor {
get { return root; }
}
/// <summary>
/// Gets whether the root progress monitor was disposed.
/// </summary>
public bool ProgressMonitorIsDisposed {
get { return rootMonitorIsDisposed; }
}
bool hasUpdateScheduled;
double storedNewProgress = -1;
OperationStatus storedNewStatus;
void SetProgress(double newProgress)
{
// this method is always called within a lock(updateLock) block, so we don't
// have to worry about thread safety when accessing hasUpdateScheduled and storedNewProgress
storedNewProgress = newProgress;
ScheduleUpdate();
}
void ScheduleUpdate()
{
// This test ensures that only 1 update is scheduled at a single point in time. If updates
// come in faster than the GUI can process them, we'll skip some and directly update to the newest value.
if (!hasUpdateScheduled) {
hasUpdateScheduled = true;
eventThread.BeginInvoke(
(Action)delegate {
lock (updateLock) {
this.Progress = storedNewProgress;
this.Status = storedNewStatus;
hasUpdateScheduled = false;
}
},
null
);
}
}
void SetStatus(OperationStatus newStatus)
{
// this method is always called within a lock(updateLock) block, so we don't
// have to worry about thread safety when accessing hasUpdateScheduled and storedNewStatus
storedNewStatus = newStatus;
ScheduleUpdate();
}
void SetShowingDialog(bool newValue)
{
eventThread.BeginInvoke(
(Action)delegate { this.ShowingDialog = newValue; },
null
);
}
void OnRootMonitorDisposed()
{
eventThread.BeginInvoke(
(Action)delegate {
if (rootMonitorIsDisposed) // ignore double dispose
return;
rootMonitorIsDisposed = true;
if (ProgressMonitorDisposed != null) {
ProgressMonitorDisposed(this, EventArgs.Empty);
}
},
null);
}
void SetTaskName(string newName)
{
eventThread.BeginInvoke(
(Action)delegate { this.TaskName = newName; },
null);
}
LinkedListNode<string> RegisterNamedMonitor(string name)
{
lock (namedMonitors) {
LinkedListNode<string> newEntry = namedMonitors.AddLast(name);
if (namedMonitors.First == newEntry) {
SetTaskName(name);
}
return newEntry;
}
}
void UnregisterNamedMonitor(LinkedListNode<string> nameEntry)
{
lock (namedMonitors) {
bool wasFirst = namedMonitors.First == nameEntry;
namedMonitors.Remove(nameEntry);
if (wasFirst)
SetTaskName(namedMonitors.First != null ? namedMonitors.First.Value : null);
}
}
void ChangeName(LinkedListNode<string> nameEntry, string newName)
{
lock (namedMonitors) {
if (namedMonitors.First == nameEntry)
SetTaskName(newName);
nameEntry.Value = newName;
}
}
sealed class MonitorImpl : IProgressMonitor
{
readonly ProgressCollector collector;
readonly MonitorImpl parent;
readonly double scaleFactor;
LinkedListNode<string> nameEntry;
double currentProgress;
OperationStatus localStatus, currentStatus;
int childrenWithWarnings, childrenWithErrors;
public MonitorImpl(ProgressCollector collector, MonitorImpl parent, double scaleFactor)
{
this.collector = collector;
this.parent = parent;
this.scaleFactor = scaleFactor;
}
public bool ShowingDialog {
get { return collector.ShowingDialog; }
set { collector.SetShowingDialog(value); }
}
public string TaskName {
get {
if (nameEntry != null)
return nameEntry.Value;
else
return null;
}
set {
if (nameEntry != null) {
if (value == null) {
collector.UnregisterNamedMonitor(nameEntry);
nameEntry = null;
} else {
if (nameEntry.Value != value)
collector.ChangeName(nameEntry, value);
}
} else {
if (value != null)
nameEntry = collector.RegisterNamedMonitor(value);
}
}
}
public CancellationToken CancellationToken {
get { return collector.cancellationToken; }
}
public double Progress {
get { return currentProgress; }
set {
lock (collector.updateLock) {
UpdateProgress(value);
}
}
}
void UpdateProgress(double progress)
{
if (parent != null)
parent.UpdateProgress(parent.currentProgress + (progress - this.currentProgress) * scaleFactor);
else
collector.SetProgress(progress);
this.currentProgress = progress;
}
public OperationStatus Status {
get { return localStatus; }
set {
if (localStatus != value) {
localStatus = value;
lock (collector.updateLock) {
UpdateStatus();
}
}
}
}
void UpdateStatus()
{
OperationStatus oldStatus = currentStatus;
if (childrenWithErrors > 0)
currentStatus = OperationStatus.Error;
else if (childrenWithWarnings > 0 && localStatus != OperationStatus.Error)
currentStatus = OperationStatus.Warning;
else
currentStatus = localStatus;
if (oldStatus != currentStatus) {
if (parent != null) {
if (oldStatus == OperationStatus.Warning)
parent.childrenWithWarnings--;
else if (oldStatus == OperationStatus.Error)
parent.childrenWithErrors--;
if (currentStatus == OperationStatus.Warning)
parent.childrenWithWarnings++;
else if (currentStatus == OperationStatus.Error)
parent.childrenWithErrors++;
parent.UpdateStatus();
} else {
collector.SetStatus(currentStatus);
}
}
}
public IProgressMonitor CreateSubTask(double workAmount)
{
return new MonitorImpl(collector, this, workAmount);
}
public void Dispose()
{
this.TaskName = null;
if (parent == null)
collector.OnRootMonitorDisposed();
}
}
}
}

152
src/Main/Base/Project/Src/Project/BuildEngine.cs

@ -8,10 +8,11 @@ @@ -8,10 +8,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
using System.Text;
namespace ICSharpCode.SharpDevelop.Project
{
@ -25,7 +26,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -25,7 +26,7 @@ namespace ICSharpCode.SharpDevelop.Project
public sealed class BuildEngine
{
#region Building in the SharpDevelop GUI
static CancellableProgressMonitor guiBuildProgressMonitor;
static CancellationTokenSource guiBuildCancellation;
static IAnalyticsMonitorTrackedFeature guiBuildTrackedFeature;
/// <summary>
@ -41,7 +42,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -41,7 +42,7 @@ namespace ICSharpCode.SharpDevelop.Project
if (options == null)
throw new ArgumentNullException("options");
WorkbenchSingleton.AssertMainThread();
if (guiBuildProgressMonitor != null) {
if (guiBuildCancellation != null) {
BuildResults results = new BuildResults();
StatusBarService.ShowErrorMessage(Core.ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning"));
results.Add(new BuildError(null, Core.ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning")));
@ -50,13 +51,13 @@ namespace ICSharpCode.SharpDevelop.Project @@ -50,13 +51,13 @@ namespace ICSharpCode.SharpDevelop.Project
options.Callback(results);
}
} else {
guiBuildProgressMonitor = new CancellableProgressMonitor(StatusBarService.CreateProgressMonitor());
guiBuildCancellation = new CancellationTokenSource();
IProgressMonitor progressMonitor = StatusBarService.CreateProgressMonitor(guiBuildCancellation.Token);
guiBuildTrackedFeature = AnalyticsMonitorService.TrackFeature("Build");
StatusBarService.SetMessage(StringParser.Parse("${res:MainWindow.CompilerMessages.BuildVerb}..."));
ProjectService.RaiseEventBuildStarted(new BuildEventArgs(project, options));
StartBuild(project, options,
new MessageViewSink(TaskService.BuildMessageViewCategory),
guiBuildProgressMonitor);
new MessageViewSink(TaskService.BuildMessageViewCategory, progressMonitor));
}
}
@ -66,7 +67,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -66,7 +67,7 @@ namespace ICSharpCode.SharpDevelop.Project
public static bool IsGuiBuildRunning {
get {
WorkbenchSingleton.AssertMainThread();
return guiBuildProgressMonitor != null;
return guiBuildCancellation != null;
}
}
@ -77,8 +78,8 @@ namespace ICSharpCode.SharpDevelop.Project @@ -77,8 +78,8 @@ namespace ICSharpCode.SharpDevelop.Project
public static void CancelGuiBuild()
{
WorkbenchSingleton.AssertMainThread();
if (guiBuildProgressMonitor != null) {
guiBuildProgressMonitor.Cancel();
if (guiBuildCancellation != null) {
guiBuildCancellation.Cancel();
}
}
@ -88,10 +89,16 @@ namespace ICSharpCode.SharpDevelop.Project @@ -88,10 +89,16 @@ namespace ICSharpCode.SharpDevelop.Project
sealed class MessageViewSink : IBuildFeedbackSink
{
Gui.MessageViewCategory messageView;
Gui.IProgressMonitor progressMonitor;
public MessageViewSink(ICSharpCode.SharpDevelop.Gui.MessageViewCategory messageView)
public MessageViewSink(MessageViewCategory messageView, Gui.IProgressMonitor progressMonitor)
{
this.messageView = messageView;
this.progressMonitor = progressMonitor;
}
public IProgressMonitor ProgressMonitor {
get { return progressMonitor; }
}
public void ReportError(BuildError error)
@ -116,7 +123,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -116,7 +123,7 @@ namespace ICSharpCode.SharpDevelop.Project
{
WorkbenchSingleton.SafeThreadAsyncCall(
delegate {
guiBuildProgressMonitor = null;
guiBuildCancellation = null;
if (guiBuildTrackedFeature != null) {
guiBuildTrackedFeature.EndTracking();
guiBuildTrackedFeature = null;
@ -140,75 +147,6 @@ namespace ICSharpCode.SharpDevelop.Project @@ -140,75 +147,6 @@ namespace ICSharpCode.SharpDevelop.Project
});
}
}
sealed class CancellableProgressMonitor : IProgressMonitor
{
IProgressMonitor baseProgressMonitor;
public CancellableProgressMonitor(IProgressMonitor baseProgressMonitor)
{
this.baseProgressMonitor = baseProgressMonitor;
}
readonly object lockObject = new object();
bool isCancelAllowed;
bool isCancelled;
public bool IsCancelAllowed {
get { return isCancelAllowed; }
}
public bool IsCancelled {
get { return isCancelled; }
}
public void Cancel()
{
EventHandler eh = null;
lock (lockObject) {
if (isCancelAllowed && !isCancelled) {
ICSharpCode.Core.LoggingService.Info("Cancel build");
isCancelled = true;
eh = Cancelled;
}
}
if (eh != null)
eh(this, EventArgs.Empty);
}
public event EventHandler Cancelled;
public void BeginTask(string name, int totalWork, bool allowCancel)
{
baseProgressMonitor.BeginTask(name, totalWork, allowCancel);
lock (lockObject) {
isCancelAllowed = allowCancel;
}
}
public void Done()
{
lock (lockObject) {
isCancelAllowed = false;
}
baseProgressMonitor.Done();
}
public int WorkDone {
get { return baseProgressMonitor.WorkDone; }
set { baseProgressMonitor.WorkDone = value; }
}
public string TaskName {
get { return baseProgressMonitor.TaskName; }
set { baseProgressMonitor.TaskName = value; }
}
public bool ShowingDialog {
get { return baseProgressMonitor.ShowingDialog; }
set { baseProgressMonitor.ShowingDialog = value; }
}
}
#endregion
#region StartBuild
@ -220,8 +158,9 @@ namespace ICSharpCode.SharpDevelop.Project @@ -220,8 +158,9 @@ namespace ICSharpCode.SharpDevelop.Project
/// <param name="realtimeBuildFeedbackSink">The build feedback sink that receives the build output.
/// The output is nearly sent "as it comes in": sometimes output must wait because the BuildEngine
/// will ensure that output from two projects building in parallel isn't interleaved.</param>
/// <param name="progressMonitor">The progress monitor that receives build progress.</param>
public static void StartBuild(IBuildable project, BuildOptions options, IBuildFeedbackSink realtimeBuildFeedbackSink, IProgressMonitor progressMonitor)
/// <param name="progressMonitor">The progress monitor that receives build progress. The monitor will be disposed
/// when the build completes.</param>
public static void StartBuild(IBuildable project, BuildOptions options, IBuildFeedbackSink realtimeBuildFeedbackSink)
{
if (project == null)
throw new ArgumentNullException("solution");
@ -240,7 +179,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -240,7 +179,7 @@ namespace ICSharpCode.SharpDevelop.Project
BuildEngine engine = new BuildEngine(options, project);
engine.buildStart = DateTime.Now;
engine.combinedBuildFeedbackSink = realtimeBuildFeedbackSink;
engine.progressMonitor = progressMonitor;
engine.progressMonitor = realtimeBuildFeedbackSink.ProgressMonitor;
try {
engine.rootNode = engine.CreateBuildGraph(project);
} catch (CyclicDependencyException ex) {
@ -257,10 +196,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -257,10 +196,7 @@ namespace ICSharpCode.SharpDevelop.Project
if (engine.workersToStart < 1)
engine.workersToStart = 1;
if (progressMonitor != null) {
progressMonitor.Cancelled += engine.BuildCancelled;
progressMonitor.BeginTask("", engine.nodeDict.Count, true);
}
engine.cancellationRegistration = engine.progressMonitor.CancellationToken.Register(engine.BuildCancelled);
engine.ReportMessageInternal("${res:MainWindow.CompilerMessages.BuildStarted}");
engine.StartBuildProjects();
@ -292,6 +228,9 @@ namespace ICSharpCode.SharpDevelop.Project @@ -292,6 +228,9 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary>The list of nodes that directly depend on this node</summary>
internal List<BuildNode> dependentOnThis = new List<BuildNode>();
/// <summary>Progress monitor just for this node, used only while the node is being built</summary>
internal IProgressMonitor perNodeProgressMonitor;
int totalDependentOnThisCount = -1;
/// <summary>Gets the number of nodes that depend on this node
@ -336,6 +275,12 @@ namespace ICSharpCode.SharpDevelop.Project @@ -336,6 +275,12 @@ namespace ICSharpCode.SharpDevelop.Project
public void ReportError(BuildError error)
{
if (error.IsWarning) {
if (perNodeProgressMonitor.Status != OperationStatus.Error)
perNodeProgressMonitor.Status = OperationStatus.Warning;
} else {
perNodeProgressMonitor.Status = OperationStatus.Error;
}
engine.ReportError(this, error);
}
@ -348,6 +293,15 @@ namespace ICSharpCode.SharpDevelop.Project @@ -348,6 +293,15 @@ namespace ICSharpCode.SharpDevelop.Project
{
engine.OnBuildFinished(this, success);
}
IProgressMonitor IBuildFeedbackSink.ProgressMonitor {
get {
// property should be accessed only while build is running and progress monitor available
if (perNodeProgressMonitor == null)
throw new InvalidOperationException();
return perNodeProgressMonitor;
}
}
}
#endregion
@ -355,6 +309,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -355,6 +309,7 @@ namespace ICSharpCode.SharpDevelop.Project
readonly Dictionary<IBuildable, BuildNode> nodeDict = new Dictionary<IBuildable, BuildNode>();
readonly BuildOptions options;
IProgressMonitor progressMonitor;
CancellationTokenRegistration cancellationRegistration;
BuildNode rootNode;
readonly IBuildable rootProject;
readonly BuildResults results = new BuildResults();
@ -461,6 +416,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -461,6 +416,7 @@ namespace ICSharpCode.SharpDevelop.Project
}
BuildNode node = projectsReadyForBuildStart[projectToStartIndex];
projectsReadyForBuildStart.RemoveAt(projectToStartIndex);
node.perNodeProgressMonitor = progressMonitor.CreateSubTask(1.0 / nodeDict.Count);
node.buildStarted = true;
bool hasDependencyErrors = false;
@ -513,7 +469,9 @@ namespace ICSharpCode.SharpDevelop.Project @@ -513,7 +469,9 @@ namespace ICSharpCode.SharpDevelop.Project
void OnBuildFinished(BuildNode node, bool success)
{
ICSharpCode.Core.LoggingService.Info("Finished building " + node.project.Name +", success=" + success);
ICSharpCode.Core.LoggingService.Info("Finished building " + node.project.Name + ", success=" + success);
node.perNodeProgressMonitor.Progress = 1;
node.perNodeProgressMonitor.Dispose();
lock (this) {
if (node.buildFinished) {
throw new InvalidOperationException("This node already finished building, do not call IBuildFeedbackSink.Done() multiple times!");
@ -524,9 +482,6 @@ namespace ICSharpCode.SharpDevelop.Project @@ -524,9 +482,6 @@ namespace ICSharpCode.SharpDevelop.Project
projectsCurrentlyBuilding.Remove(node);
results.AddBuiltProject(node.project);
if (progressMonitor != null) {
progressMonitor.WorkDone += 1;
}
foreach (BuildNode n in node.dependentOnThis) {
n.outstandingDependencies--;
@ -571,9 +526,8 @@ namespace ICSharpCode.SharpDevelop.Project @@ -571,9 +526,8 @@ namespace ICSharpCode.SharpDevelop.Project
ReportMessageInternal("${res:MainWindow.CompilerMessages.BuildFinished}" + buildTime);
}
if (progressMonitor != null) {
progressMonitor.Done();
}
cancellationRegistration.Dispose();
progressMonitor.Dispose();
ReportDone();
}
@ -603,7 +557,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -603,7 +557,7 @@ namespace ICSharpCode.SharpDevelop.Project
#region Cancel build
bool buildIsCancelled;
void BuildCancelled(object sender, EventArgs e)
void BuildCancelled()
{
lock (this) {
if (buildIsDone)
@ -692,11 +646,9 @@ namespace ICSharpCode.SharpDevelop.Project @@ -692,11 +646,9 @@ namespace ICSharpCode.SharpDevelop.Project
void UpdateProgressTaskName()
{
lock (this) {
if (progressMonitor != null) {
progressMonitor.TaskName = "${res:MainWindow.CompilerMessages.BuildVerb} "
+ string.Join(", ", projectsCurrentlyBuilding.Select(n => n.project.Name))
+ "...";
}
progressMonitor.TaskName = StringParser.Parse("${res:MainWindow.CompilerMessages.BuildVerb} ")
+ string.Join(", ", projectsCurrentlyBuilding.Select(n => n.project.Name))
+ "...";
}
}
#endregion

29
src/Main/Base/Project/Src/Project/Converter/LanguageConverter.cs

@ -55,7 +55,7 @@ namespace ICSharpCode.SharpDevelop.Project.Converter @@ -55,7 +55,7 @@ namespace ICSharpCode.SharpDevelop.Project.Converter
}
}
protected virtual int GetRequiredWork(ProjectItem item)
protected virtual double GetRequiredWork(ProjectItem item)
{
if (item.ItemType == ItemType.Compile) {
return 50;
@ -121,13 +121,11 @@ namespace ICSharpCode.SharpDevelop.Project.Converter @@ -121,13 +121,11 @@ namespace ICSharpCode.SharpDevelop.Project.Converter
throw new ArgumentNullException("targetProjectItems");
ICollection<ProjectItem> sourceItems = sourceProject.Items;
int totalWork = 0;
double totalWork = 0;
foreach (ProjectItem item in sourceItems) {
totalWork += GetRequiredWork(item);
}
monitor.BeginTask("Converting", totalWork, true);
int workDone = 0;
foreach (ProjectItem item in sourceItems) {
FileProjectItem fileItem = item as FileProjectItem;
if (fileItem != null && FileUtility.IsBaseDirectory(sourceProject.Directory, fileItem.FileName)) {
@ -148,13 +146,9 @@ namespace ICSharpCode.SharpDevelop.Project.Converter @@ -148,13 +146,9 @@ namespace ICSharpCode.SharpDevelop.Project.Converter
} else {
targetProjectItems.AddProjectItem(item.CloneFor(targetProject));
}
if (monitor.IsCancelled) {
return;
}
workDone += GetRequiredWork(item);
monitor.WorkDone = workDone;
monitor.CancellationToken.ThrowIfCancellationRequested();
monitor.Progress += GetRequiredWork(item) / totalWork;
}
monitor.Done();
}
protected StringBuilder conversionLog;
@ -178,16 +172,23 @@ namespace ICSharpCode.SharpDevelop.Project.Converter @@ -178,16 +172,23 @@ namespace ICSharpCode.SharpDevelop.Project.Converter
conversionLog.Append(ResourceService.GetString("ICSharpCode.SharpDevelop.Commands.Convert.TargetDirectory")).Append(": ");
conversionLog.AppendLine(targetProjectDirectory);
try {
PerformConversion(translatedTitle, sourceProject, targetProjectDirectory);
} catch (OperationCanceledException) {
// ignore
}
}
void PerformConversion(string translatedTitle, MSBuildBasedProject sourceProject, string targetProjectDirectory)
{
IProject targetProject;
using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog(translatedTitle)) {
using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog(translatedTitle, "Converting", true)) {
Directory.CreateDirectory(targetProjectDirectory);
targetProject = CreateProject(targetProjectDirectory, sourceProject);
CopyProperties(sourceProject, targetProject);
conversionLog.AppendLine();
CopyItems(sourceProject, targetProject, monitor);
if (monitor.IsCancelled) {
return;
}
monitor.CancellationToken.ThrowIfCancellationRequested();
conversionLog.AppendLine();
AfterConversion(targetProject);
conversionLog.AppendLine(ResourceService.GetString("ICSharpCode.SharpDevelop.Commands.Convert.ConversionComplete"));

7
src/Main/Base/Project/Src/Project/IBuildFeedbackSink.cs

@ -15,6 +15,13 @@ namespace ICSharpCode.SharpDevelop.Project @@ -15,6 +15,13 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary>
public interface IBuildFeedbackSink
{
/// <summary>
/// Gets the progress monitor associated with this build.
/// Does not return null.
/// This member is thread-safe.
/// </summary>
Gui.IProgressMonitor ProgressMonitor { get; }
/// <summary>
/// Reports an build error by adding it to the error list.
/// This member is thread-safe.

18
src/Main/Base/Project/Src/Project/ProjectLoadInformation.cs

@ -11,7 +11,7 @@ using System; @@ -11,7 +11,7 @@ using System;
namespace ICSharpCode.SharpDevelop.Project
{
/// <summary>
/// Description of ProjectLoadInformation.
/// Parameter object for loading an existing project.
/// </summary>
public class ProjectLoadInformation
{
@ -21,7 +21,21 @@ namespace ICSharpCode.SharpDevelop.Project @@ -21,7 +21,21 @@ namespace ICSharpCode.SharpDevelop.Project
public string ProjectName { get; private set; }
public string TypeGuid { get; set; }
internal string Guid { get; set; }
public Gui.IProgressMonitor ProgressMonitor { get; set; }
Gui.IProgressMonitor progressMonitor = new Gui.DummyProgressMonitor();
/// <summary>
/// Gets/Sets the progress monitor used during the load.
/// This property never returns null.
/// </summary>
public Gui.IProgressMonitor ProgressMonitor {
get { return progressMonitor; }
set {
if (value == null)
throw new ArgumentNullException();
progressMonitor = value;
}
}
public ProjectLoadInformation(Solution parentSolution, string fileName, string projectName)
{

22
src/Main/Base/Project/Src/Project/Solution/Solution.cs

@ -511,6 +511,9 @@ namespace ICSharpCode.SharpDevelop.Project @@ -511,6 +511,9 @@ namespace ICSharpCode.SharpDevelop.Project
static ProjectSection SetupSolutionLoadSolutionProjects(Solution newSolution, StreamReader sr, IProgressMonitor progressMonitor)
{
if (progressMonitor == null)
throw new ArgumentNullException("progressMonitor");
string solutionDirectory = Path.GetDirectoryName(newSolution.FileName);
ProjectSection nestedProjectsSection = null;
@ -542,15 +545,10 @@ namespace ICSharpCode.SharpDevelop.Project @@ -542,15 +545,10 @@ namespace ICSharpCode.SharpDevelop.Project
ProjectLoadInformation loadInfo = new ProjectLoadInformation(newSolution, location, title);
loadInfo.TypeGuid = projectGuid;
loadInfo.Guid = guid;
// loadInfo.ProgressMonitor = progressMonitor;
// IProject newProject = ProjectBindingService.LoadProject(loadInfo);
// newProject.IdGuid = guid;
projectsToLoad.Add(loadInfo);
IList<ProjectSection> currentProjectSections = new List<ProjectSection>();
ReadProjectSections(sr, currentProjectSections);
readProjectSections.Add(currentProjectSections);
// newSolution.AddFolder(newProject);
}
match = match.NextMatch();
} else {
@ -578,10 +576,16 @@ namespace ICSharpCode.SharpDevelop.Project @@ -578,10 +576,16 @@ namespace ICSharpCode.SharpDevelop.Project
loadInfo.Platform = AbstractProject.GetPlatformNameFromKey(projectConfig.Location);
loadInfo.ProgressMonitor = progressMonitor;
IProject newProject = ProjectBindingService.LoadProject(loadInfo);
newProject.IdGuid = loadInfo.Guid;
newProject.ProjectSections.AddRange(projectSections);
newSolution.AddFolder(newProject);
progressMonitor.Progress = (double)i / projectsToLoad.Count;
progressMonitor.TaskName = "Loading " + loadInfo.ProjectName;
using (IProgressMonitor nestedProgressMonitor = progressMonitor.CreateSubTask(1.0 / projectsToLoad.Count)) {
loadInfo.ProgressMonitor = nestedProgressMonitor;
IProject newProject = ProjectBindingService.LoadProject(loadInfo);
newProject.IdGuid = loadInfo.Guid;
newProject.ProjectSections.AddRange(projectSections);
newSolution.AddFolder(newProject);
}
}
return nestedProjectsSection;
}

79
src/Main/Base/Project/Src/Services/ParserService/LoadSolutionProjects.cs

@ -77,8 +77,8 @@ namespace ICSharpCode.SharpDevelop @@ -77,8 +77,8 @@ namespace ICSharpCode.SharpDevelop
if (!reParse1.Contains(pc)) {
LoggingService.Debug("Enqueue for reinitializing references: " + project);
reParse1.Add(pc);
jobs.AddJob(new JobTask(ct => ReInitializeReferences(pc, ct),
"Loading references for " + project.Name + "...",
jobs.AddJob(new JobTask(pm => ReInitializeReferences(pc, pm),
GetLoadReferenceTaskTitle(project.Name),
10
));
}
@ -89,8 +89,8 @@ namespace ICSharpCode.SharpDevelop @@ -89,8 +89,8 @@ namespace ICSharpCode.SharpDevelop
if (!reParse2.Contains(pc)) {
LoggingService.Debug("Enqueue for reparsing code: " + project);
reParse2.Add(pc);
jobs.AddJob(new JobTask(ct => ReparseCode(pc, ct),
"${res:ICSharpCode.SharpDevelop.Internal.ParserService.Parsing} " + project.Name + "...",
jobs.AddJob(new JobTask(pm => ReparseCode(pc, pm),
GetParseTaskTitle(project.Name),
pc.GetInitializationWorkAmount()
));
}
@ -100,20 +100,20 @@ namespace ICSharpCode.SharpDevelop @@ -100,20 +100,20 @@ namespace ICSharpCode.SharpDevelop
}
}
static void ReInitializeReferences(ParseProjectContent pc, CancellationToken ct)
static void ReInitializeReferences(ParseProjectContent pc, IProgressMonitor progressMonitor)
{
lock (reParse1) {
reParse1.Remove(pc);
}
pc.ReInitialize1(ct);
pc.ReInitialize1(progressMonitor);
}
static void ReparseCode(ParseProjectContent pc, CancellationToken ct)
static void ReparseCode(ParseProjectContent pc, IProgressMonitor progressMonitor)
{
lock (reParse2) {
reParse2.Remove(pc);
}
pc.ReInitialize2(ct);
pc.ReInitialize2(progressMonitor);
}
#endregion
@ -132,26 +132,36 @@ namespace ICSharpCode.SharpDevelop @@ -132,26 +132,36 @@ namespace ICSharpCode.SharpDevelop
for (int i = 0; i < createdContents.Count; i++) {
ParseProjectContent pc = createdContents[i];
jobs.AddJob(new JobTask(pc.Initialize1,
"Loading references for " + pc.ProjectName + "...",
GetLoadReferenceTaskTitle(pc.ProjectName),
10));
}
for (int i = 0; i < createdContents.Count; i++) {
ParseProjectContent pc = createdContents[i];
jobs.AddJob(new JobTask(pc.Initialize2,
"${res:ICSharpCode.SharpDevelop.Internal.ParserService.Parsing} " + pc.ProjectName + "...",
GetParseTaskTitle(pc.ProjectName),
pc.GetInitializationWorkAmount()));
}
jobs.AddJob(new JobTask(ct => RaiseThreadEnded(openedSolution), "", 0));
jobs.StartRunningIfRequired();
}
static string GetLoadReferenceTaskTitle(string projectName)
{
return "Loading references for " + projectName + "...";
}
static string GetParseTaskTitle(string projectName)
{
return StringParser.Parse("${res:ICSharpCode.SharpDevelop.Internal.ParserService.Parsing} ") + projectName + "...";
}
internal static void InitNewProject(ParseProjectContent pc)
{
jobs.AddJob(new JobTask(pc.Initialize1,
"Loading references for " + pc.ProjectName + "...",
GetLoadReferenceTaskTitle(pc.ProjectName),
10));
jobs.AddJob(new JobTask(pc.Initialize2,
"${res:ICSharpCode.SharpDevelop.Internal.ParserService.Parsing} " + pc.ProjectName + "...",
GetParseTaskTitle(pc.ProjectName),
pc.GetInitializationWorkAmount()));
jobs.StartRunningIfRequired();
}
@ -171,11 +181,11 @@ namespace ICSharpCode.SharpDevelop @@ -171,11 +181,11 @@ namespace ICSharpCode.SharpDevelop
{
readonly object lockObj = new object();
readonly Queue<JobTask> actions = new Queue<JobTask>();
readonly IProgressMonitor progressMonitor = StatusBarService.CreateProgressMonitor();
CancellationTokenSource cancellationSource = new CancellationTokenSource();
IProgressMonitor progressMonitor;
bool threadIsRunning;
int totalWork;
int workDone;
double totalWork;
double workDone;
public void AddJob(JobTask task)
{
@ -193,8 +203,8 @@ namespace ICSharpCode.SharpDevelop @@ -193,8 +203,8 @@ namespace ICSharpCode.SharpDevelop
if (!this.threadIsRunning && this.actions.Count > 0) {
this.threadIsRunning = true;
progressMonitor.BeginTask(this.actions.Peek().name, totalWork, false);
progressMonitor.WorkDone = 0;
progressMonitor = StatusBarService.CreateProgressMonitor(cancellationSource.Token);
progressMonitor.TaskName = this.actions.Peek().name;
Thread thread = new Thread(new ThreadStart(RunThread));
thread.Name = "LoadSolutionProjects";
@ -208,26 +218,29 @@ namespace ICSharpCode.SharpDevelop @@ -208,26 +218,29 @@ namespace ICSharpCode.SharpDevelop
void RunThread()
{
while (true) {
CancellationToken token;
JobTask task;
int totalWork, workDone;
// copy fields from this class into local variables to ensure thread-safety
// with concurrent Clear() calls
double totalWork, workDone;
IProgressMonitor progressMonitor;
lock (lockObj) {
if (actions.Count == 0) {
this.threadIsRunning = false;
progressMonitor.Done();
this.progressMonitor.Dispose();
this.progressMonitor = null;
return;
}
task = this.actions.Dequeue();
token = this.cancellationSource.Token;
totalWork = this.totalWork;
workDone = this.workDone;
progressMonitor = this.progressMonitor;
}
if (task.cost > 0) {
progressMonitor.BeginTask(task.name, totalWork, false);
progressMonitor.WorkDone = workDone;
}
progressMonitor.Progress = workDone / totalWork;
progressMonitor.TaskName = task.name;
try {
task.Run(token);
using (IProgressMonitor subTask = progressMonitor.CreateSubTask(task.cost / totalWork)) {
task.Run(subTask);
}
lock (lockObj) {
this.workDone += task.cost;
}
@ -245,6 +258,10 @@ namespace ICSharpCode.SharpDevelop @@ -245,6 +258,10 @@ namespace ICSharpCode.SharpDevelop
cancellationSource.Cancel();
actions.Clear();
cancellationSource = new CancellationTokenSource();
if (progressMonitor != null) {
progressMonitor.Dispose();
progressMonitor = null;
}
this.totalWork = 0;
this.workDone = 0;
}
@ -253,11 +270,11 @@ namespace ICSharpCode.SharpDevelop @@ -253,11 +270,11 @@ namespace ICSharpCode.SharpDevelop
sealed class JobTask
{
readonly Action<CancellationToken> action;
readonly Action<IProgressMonitor> action;
internal readonly string name;
internal readonly int cost;
internal readonly double cost;
public JobTask(Action<CancellationToken> action, string name, int cost)
public JobTask(Action<IProgressMonitor> action, string name, double cost)
{
if (action == null)
throw new ArgumentNullException("action");
@ -266,9 +283,9 @@ namespace ICSharpCode.SharpDevelop @@ -266,9 +283,9 @@ namespace ICSharpCode.SharpDevelop
this.cost = cost;
}
public void Run(CancellationToken token)
public void Run(IProgressMonitor progressMonitor)
{
action(token);
action(progressMonitor);
}
}
}

16
src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs

@ -54,7 +54,7 @@ namespace ICSharpCode.SharpDevelop @@ -54,7 +54,7 @@ namespace ICSharpCode.SharpDevelop
return string.Format("[{0}: {1}]", GetType().Name, project.Name);
}
internal void Initialize1(CancellationToken token)
internal void Initialize1(IProgressMonitor progressMonitor)
{
ICollection<ProjectItem> items = project.Items;
ProjectService.ProjectItemAdded += OnProjectItemAdded;
@ -65,7 +65,7 @@ namespace ICSharpCode.SharpDevelop @@ -65,7 +65,7 @@ namespace ICSharpCode.SharpDevelop
project.ResolveAssemblyReferences();
foreach (ProjectItem item in items) {
if (!initializing) return; // abort initialization
token.ThrowIfCancellationRequested();
progressMonitor.CancellationToken.ThrowIfCancellationRequested();
if (ItemType.ReferenceItemTypes.Contains(item.ItemType)) {
ReferenceProjectItem reference = item as ReferenceProjectItem;
if (reference != null) {
@ -79,7 +79,7 @@ namespace ICSharpCode.SharpDevelop @@ -79,7 +79,7 @@ namespace ICSharpCode.SharpDevelop
OnReferencedContentsChanged(EventArgs.Empty);
}
internal void ReInitialize1(CancellationToken token)
internal void ReInitialize1(IProgressMonitor progressMonitor)
{
lock (ReferencedContents) {
ReferencedContents.Clear();
@ -90,7 +90,7 @@ namespace ICSharpCode.SharpDevelop @@ -90,7 +90,7 @@ namespace ICSharpCode.SharpDevelop
ProjectService.ProjectItemRemoved -= OnProjectItemRemoved;
initializing = true;
try {
Initialize1(token);
Initialize1(progressMonitor);
} finally {
initializing = false;
}
@ -236,14 +236,14 @@ namespace ICSharpCode.SharpDevelop @@ -236,14 +236,14 @@ namespace ICSharpCode.SharpDevelop
return project.Items.Count;
}
internal void ReInitialize2(CancellationToken token)
internal void ReInitialize2(IProgressMonitor progressMonitor)
{
if (initializing) return;
initializing = true;
Initialize2(token);
Initialize2(progressMonitor);
}
internal void Initialize2(CancellationToken token)
internal void Initialize2(IProgressMonitor progressMonitor)
{
if (!initializing) return;
//int progressStart = progressMonitor.WorkDone;
@ -262,7 +262,7 @@ namespace ICSharpCode.SharpDevelop @@ -262,7 +262,7 @@ namespace ICSharpCode.SharpDevelop
ParseableFileContentFinder finder = new ParseableFileContentFinder();
var fileContents =
from p in project.Items.AsParallel().WithCancellation(token)
from p in project.Items.AsParallel().WithCancellation(progressMonitor.CancellationToken)
where !ItemType.NonFileItemTypes.Contains(p.ItemType) && !String.IsNullOrEmpty(p.FileName)
select finder.Create(p);
fileContents.ForAll(

12
src/Main/Base/Project/Src/Services/ProjectBinding/ProjectBindingService.cs

@ -97,9 +97,7 @@ namespace ICSharpCode.SharpDevelop @@ -97,9 +97,7 @@ namespace ICSharpCode.SharpDevelop
string title = loadInformation.ProjectName;
IProgressMonitor progressMonitor = loadInformation.ProgressMonitor;
if (progressMonitor != null) {
progressMonitor.BeginTask("Loading " + title, 0, false);
}
progressMonitor.CancellationToken.ThrowIfCancellationRequested();
IProject newProject;
if (!File.Exists(location)) {
@ -112,16 +110,16 @@ namespace ICSharpCode.SharpDevelop @@ -112,16 +110,16 @@ namespace ICSharpCode.SharpDevelop
newProject = binding.LoadProject(loadInformation);
} catch (ProjectLoadException ex) {
LoggingService.Warn("Project load error", ex);
if (progressMonitor != null) progressMonitor.ShowingDialog = true;
progressMonitor.ShowingDialog = true;
newProject = new UnknownProject(location, title, ex.Message, true);
newProject.TypeGuid = loadInformation.TypeGuid;
if (progressMonitor != null) progressMonitor.ShowingDialog = false;
progressMonitor.ShowingDialog = false;
} catch (UnauthorizedAccessException ex) {
LoggingService.Warn("Project load error", ex);
if (progressMonitor != null) progressMonitor.ShowingDialog = true;
progressMonitor.ShowingDialog = true;
newProject = new UnknownProject(location, title, ex.Message, true);
newProject.TypeGuid = loadInformation.TypeGuid;
if (progressMonitor != null) progressMonitor.ShowingDialog = false;
progressMonitor.ShowingDialog = false;
}
} else {
string ext = Path.GetExtension(location);

4
src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs

@ -297,6 +297,10 @@ namespace ICSharpCode.SharpDevelop.Project @@ -297,6 +297,10 @@ namespace ICSharpCode.SharpDevelop.Project
this.currentPass = currentPass;
}
public Gui.IProgressMonitor ProgressMonitor {
get { return sink.ProgressMonitor; }
}
public void ReportError(BuildError error)
{
sink.ReportError(error);

35
src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs

@ -154,32 +154,25 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -154,32 +154,25 @@ namespace ICSharpCode.SharpDevelop.Refactoring
}
ParseableFileContentFinder finder = new ParseableFileContentFinder();
List<Reference> references = new List<Reference>();
try {
if (progressMonitor != null) {
progressMonitor.BeginTask("${res:SharpDevelop.Refactoring.FindingReferences}", files.Count, true);
}
int index = 0;
foreach (ProjectItem item in files) {
var entry = finder.Create(item);
if (progressMonitor != null) {
progressMonitor.WorkDone = index;
if (progressMonitor.IsCancelled) {
return null;
}
}
if (progressMonitor != null)
progressMonitor.TaskName = StringParser.Parse("${res:SharpDevelop.Refactoring.FindingReferences}");
foreach (ProjectItem item in files) {
var entry = finder.Create(item);
ITextBuffer content = entry.GetContent();
if (content != null) {
AddReferences(references, ownerClass, member, isLocal, entry.FileName, content.Text);
}
index++;
}
} finally {
if (progressMonitor != null) {
progressMonitor.Done();
progressMonitor.Progress += 1.0 / files.Count;
if (progressMonitor.CancellationToken.IsCancellationRequested)
return null;
}
ITextBuffer content = entry.GetContent();
if (content != null) {
AddReferences(references, ownerClass, member, isLocal, entry.FileName, content.Text);
}
}
return references;
}

130
src/Main/Base/Project/Src/Services/StatusBar/StatusBarService.cs

@ -7,10 +7,13 @@ @@ -7,10 +7,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Media.Imaging;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui;
@ -97,107 +100,58 @@ namespace ICSharpCode.SharpDevelop @@ -97,107 +100,58 @@ namespace ICSharpCode.SharpDevelop
Visible = PropertyService.Get("ICSharpCode.SharpDevelop.Gui.StatusBarVisible", true);
}
public static void Update()
{
System.Diagnostics.Debug.Assert(statusBar != null);
/* statusBar.Panels.Clear();
statusBar.Controls.Clear();
foreach (StatusBarContributionItem item in Items) {
if (item.Control != null) {
statusBar.Controls.Add(item.Control);
} else if (item.Panel != null) {
statusBar.Panels.Add(item.Panel);
} else {
throw new ApplicationException("StatusBarContributionItem " + item.ItemID + " has no Control or Panel defined.");
}
}*/
}
#region Progress Monitor
static HashSet<StatusBarProgressMonitor> activeProgressMonitors = new HashSet<StatusBarProgressMonitor>();
static StatusBarProgressMonitor currentProgressMonitor;
static Stack<ProgressCollector> waitingProgresses = new Stack<ProgressCollector>();
static ProgressCollector currentProgress;
public static IProgressMonitor CreateProgressMonitor()
public static IProgressMonitor CreateProgressMonitor(CancellationToken cancellationToken)
{
System.Diagnostics.Debug.Assert(statusBar != null);
return new StatusBarProgressMonitor();
ProgressCollector progress = new ProgressCollector(WorkbenchSingleton.Workbench.SynchronizingObject, cancellationToken);
AddProgress(progress);
return progress.ProgressMonitor;
}
sealed class StatusBarProgressMonitor : IProgressMonitor
public static void AddProgress(ProgressCollector progress)
{
int workDone, totalWork;
public int WorkDone {
get { return workDone; }
set {
if (workDone == value)
return;
workDone = value;
lock (activeProgressMonitors) {
if (currentProgressMonitor == this) {
UpdateDisplay();
}
}
}
}
void UpdateDisplay()
{
statusBar.DisplayProgress(taskName, workDone, totalWork);
}
string taskName;
public string TaskName {
get { return taskName; }
set {
if (taskName == value)
return;
taskName = value;
lock (activeProgressMonitors) {
if (currentProgressMonitor == this) {
UpdateDisplay();
}
}
}
if (progress == null)
throw new ArgumentNullException("progress");
System.Diagnostics.Debug.Assert(statusBar != null);
WorkbenchSingleton.AssertMainThread();
if (currentProgress != null) {
currentProgress.ProgressMonitorDisposed -= progress_ProgressMonitorDisposed;
currentProgress.PropertyChanged -= progress_PropertyChanged;
}
waitingProgresses.Push(currentProgress); // push even if currentProgress==null
SetActiveProgress(progress);
}
public bool ShowingDialog { get; set; }
public bool IsCancelled {
get { return false; }
static void SetActiveProgress(ProgressCollector progress)
{
WorkbenchSingleton.AssertMainThread();
currentProgress = progress;
if (progress == null) {
statusBar.HideProgress();
return;
}
public void BeginTask(string name, int totalWork, bool allowCancel)
{
lock (activeProgressMonitors) {
activeProgressMonitors.Add(this);
currentProgressMonitor = this;
this.taskName = name;
this.workDone = 0;
this.totalWork = totalWork;
UpdateDisplay();
}
progress.ProgressMonitorDisposed += progress_ProgressMonitorDisposed;
if (progress.ProgressMonitorIsDisposed) {
progress_ProgressMonitorDisposed(progress, null);
return;
}
progress.PropertyChanged += progress_PropertyChanged;
}
public void Done()
{
lock (activeProgressMonitors) {
activeProgressMonitors.Remove(this);
if (currentProgressMonitor == this) {
if (activeProgressMonitors.Count > 0) {
currentProgressMonitor = activeProgressMonitors.First();
currentProgressMonitor.UpdateDisplay();
} else {
currentProgressMonitor = null;
statusBar.HideProgress();
}
}
}
}
static void progress_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Debug.Assert(sender == currentProgress);
statusBar.DisplayProgress(currentProgress.TaskName, currentProgress.Progress, currentProgress.Status);
}
public event EventHandler Cancelled { add { } remove { } }
static void progress_ProgressMonitorDisposed(object sender, EventArgs e)
{
Debug.Assert(sender == currentProgress);
SetActiveProgress(waitingProgresses.Pop()); // stack is never empty: we push null as first element
}
#endregion
}

Loading…
Cancel
Save