diff options
author | Mikayla Hutchinson <m.j.hutchinson@gmail.com> | 2016-11-05 01:49:29 +0300 |
---|---|---|
committer | Mikayla Hutchinson <m.j.hutchinson@gmail.com> | 2016-11-05 01:49:29 +0300 |
commit | 45f2b9092af4e89ac60ecab2e80ab73a8ed95537 (patch) | |
tree | b265fc6edc399d895350be1082eb023d84ce3ae0 /main | |
parent | d4e86ee9c8d321f012427fae99f1239842ed9f13 (diff) | |
parent | e4fad978da2dd6c1e6850a3c19f0e4affd2ffe38 (diff) |
Merge remote-tracking branch 'origin/multisessiondebugging' into vNext
Diffstat (limited to 'main')
39 files changed, 1587 insertions, 1188 deletions
diff --git a/main/src/addins/AspNet/Projects/AspNetAppProjectFlavor.cs b/main/src/addins/AspNet/Projects/AspNetAppProjectFlavor.cs index 7074fa24b2..bffd2966e8 100644 --- a/main/src/addins/AspNet/Projects/AspNetAppProjectFlavor.cs +++ b/main/src/addins/AspNet/Projects/AspNetAppProjectFlavor.cs @@ -171,7 +171,7 @@ namespace MonoDevelop.AspNet.Projects var cfg = GetConfiguration (configuration); var cmd = CreateExecutionCommand (configuration, cfg); - var browserExcTarget = (BrowserExecutionTarget) context.ExecutionTarget; + var browserExcTarget = context.ExecutionTarget as BrowserExecutionTarget; OperationConsole console = null; diff --git a/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs b/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs index a8c17936d5..2d7988e3e5 100644 --- a/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs +++ b/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs @@ -235,11 +235,14 @@ namespace MonoDevelop.MacIntegration.MainToolbar var menu = new NSMenu { AutoEnablesItems = false, - ShowsStateColumn = false, + ShowsStateColumn = true, Font = NSFont.MenuFontOfSize (12), }; foreach (var item in runtime.Children) - CreateMenuItem (menu, item); + if (item.IsSeparator) + menu.AddItem (NSMenuItem.SeparatorItem); + else + CreateMenuItem (menu, item); return menu; } @@ -251,14 +254,17 @@ namespace MonoDevelop.MacIntegration.MainToolbar using (var mutableModel = runtime.GetMutableModel ()) { runtimeFullDisplayString = mutableModel.FullDisplayString; - menuItem = new NSMenuItem { - IndentationLevel = runtime.IsIndented ? 2 : 1, + menuItem = new NSMenuItem () { + IndentationLevel = runtime.IsIndented ? 1 : 0, AttributedTitle = new NSAttributedString (mutableModel.DisplayString, new NSStringAttributes { Font = runtime.Notable ? NSFontManager.SharedFontManager.ConvertFont (menu.Font, NSFontTraitMask.Bold) : menu.Font, }), Enabled = mutableModel.Enabled, Hidden = !mutableModel.Visible, }; + if (ActiveRuntime == runtime || (ActiveRuntime?.Children.Contains (runtime) ?? false)) { + menuItem.State = NSCellStateValue.On; + } } var subMenu = CreateSubMenuForRuntime (runtime); @@ -267,27 +273,15 @@ namespace MonoDevelop.MacIntegration.MainToolbar menuItem.Enabled = true; } else { menuItem.Activated += (o2, e2) => { - string old; - using (var activeMutableModel = ActiveRuntime.GetMutableModel ()) - old = activeMutableModel.FullDisplayString; - - IRuntimeModel newRuntime = runtimeModel.FirstOrDefault (r => { - using (var newRuntimeMutableModel = r.GetMutableModel ()) - return newRuntimeMutableModel.FullDisplayString == runtimeFullDisplayString; - }); - if (newRuntime == null) - return; - - ActiveRuntime = newRuntime; + var old = ActiveRuntime; + + ActiveRuntime = runtime; var ea = new HandledEventArgs (); if (RuntimeChanged != null) RuntimeChanged (o2, ea); if (ea.Handled) - ActiveRuntime = runtimeModel.First (r => { - using (var newRuntimeMutableModel = r.GetMutableModel ()) - return newRuntimeMutableModel.FullDisplayString == old; - }); + ActiveRuntime = old; }; } menu.AddItem (menuItem); @@ -388,7 +382,7 @@ namespace MonoDevelop.MacIntegration.MainToolbar NSMenuItem selectedItem = null; var menu = new NSMenu { AutoEnablesItems = false, - ShowsStateColumn = false, + ShowsStateColumn = true, Font = NSFont.MenuFontOfSize (12), }; if (cellIdx == RunConfigurationIdx) { @@ -435,9 +429,6 @@ namespace MonoDevelop.MacIntegration.MainToolbar using (var activeMutableModel = ActiveRuntime.GetMutableModel ()) { foreach (var runtime in RuntimeModel) { - if (runtime.HasParent) - continue; - NSMenuItem menuitem = null; if (runtime.IsSeparator) menu.AddItem (NSMenuItem.SeparatorItem); diff --git a/main/src/addins/MonoDevelop.Debugger.Win32/CorApi2/Extensions/ProcessExtensions.cs b/main/src/addins/MonoDevelop.Debugger.Win32/CorApi2/Extensions/ProcessExtensions.cs index de197db461..6bbd517df2 100644 --- a/main/src/addins/MonoDevelop.Debugger.Win32/CorApi2/Extensions/ProcessExtensions.cs +++ b/main/src/addins/MonoDevelop.Debugger.Win32/CorApi2/Extensions/ProcessExtensions.cs @@ -79,9 +79,11 @@ namespace Microsoft.Samples.Debugging.Extensions break; // pipe done - normal exit path.
string s = System.Text.Encoding.Default.GetString (buffer, 0, nBytesRead);
- if (OnStdOutput != null)
- OnStdOutput (proc, new CorTargetOutputEventArgs (s, isStdError));
- }
+ List<CorTargetOutputEventHandler> list;
+ if (events.TryGetValue(proc, out list))
+ foreach (var del in list)
+ del(proc, new CorTargetOutputEventArgs(s, isStdError));
+ }
} catch {
}
}
@@ -98,19 +100,14 @@ namespace Microsoft.Samples.Debugging.Extensions list.Add (handler);
events [proc] = list;
- OnStdOutput += handler;
}
static void RemoveEventsFor (CorProcess proc)
{
- foreach (CorTargetOutputEventHandler handler in events [proc])
- OnStdOutput -= handler;
-
events.Remove (proc);
}
// [Xamarin] Output redirection.
- static event CorTargetOutputEventHandler OnStdOutput;
static readonly Dictionary<CorProcess, List<CorTargetOutputEventHandler>> events = new Dictionary<CorProcess, List<CorTargetOutputEventHandler>> ();
}
}
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs index c5f96272fa..c3ce656268 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs @@ -77,28 +77,6 @@ namespace MonoDevelop.Debugger return IdeApp.ProjectOperations.CurrentSelectedSolution ?? IdeApp.ProjectOperations.CurrentSelectedBuildTarget; } - internal async static void BuildAndDebug () - { - if (!DebuggingService.IsDebuggingSupported && !IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted) { - MonoDevelop.Ide.Commands.StopHandler.StopBuildOperations (); - await IdeApp.ProjectOperations.CurrentRunOperation.Task; - } - - if (IdeApp.Workspace.IsOpen) { - var it = GetRunTarget (); - ExecuteSolution (it); - return; - } - } - - static void ExecuteSolution (IBuildTarget target) - { - if (IdeApp.ProjectOperations.CanDebug (target)) - IdeApp.ProjectOperations.Debug (target); - else - IdeApp.ProjectOperations.Execute (target); - } - protected override void Run () { if (DebuggingService.IsPaused) { @@ -106,41 +84,32 @@ namespace MonoDevelop.Debugger return; } - BuildAndDebug (); + if (IdeApp.Workspace.IsOpen) { + var target = GetRunTarget (); + if (target != null) + IdeApp.ProjectOperations.Debug (target); + } } - + protected override void Update (CommandInfo info) { - if (DebuggingService.IsRunning) { + if (!IdeApp.Workspace.IsOpen || !DebuggingService.IsDebuggingSupported) { info.Enabled = false; return; } - if (DebuggingService.IsPaused) { info.Enabled = true; info.Text = GettextCatalog.GetString ("_Continue Debugging"); info.Description = GettextCatalog.GetString ("Continue the execution of the application"); return; } - - // If there are no debugger installed, this command will not debug, it will - // just run, so the label has to be changed accordingly. - if (!DebuggingService.IsDebuggingSupported) { - info.Text = IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted ? GettextCatalog.GetString ("Start Without Debugging") : GettextCatalog.GetString ("Restart Without Debugging"); - info.Icon = Stock.RunProgramIcon; - } - - if (IdeApp.Workspace.IsOpen) { - var target = GetRunTarget (); - bool canExecute = target != null && ( - IdeApp.ProjectOperations.CanDebug (target) || - (!DebuggingService.IsDebuggingSupported && IdeApp.ProjectOperations.CanExecute (target)) - ); - - info.Enabled = canExecute && (IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted || !DebuggingService.IsDebuggingSupported); - } else { + if (DebuggingService.IsDebugging) { info.Enabled = false; + return; } + + var target = GetRunTarget (); + info.Enabled = target != null && IdeApp.ProjectOperations.CanDebug (target); } } @@ -156,9 +125,7 @@ namespace MonoDevelop.Debugger protected override void Update (CommandInfo info) { IBuildTarget target = IdeApp.ProjectOperations.CurrentSelectedBuildTarget; - info.Enabled = target != null && - !(target is Workspace) && IdeApp.ProjectOperations.CanDebug (target) && - IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted; + info.Enabled = target != null && !(target is Workspace) && IdeApp.ProjectOperations.CanDebug (target); } } @@ -207,8 +174,7 @@ namespace MonoDevelop.Debugger protected override void Update (CommandInfo info) { - info.Enabled = IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted; - info.Visible = DebuggingService.IsFeatureSupported (DebuggerFeatures.DebugFile); + info.Enabled = info.Visible = DebuggingService.IsFeatureSupported (DebuggerFeatures.DebugFile); } } @@ -229,8 +195,7 @@ namespace MonoDevelop.Debugger protected override void Update (CommandInfo info) { - info.Enabled = IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted; - info.Visible = DebuggingService.IsFeatureSupported (DebuggerFeatures.Attaching); + info.Enabled = info.Visible = DebuggingService.IsFeatureSupported (DebuggerFeatures.Attaching); } } @@ -315,7 +280,7 @@ namespace MonoDevelop.Debugger protected override void Update (CommandInfo info) { - info.Visible = !DebuggingService.IsRunning; + info.Visible = DebuggingService.IsPaused; info.Enabled = DebuggingService.IsConnected && DebuggingService.IsPaused; } } @@ -577,33 +542,34 @@ namespace MonoDevelop.Debugger return; } - var bp = new RunToCursorBreakpoint (doc.FileName, doc.Editor.CaretLine, doc.Editor.CaretColumn); - DebuggingService.Breakpoints.Add (bp); - DebugHandler.BuildAndDebug (); + if (IdeApp.Workspace.IsOpen) { + var bp = new RunToCursorBreakpoint (doc.FileName, doc.Editor.CaretLine, doc.Editor.CaretColumn); + DebuggingService.Breakpoints.Add (bp); + var target = DebugHandler.GetRunTarget (); + if (target != null) + IdeApp.ProjectOperations.Debug (target); + } } protected override void Update (CommandInfo info) { info.Visible = true; - if (!DebuggingService.IsDebuggingSupported || !DebuggingService.IsFeatureSupported (DebuggerFeatures.Breakpoints) || DebuggingService.Breakpoints.IsReadOnly) { + if (!IdeApp.Workspace.IsOpen || !DebuggingService.IsDebuggingSupported || !DebuggingService.IsFeatureSupported (DebuggerFeatures.Breakpoints) || DebuggingService.Breakpoints.IsReadOnly) { info.Enabled = false; return; } var doc = IdeApp.Workbench.ActiveDocument; - if (doc != null && doc.Editor != null && doc.FileName != FilePath.Null) { - if (IdeApp.Workspace.IsOpen) { - var target = DebugHandler.GetRunTarget (); - - info.Enabled = target != null && IdeApp.ProjectOperations.CanDebug (target); - } else { - info.Enabled = false; + if (doc?.Editor != null && doc.FileName != FilePath.Null) { + var target = DebugHandler.GetRunTarget (); + if (target != null && IdeApp.ProjectOperations.CanDebug (target)) { + info.Enabled = true; + return; } - } else { - info.Enabled = false; } + info.Enabled = false; } } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugExecutionHandlerFactory.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugExecutionHandlerFactory.cs index fb30399b30..1dc7d699d2 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugExecutionHandlerFactory.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugExecutionHandlerFactory.cs @@ -56,9 +56,11 @@ namespace MonoDevelop.Debugger class DebugAsyncOperation: ProcessAsyncOperation { TaskCompletionSource<int> taskSource; + DebuggerSession session; - public DebugAsyncOperation () + public DebugAsyncOperation (DebuggerSession session) { + this.session = session; taskSource = new TaskCompletionSource<int> (); DebuggingService.StoppedEvent += OnStopDebug; CancellationTokenSource = new CancellationTokenSource (); @@ -73,11 +75,12 @@ namespace MonoDevelop.Debugger taskSource = null; } DebuggingService.StoppedEvent -= OnStopDebug; + session = null; } void OnStopDebug (object sender, EventArgs args) { - if (taskSource != null) { + if (taskSource != null && session == sender) { taskSource.SetResult (0); taskSource = null; } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs index c3c9c248ca..7bfe017058 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs @@ -46,52 +46,48 @@ using MonoDevelop.Debugger.Viewers; using MonoDevelop.Ide.TextEditing; using System.Linq; using System.Threading.Tasks; -using MonoDevelop.Ide.TypeSystem; +using System.Collections.Concurrent; +using System.Threading; namespace MonoDevelop.Debugger { public static class DebuggingService { const string FactoriesPath = "/MonoDevelop/Debugging/DebuggerEngines"; - static DebuggerEngine[] engines; - + static DebuggerEngine [] engines; + const string EvaluatorsPath = "/MonoDevelop/Debugging/Evaluators"; static Dictionary<string, ExpressionEvaluatorExtensionNode> evaluators; static readonly PinnedWatchStore pinnedWatches = new PinnedWatchStore (); static readonly BreakpointStore breakpoints = new BreakpointStore (); static readonly DebugExecutionHandlerFactory executionHandlerFactory; - - static OperationConsole console; - static IDisposable cancelRegistration; static Dictionary<long, SourceLocation> nextStatementLocations = new Dictionary<long, SourceLocation> (); - static DebuggerEngine currentEngine; - static DebuggerSession session; + static Dictionary<DebuggerSession, SessionManager> sessions = new Dictionary<DebuggerSession, SessionManager> (); static Backtrace currentBacktrace; + static SessionManager currentSession; static int currentFrame; - + static ExceptionCaughtMessage exceptionDialog; - + static BusyEvaluator busyEvaluator; static StatusBarIcon busyStatusIcon; static bool isBusy; - static DebugAsyncOperation currentDebugOperation = new DebugAsyncOperation (); - static public event EventHandler DebugSessionStarted; static public event EventHandler PausedEvent; static public event EventHandler ResumedEvent; static public event EventHandler StoppedEvent; - + static public event EventHandler CallStackChanged; static public event EventHandler CurrentFrameChanged; static public event EventHandler ExecutionLocationChanged; static public event EventHandler DisassemblyRequested; static public event EventHandler<DocumentEventArgs> DisableConditionalCompilation; - + static public event EventHandler EvaluationOptionsChanged; - + static DebuggingService () { executionHandlerFactory = new DebugExecutionHandlerFactory (); @@ -110,30 +106,54 @@ namespace MonoDevelop.Debugger // Refresh the evaluators list evaluators = null; }); - } + } public static IExecutionHandler GetExecutionHandler () { return executionHandlerFactory; } - + public static DebuggerSession DebuggerSession { - get { return session; } + get { return currentSession?.Session; } } - + + + public static DebuggerSession [] GetSessions () + { + return sessions.Keys.ToArray (); + } + + public static ProcessInfo [] GetProcesses () + { + return sessions.Keys.Where (s => !s.IsRunning).SelectMany (s => s.GetProcesses ()).ToArray (); + } + + public static BreakEventStatus GetBreakpointStatus (Breakpoint bp) + { + var result = BreakEventStatus.Disconnected; + foreach (var sesion in sessions.Keys.ToArray ()) { + var status = bp.GetStatus (sesion); + if (status == BreakEventStatus.Bound) + return BreakEventStatus.Bound; + else + result = status; + } + return result; + } + public static BreakpointStore Breakpoints { get { return breakpoints; } } - + public static PinnedWatchStore PinnedWatches { get { return pinnedWatches; } } - + public static void SetLiveUpdateMode (PinnedWatch watch, bool liveUpdate) { if (watch.LiveUpdate == liveUpdate) return; - + watch.LiveUpdate = liveUpdate; if (liveUpdate) { var bp = new Breakpoint (watch.File, watch.Line); @@ -149,19 +169,10 @@ namespace MonoDevelop.Debugger breakpoints.Remove (watch.BoundTracer); } } - - static void BreakpointTraceHandler (BreakEvent be, string trace) - { - if (be is Breakpoint) { - if (pinnedWatches.UpdateLiveWatch ((Breakpoint) be, trace)) - return; // No need to log the value. It is shown in the watch. - } - DebugWriter (0, "", trace + Environment.NewLine); - } [Obsolete] - public static string[] EnginePriority { - get { return new string[0]; } + public static string [] EnginePriority { + get { return new string [0]; } set { } } @@ -245,7 +256,7 @@ namespace MonoDevelop.Debugger { return GetSetConverter<T> (val) != null; } - + public static void ShowValueVisualizer (ObjectValue val) { using (var dlg = new ValueVisualizerDialog ()) { @@ -258,7 +269,7 @@ namespace MonoDevelop.Debugger { PreviewWindowManager.Show (val, widget, previewButtonArea); } - + public static bool ShowBreakpointProperties (ref BreakEvent bp, BreakpointType breakpointType = BreakpointType.Location) { using (var dlg = new BreakpointPropertiesDialog (bp, breakpointType)) { @@ -272,7 +283,7 @@ namespace MonoDevelop.Debugger public static void AddWatch (string expression) { var pad = IdeApp.Workbench.GetPad<WatchPad> (); - var wp = (WatchPad) pad.Content; + var wp = (WatchPad)pad.Content; pad.BringToFront (false); wp.AddWatch (expression); @@ -291,7 +302,7 @@ namespace MonoDevelop.Debugger public static bool CurrentSessionSupportsFeature (DebuggerFeatures feature) { - return (currentEngine.SupportedFeatures & feature) == feature; + return (currentSession.Engine.SupportedFeatures & feature) == feature; } public static bool IsFeatureSupported (DebuggerFeatures feature) @@ -305,7 +316,7 @@ namespace MonoDevelop.Debugger public static DebuggerFeatures GetSupportedFeatures (IBuildTarget target) { var fc = new FeatureCheckerHandlerFactory (); - var ctx = new ExecutionContext (fc, null, IdeApp.Workspace.ActiveExecutionTarget); + var ctx = new Projects.ExecutionContext (fc, null, IdeApp.Workspace.ActiveExecutionTarget); target.CanExecute (ctx, IdeApp.Workspace.ActiveConfiguration); @@ -331,7 +342,7 @@ namespace MonoDevelop.Debugger public static void ShowExceptionCaughtDialog () { - var ops = session.EvaluationOptions.Clone (); + var ops = GetUserOptions ().EvaluationOptions; ops.MemberEvaluationTimeout = 0; ops.EvaluationTimeout = 0; ops.EllipsizeStrings = false; @@ -362,19 +373,21 @@ namespace MonoDevelop.Debugger return exceptionDialog; } } - - static void SetupSession () + + static void SetupSession (SessionManager sessionManager) { + sessions.Add (sessionManager.Session, sessionManager); isBusy = false; + var session = sessionManager.Session; session.Breakpoints = breakpoints; session.TargetEvent += OnTargetEvent; session.TargetStarted += OnStarted; - session.OutputWriter = OutputWriter; - session.LogWriter = LogWriter; - session.DebugWriter = DebugWriter; + session.OutputWriter = sessionManager.OutputWriter; + session.LogWriter = sessionManager.LogWriter; + session.DebugWriter = sessionManager.DebugWriter; session.BusyStateChanged += OnBusyStateChanged; session.TypeResolverHandler = ResolveType; - session.BreakpointTraceHandler = BreakpointTraceHandler; + session.BreakpointTraceHandler = sessionManager.BreakpointTraceHandler; session.GetExpressionEvaluator = OnGetExpressionEvaluator; session.ConnectionDialogCreatorExtended = delegate (DebuggerStartInfo dsi) { if (dsi.RequiresManualStart) @@ -382,61 +395,54 @@ namespace MonoDevelop.Debugger return new StatusBarConnectionDialog (); }; - currentDebugOperation = new DebugAsyncOperation (); - cancelRegistration = console.CancellationToken.Register (Stop); - Runtime.RunInMainThread (delegate { if (DebugSessionStarted != null) DebugSessionStarted (null, EventArgs.Empty); NotifyLocationChanged (); }); - } static readonly object cleanup_lock = new object (); - static void Cleanup () + static void Cleanup (SessionManager sessionManager) { - DebuggerSession currentSession; StatusBarIcon currentIcon; - OperationConsole currentConsole; + var cleaningCurrentSession = sessionManager == currentSession; lock (cleanup_lock) { if (!IsDebugging) return; currentIcon = busyStatusIcon; - currentSession = session; - currentConsole = console; nextStatementLocations.Clear (); - currentBacktrace = null; + if (cleaningCurrentSession) { + currentSession = null; + currentBacktrace = null; + } busyStatusIcon = null; - session = null; - console = null; + sessions.Remove (sessionManager.Session); pinnedWatches.InvalidateAll (); } - UnsetDebugLayout (); + if (sessions.Count == 0) + UnsetDebugLayout (); + var session = sessionManager.Session; + session.BusyStateChanged -= OnBusyStateChanged; + session.TargetEvent -= OnTargetEvent; + session.TargetStarted -= OnStarted; - currentSession.BusyStateChanged -= OnBusyStateChanged; - currentSession.TargetEvent -= OnTargetEvent; - currentSession.TargetStarted -= OnStarted; + session.BreakpointTraceHandler = null; + session.GetExpressionEvaluator = null; + session.TypeResolverHandler = null; + session.OutputWriter = null; + session.LogWriter = null; - currentSession.BreakpointTraceHandler = null; - currentSession.GetExpressionEvaluator = null; - currentSession.TypeResolverHandler = null; - currentSession.OutputWriter = null; - currentSession.LogWriter = null; - currentDebugOperation.Cleanup (); + sessionManager.Dispose (); - if (currentConsole != null) { - cancelRegistration.Dispose (); - currentConsole.Dispose (); - } - Runtime.RunInMainThread (delegate { - HideExceptionCaughtDialog (); + if (cleaningCurrentSession) + HideExceptionCaughtDialog (); if (currentIcon != null) { currentIcon.Dispose (); @@ -444,14 +450,13 @@ namespace MonoDevelop.Debugger } if (StoppedEvent != null) - StoppedEvent (null, new EventArgs ()); + StoppedEvent (session, new EventArgs ()); NotifyCallStackChanged (); NotifyCurrentFrameChanged (); NotifyLocationChanged (); }); - currentSession.Dispose (); } static string oldLayout; @@ -479,31 +484,48 @@ namespace MonoDevelop.Debugger public static bool IsDebugging { get { - return session != null; + return sessions.Count > 0; } } public static bool IsConnected { get { - return IsDebugging && session.IsConnected; + return IsDebugging && sessions.Keys.Any (s => s.IsConnected); } } public static bool IsRunning { get { - return IsDebugging && session.IsRunning; + return IsDebugging && sessions.Keys.Any (s => s.IsRunning); } } public static bool IsPaused { get { - return IsDebugging && !IsRunning && currentBacktrace != null; + return IsDebugging && currentSession != null && currentBacktrace != null; } } public static void Pause () { - session.Stop (); + foreach (var session in sessions.Keys.ToArray ()) { + if (session.IsRunning) + session.Stop (); + } + } + + static ConcurrentQueue<Func<bool>> StopsQueue = new ConcurrentQueue<Func<bool>> (); + + static bool HandleStopQueue () + { + Func<bool> delayedStop; + while (StopsQueue.TryDequeue (out delayedStop)) { + //Returns false if session which scheduled stop is terminated + //So we just ignore it's stop entry and keep processing others or resume + if (delayedStop ()) + return true; + } + return false; } public static void Resume () @@ -511,8 +533,13 @@ namespace MonoDevelop.Debugger Runtime.AssertMainThread (); if (CheckIsBusy ()) return; + if (HandleStopQueue ()) + return; - session.Continue (); + foreach (var session in sessions.Keys.ToArray ()) { + if (!session.IsRunning) + session.Continue (); + } NotifyLocationChanged (); } @@ -525,20 +552,20 @@ namespace MonoDevelop.Debugger var bp = new RunToCursorBreakpoint (fileName, line, column); Breakpoints.Add (bp); - session.Continue (); + Resume (); NotifyLocationChanged (); } public static void SetNextStatement (string fileName, int line, int column) { Runtime.AssertMainThread (); - if (!IsDebugging || IsRunning || CheckIsBusy ()) + if (!IsDebugging || !IsPaused || CheckIsBusy ()) return; - session.SetNextStatement (fileName, line, column); + currentSession.Session.SetNextStatement (fileName, line, column); - var location = new SourceLocation (CurrentFrame.SourceLocation.MethodName, fileName, line); - nextStatementLocations[session.ActiveThread.Id] = location; + var location = new SourceLocation (CurrentFrame.SourceLocation.MethodName, fileName, line, column, -1, -1, null); + nextStatementLocations [ActiveThread.Id] = location; NotifyLocationChanged (); } @@ -548,10 +575,10 @@ namespace MonoDevelop.Debugger return Run (cmd, console); } - public static ProcessAsyncOperation Run (string file, string args, string workingDir, IDictionary<string,string> envVars, OperationConsole console) + public static ProcessAsyncOperation Run (string file, string args, string workingDir, IDictionary<string, string> envVars, OperationConsole console) { var cmd = Runtime.ProcessService.CreateCommand (file); - if (args != null) + if (args != null) cmd.Arguments = args; if (workingDir != null) cmd.WorkingDirectory = workingDir; @@ -562,26 +589,25 @@ namespace MonoDevelop.Debugger public static ProcessAsyncOperation Run (ExecutionCommand cmd, OperationConsole console, DebuggerEngine engine = null) { - InternalRun (cmd, engine, console); - return currentDebugOperation; + return InternalRun (cmd, engine, console); } - + public static AsyncOperation AttachToProcess (DebuggerEngine debugger, ProcessInfo proc) { - currentEngine = debugger; - session = debugger.CreateSession (); - session.ExceptionHandler = ExceptionHandler; + var session = debugger.CreateSession (); var monitor = IdeApp.Workbench.ProgressMonitors.GetRunProgressMonitor (); - console = monitor.Console; - SetupSession (); + var sessionManager = new SessionManager (session, monitor.Console, debugger); + sessions.Add (session, sessionManager); + session.ExceptionHandler = ExceptionHandler; + SetupSession (sessionManager); session.TargetExited += delegate { monitor.Dispose (); }; SetDebugLayout (); session.AttachToProcess (proc, GetUserOptions ()); - return currentDebugOperation; + return sessionManager.debugOperation; } - + public static DebuggerSessionOptions GetUserOptions () { EvaluationOptions eval = EvaluationOptions.DefaultOptions; @@ -598,70 +624,68 @@ namespace MonoDevelop.Debugger EvaluationOptions = eval, }; } - + public static void SetUserOptions (DebuggerSessionOptions options) { PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.StepOverPropertiesAndOperators", options.StepOverPropertiesAndOperators); PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.ProjectAssembliesOnly", options.ProjectAssembliesOnly); - + PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.AllowTargetInvoke", options.EvaluationOptions.AllowTargetInvoke); PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.AllowToStringCalls", options.EvaluationOptions.AllowToStringCalls); PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.EvaluationTimeout", options.EvaluationOptions.EvaluationTimeout); PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.FlattenHierarchy", options.EvaluationOptions.FlattenHierarchy); PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.GroupPrivateMembers", options.EvaluationOptions.GroupPrivateMembers); PropertyService.Set ("MonoDevelop.Debugger.DebuggingService.GroupStaticMembers", options.EvaluationOptions.GroupStaticMembers); - - if (session != null) { + + foreach (var session in sessions.Keys.ToArray ()) { session.Options.EvaluationOptions = GetUserOptions ().EvaluationOptions; - if (EvaluationOptionsChanged != null) - EvaluationOptionsChanged (null, EventArgs.Empty); } + if (EvaluationOptionsChanged != null) + EvaluationOptionsChanged (null, EventArgs.Empty); } - + public static void ShowDisassembly () { if (DisassemblyRequested != null) DisassemblyRequested (null, EventArgs.Empty); } - - internal static void InternalRun (ExecutionCommand cmd, DebuggerEngine factory, OperationConsole c) + + internal static ProcessAsyncOperation InternalRun (ExecutionCommand cmd, DebuggerEngine factory, OperationConsole c) { if (factory == null) { factory = GetFactoryForCommand (cmd); if (factory == null) throw new InvalidOperationException ("Unsupported command: " + cmd); } - - if (session != null) - throw new InvalidOperationException ("A debugger session is already started"); DebuggerStartInfo startInfo = factory.CreateDebuggerStartInfo (cmd); startInfo.UseExternalConsole = c is ExternalConsole; if (startInfo.UseExternalConsole) startInfo.CloseExternalConsoleOnExit = ((ExternalConsole)c).CloseOnDispose; - currentEngine = factory; - session = factory.CreateSession (); + + var session = factory.CreateSession (); session.ExceptionHandler = ExceptionHandler; - + + SessionManager sessionManager; // When using an external console, create a new internal console which will be used // to show the debugger log if (startInfo.UseExternalConsole) - console = IdeApp.Workbench.ProgressMonitors.GetRunProgressMonitor ().Console; + sessionManager = new SessionManager (session, IdeApp.Workbench.ProgressMonitors.GetRunProgressMonitor ().Console, factory); else - console = c; - - SetupSession (); - + sessionManager = new SessionManager (session, c, factory); + SetupSession (sessionManager); + SetDebugLayout (); - + try { session.Run (startInfo, GetUserOptions ()); } catch { - Cleanup (); + Cleanup (sessionManager); throw; } + return sessionManager.debugOperation; } - + static bool ExceptionHandler (Exception ex) { Gtk.Application.Invoke (delegate { @@ -672,34 +696,65 @@ namespace MonoDevelop.Debugger }); return true; } - - static void LogWriter (bool iserr, string text) + + class SessionManager : IDisposable { - // Events may come with a bit of delay, so the debug session - // may already have been cleaned up - var logger = console; + OperationConsole console; + IDisposable cancelRegistration; + public readonly DebuggerSession Session; + public readonly DebugAsyncOperation debugOperation; + public readonly DebuggerEngine Engine; - if (logger != null) - logger.Log.Write (text); - } + public SessionManager (DebuggerSession session, OperationConsole console, DebuggerEngine engine) + { + Engine = engine; + Session = session; + this.console = console; + cancelRegistration = console.CancellationToken.Register (Cancel); + debugOperation = new DebugAsyncOperation (session); + } - static void DebugWriter (int level, string category, string message) - { - var logger = console; + void Cancel () + { + Session.Exit (); + Cleanup (this); + } - if (logger != null) - logger.Debug (level, category, message); - } + public void LogWriter (bool iserr, string text) + { + console?.Log.Write (text); + } - static void OutputWriter (bool iserr, string text) - { - var logger = console; + public void DebugWriter (int level, string category, string message) + { + console?.Debug (level, category, message); + } - if (logger != null) { + public void OutputWriter (bool iserr, string text) + { if (iserr) - logger.Error.Write (text); + console?.Error.Write (text); else - logger.Out.Write (text); + console?.Out.Write (text); + } + + public void BreakpointTraceHandler (BreakEvent be, string trace) + { + if (be is Breakpoint) { + if (pinnedWatches.UpdateLiveWatch ((Breakpoint)be, trace)) + return; // No need to log the value. It is shown in the watch. + } + DebugWriter (0, "", trace + Environment.NewLine); + } + + public void Dispose () + { + console?.Dispose (); + console = null; + Session.Dispose (); + debugOperation.Cleanup (); + cancelRegistration?.Dispose (); + cancelRegistration = null; } } @@ -729,18 +784,21 @@ namespace MonoDevelop.Debugger { MessageService.PlaceDialog (busyEvaluator.Dialog, MessageService.RootWindow); } - + static bool CheckIsBusy () { if (isBusy && !busyEvaluator.Dialog.Visible) MessageService.PlaceDialog (busyEvaluator.Dialog, MessageService.RootWindow); return isBusy; } - + static void OnStarted (object s, EventArgs a) { nextStatementLocations.Clear (); - currentBacktrace = null; + if (currentSession?.Session == s) { + currentBacktrace = null; + currentSession = null; + } Runtime.RunInMainThread (delegate { HideExceptionCaughtDialog (); @@ -751,9 +809,10 @@ namespace MonoDevelop.Debugger NotifyLocationChanged (); }); } - + static void OnTargetEvent (object sender, TargetEventArgs args) { + var session = (DebuggerSession)sender; if (args.BreakEvent != null && args.BreakEvent.NonUserBreakpoint) return; nextStatementLocations.Clear (); @@ -762,7 +821,9 @@ namespace MonoDevelop.Debugger switch (args.Type) { case TargetEventType.TargetExited: Breakpoints.RemoveRunToCursorBreakpoints (); - Cleanup (); + SessionManager sessionToCleanup; + if (sessions.TryGetValue (session, out sessionToCleanup))//It was already cleanedUp by Stop command + Cleanup (sessionToCleanup); break; case TargetEventType.TargetSignaled: case TargetEventType.TargetStopped: @@ -770,17 +831,30 @@ namespace MonoDevelop.Debugger case TargetEventType.TargetInterrupted: case TargetEventType.UnhandledException: case TargetEventType.ExceptionThrown: - Breakpoints.RemoveRunToCursorBreakpoints (); - SetCurrentBacktrace (args.Backtrace); - NotifyPaused (); - NotifyException (args); + var action = new Func<bool> (delegate { + SessionManager sessionManager; + if (!sessions.TryGetValue (session, out sessionManager)) + return false; + Breakpoints.RemoveRunToCursorBreakpoints (); + currentSession = sessionManager; + ActiveThread = args.Thread; + NotifyPaused (); + NotifyException (args); + return true; + }); + if (currentSession != null && currentSession != sessions [session]) { + StopsQueue.Enqueue (action); + NotifyPaused ();//Notify about pause again, so ThreadsPad can update, to show all processes + } else { + action (); + } break; } } catch (Exception ex) { LoggingService.LogError ("Error handling debugger target event", ex); } } - + static void OnDisableConditionalCompilation (DocumentEventArgs e) { EventHandler<DocumentEventArgs> handler = DisableConditionalCompilation; @@ -791,13 +865,14 @@ namespace MonoDevelop.Debugger static void NotifyPaused () { Runtime.RunInMainThread (delegate { + stepSwitchCts?.Cancel (); if (PausedEvent != null) PausedEvent (null, EventArgs.Empty); NotifyLocationChanged (); IdeApp.Workbench.GrabDesktopFocus (); }); } - + static void NotifyException (TargetEventArgs args) { if (args.Type == TargetEventType.UnhandledException || args.Type == TargetEventType.ExceptionThrown) { @@ -808,14 +883,14 @@ namespace MonoDevelop.Debugger }); } } - + static void NotifyLocationChanged () { Runtime.AssertMainThread (); if (ExecutionLocationChanged != null) ExecutionLocationChanged (null, EventArgs.Empty); } - + static void NotifyCurrentFrameChanged () { if (currentBacktrace != null) @@ -823,53 +898,76 @@ namespace MonoDevelop.Debugger if (CurrentFrameChanged != null) CurrentFrameChanged (null, EventArgs.Empty); } - + static void NotifyCallStackChanged () { if (CallStackChanged != null) CallStackChanged (null, EventArgs.Empty); } - + public static void Stop () { if (!IsDebugging) return; - session.Exit (); - Cleanup (); + foreach (var pair in sessions.ToArray ()) { + pair.Key.Exit (); + Cleanup (pair.Value); + } } public static void StepInto () { Runtime.AssertMainThread (); - if (!IsDebugging || IsRunning || CheckIsBusy ()) + if (!IsDebugging || !IsPaused || CheckIsBusy ()) return; - - session.StepLine (); + currentSession.Session.StepLine (); NotifyLocationChanged (); + DelayHandleStopQueue (); } public static void StepOver () { Runtime.AssertMainThread (); - if (!IsDebugging || IsRunning || CheckIsBusy ()) + if (!IsDebugging || !IsPaused || CheckIsBusy ()) return; - - session.NextLine (); + currentSession.Session.NextLine (); NotifyLocationChanged (); + DelayHandleStopQueue (); } public static void StepOut () { Runtime.AssertMainThread (); - if (!IsDebugging || IsRunning || CheckIsBusy ()) + if (!IsDebugging || !IsPaused || CheckIsBusy ()) return; - - session.Finish (); + currentSession.Session.Finish (); NotifyLocationChanged (); + DelayHandleStopQueue (); + } + + static CancellationTokenSource stepSwitchCts; + static void DelayHandleStopQueue () + { + stepSwitchCts?.Cancel (); + if (StopsQueue.Count > 0) { + stepSwitchCts = new CancellationTokenSource (); + var token = stepSwitchCts.Token; + Task.Delay (500, token).ContinueWith ((t) => { + if (token.IsCancellationRequested) + return; + Runtime.RunInMainThread (() => { + if (token.IsCancellationRequested) + return; + if (IsPaused)//If session is already paused(stepping finished in time), don't switch + return; + HandleStopQueue (); + }); + }); + } } public static Backtrace CurrentCallStack { @@ -881,7 +979,7 @@ namespace MonoDevelop.Debugger SourceLocation location = null; if (IsPaused) - nextStatementLocations.TryGetValue (session.ActiveThread.Id, out location); + nextStatementLocations.TryGetValue (ActiveThread.Id, out location); return location; } @@ -895,7 +993,7 @@ namespace MonoDevelop.Debugger return null; } } - + /// <summary> /// The deepest stack frame with source above the CurrentFrame /// </summary> @@ -910,7 +1008,7 @@ namespace MonoDevelop.Debugger } return null; } - + public static int CurrentFrameIndex { get { return currentFrame; @@ -921,22 +1019,33 @@ namespace MonoDevelop.Debugger Runtime.RunInMainThread (delegate { NotifyCurrentFrameChanged (); }); - } - else + } else currentFrame = -1; } } - + public static ThreadInfo ActiveThread { get { - return session.ActiveThread; + return currentSession?.Session.ActiveThread; } set { - session.ActiveThread = value; - SetCurrentBacktrace (session.ActiveThread.Backtrace); + if (currentSession != null && currentSession.Session.GetProcesses () [0].GetThreads ().Contains (value)) { + currentSession.Session.ActiveThread = value; + SetCurrentBacktrace (value.Backtrace); + } else { + foreach (var session in sessions) { + if (session.Key.GetProcesses () [0].GetThreads ().Contains (value)) { + currentSession = session.Value; + currentSession.Session.ActiveThread = value; + SetCurrentBacktrace (value.Backtrace); + return; + } + } + throw new Exception ("Thread not found in any of active sessions."); + } } } - + static void SetCurrentBacktrace (Backtrace bt) { currentBacktrace = bt; @@ -951,7 +1060,7 @@ namespace MonoDevelop.Debugger NotifyLocationChanged (); }); } - + public static async void ShowCurrentExecutionLine () { Runtime.AssertMainThread (); @@ -976,13 +1085,13 @@ namespace MonoDevelop.Debugger ShowCurrentExecutionLine (); } } - + public static bool CanDebugCommand (ExecutionCommand command) { return GetFactoryForCommand (command) != null; } - - public static DebuggerEngine[] GetDebuggerEngines () + + public static DebuggerEngine [] GetDebuggerEngines () { if (engines == null) { var list = new List<DebuggerEngine> (); @@ -996,18 +1105,18 @@ namespace MonoDevelop.Debugger return engines; } - public static Dictionary<string, ExpressionEvaluatorExtensionNode> GetExpressionEvaluators() + public static Dictionary<string, ExpressionEvaluatorExtensionNode> GetExpressionEvaluators () { if (evaluators == null) { var evgs = new Dictionary<string, ExpressionEvaluatorExtensionNode> (StringComparer.InvariantCultureIgnoreCase); foreach (ExpressionEvaluatorExtensionNode node in AddinManager.GetExtensionNodes (EvaluatorsPath)) evgs.Add (node.extension, node); - + evaluators = evgs; } return evaluators; } - + static DebuggerEngine GetFactoryForCommand (ExecutionCommand cmd) { DebuggerEngine supportedEngine = null; @@ -1025,7 +1134,7 @@ namespace MonoDevelop.Debugger } return supportedEngine; } - + static void OnLineCountChanged (object ob, LineCountEventArgs a) { lock (breakpoints) { @@ -1043,7 +1152,7 @@ namespace MonoDevelop.Debugger } } } - + static void OnStoreUserPrefs (object s, UserPreferencesEventArgs args) { var baseDir = (args.Item as Solution)?.BaseDirectory; @@ -1051,7 +1160,7 @@ namespace MonoDevelop.Debugger args.Properties.SetValue ("MonoDevelop.Ide.DebuggingService.Breakpoints", breakpoints.Save (baseDir)); args.Properties.SetValue ("MonoDevelop.Ide.DebuggingService.PinnedWatches", pinnedWatches); } - + static Task OnLoadUserPrefs (object s, UserPreferencesEventArgs args) { var elem = args.Properties.GetValue<XmlElement> ("MonoDevelop.Ide.DebuggingService.Breakpoints") ?? args.Properties.GetValue<XmlElement> ("MonoDevelop.Ide.DebuggingService"); @@ -1068,21 +1177,21 @@ namespace MonoDevelop.Debugger lock (breakpoints) pinnedWatches.BindAll (breakpoints); - + return Task.FromResult (true); } - + static void OnSolutionClosed (object s, EventArgs args) { lock (breakpoints) breakpoints.Clear (); } - + static string ResolveType (string identifier, SourceLocation location) { Document doc = IdeApp.Workbench.GetDocument (location.FileName); if (doc != null) { - ITextEditorResolver textEditorResolver = doc.GetContent <ITextEditorResolver> (); + ITextEditorResolver textEditorResolver = doc.GetContent<ITextEditorResolver> (); if (textEditorResolver != null) { var rr = textEditorResolver.GetLanguageItem (doc.Editor.LocationToOffset (location.Line, 1), identifier); var ns = rr as Microsoft.CodeAnalysis.INamespaceSymbol; @@ -1100,7 +1209,7 @@ namespace MonoDevelop.Debugger } return null; } - + public static ExpressionEvaluatorExtensionNode EvaluatorForExtension (string extension) { ExpressionEvaluatorExtensionNode result; @@ -1118,11 +1227,11 @@ namespace MonoDevelop.Debugger return info != null ? info.Evaluator : null; } } - - class FeatureCheckerHandlerFactory: IExecutionHandler + + class FeatureCheckerHandlerFactory : IExecutionHandler { public DebuggerFeatures SupportedFeatures { get; set; } - + public bool CanExecute (ExecutionCommand command) { SupportedFeatures = DebuggingService.GetSupportedFeaturesForCommand (command); @@ -1135,16 +1244,16 @@ namespace MonoDevelop.Debugger throw new NotImplementedException (); } } - - class InternalDebugExecutionHandler: IExecutionHandler + + class InternalDebugExecutionHandler : IExecutionHandler { readonly DebuggerEngine engine; - + public InternalDebugExecutionHandler (DebuggerEngine engine) { this.engine = engine; } - + public bool CanExecute (ExecutionCommand command) { return engine.CanDebugCommand (command); @@ -1174,13 +1283,13 @@ namespace MonoDevelop.Debugger }); } } - + class GtkConnectionDialog : IConnectionDialog { static readonly string DefaultListenMessage = GettextCatalog.GetString ("Waiting for debugger to connect..."); System.Threading.CancellationTokenSource cts; bool disposed; - + public event EventHandler UserCancelled; public void SetMessage (DebuggerStartInfo dsi, string message, bool listening, int attemptNumber) @@ -1188,22 +1297,22 @@ namespace MonoDevelop.Debugger //FIXME: we don't support changing the message if (disposed || cts != null) return; - + cts = new System.Threading.CancellationTokenSource (); - + //MessageService is threadsafe but we want this to be async Gtk.Application.Invoke (delegate { RunDialog (message); }); } - + void RunDialog (string message) { if (disposed) return; - + string title; - + if (message == null) { title = GettextCatalog.GetString ("Waiting for debugger"); } else { @@ -1223,7 +1332,7 @@ namespace MonoDevelop.Debugger gm.DefaultButton = 0; MessageService.GenericAlert (gm); cts = null; - + if (!disposed && UserCancelled != null) { UserCancelled (null, null); } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs index 3a03dee6d1..64d0f31467 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DisassemblyView.cs @@ -60,6 +60,7 @@ namespace MonoDevelop.Debugger bool dragging; FilePath currentFile; ITextLineMarker asmMarker; + DebuggerSession session; List<AssemblyLine> cachedLines = new List<AssemblyLine> (); string cachedLinesAddrSpace; @@ -206,9 +207,9 @@ namespace MonoDevelop.Debugger cachedLines.Clear (); StackFrame sf = DebuggingService.CurrentFrame; - + session = sf.DebuggerSession; if (currentFile != sf.SourceLocation.FileName) { - AssemblyLine[] asmLines = DebuggingService.DebuggerSession.DisassembleFile (sf.SourceLocation.FileName); + AssemblyLine[] asmLines = sf.DebuggerSession.DisassembleFile (sf.SourceLocation.FileName); if (asmLines == null) { // Mixed disassemble not supported Fill (); @@ -217,24 +218,25 @@ namespace MonoDevelop.Debugger currentFile = sf.SourceLocation.FileName; addressLines.Clear (); editor.Text = string.Empty; - StreamReader sr = new StreamReader (sf.SourceLocation.FileName); - string line; - int sourceLine = 1; - int na = 0; - int editorLine = 1; - StringBuilder sb = new StringBuilder (); - List<int> asmLineNums = new List<int> (); - while ((line = sr.ReadLine ()) != null) { - InsertSourceLine (sb, editorLine++, line); - while (na < asmLines.Length && asmLines [na].SourceLine == sourceLine) { - asmLineNums.Add (editorLine); - InsertAssemblerLine (sb, editorLine++, asmLines [na++]); + using (var sr = new StreamReader (sf.SourceLocation.FileName)) { + string line; + int sourceLine = 1; + int na = 0; + int editorLine = 1; + var sb = new StringBuilder (); + var asmLineNums = new List<int> (); + while ((line = sr.ReadLine ()) != null) { + InsertSourceLine (sb, editorLine++, line); + while (na < asmLines.Length && asmLines [na].SourceLine == sourceLine) { + asmLineNums.Add (editorLine); + InsertAssemblerLine (sb, editorLine++, asmLines [na++]); + } + sourceLine++; } - sourceLine++; + editor.Text = sb.ToString (); + foreach (int li in asmLineNums) + editor.AddMarker (li, asmMarker); } - editor.Text = sb.ToString (); - foreach (int li in asmLineNums) - editor.AddMarker (li, asmMarker); } int aline; if (!addressLines.TryGetValue (GetAddrId (sf.Address, sf.AddressSpace), out aline)) @@ -397,9 +399,11 @@ namespace MonoDevelop.Debugger this.cachedLines.AddRange (lines); return lineCount; } - + void OnStop (object s, EventArgs args) { + if (session != s) + return; addressLines.Clear (); currentFile = null; if (messageOverlayContent != null) { @@ -410,6 +414,7 @@ namespace MonoDevelop.Debugger autoRefill = false; editor.Text = string.Empty; cachedLines.Clear (); + session = null; } public override bool IsReadOnly { @@ -421,6 +426,7 @@ namespace MonoDevelop.Debugger { base.Dispose (); DebuggingService.StoppedEvent -= OnStop; + session = null; } [CommandHandler (DebugCommands.StepOver)] diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs index c1a7fac6ae..ac19a8f579 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Extensions.cs @@ -46,13 +46,9 @@ namespace MonoDevelop.Debugger public static AsyncOperation Debug (this ProjectOperations opers, IBuildTarget entry, bool buildBeforeExecuting = true) { - if (opers.CurrentRunOperation != null && !opers.CurrentRunOperation.IsCompleted) - return opers.CurrentRunOperation; - ExecutionContext context = new ExecutionContext (DebuggingService.GetExecutionHandler (), IdeApp.Workbench.ProgressMonitors.ConsoleFactory, IdeApp.Workspace.ActiveExecutionTarget); - AsyncOperation op = opers.Execute (entry, context, buildBeforeExecuting); - return op; + return opers.Execute (entry, context, buildBeforeExecuting); } public static bool CanDebugFile (this ProjectOperations opers, string file) @@ -75,7 +71,7 @@ namespace MonoDevelop.Debugger var monitor = IdeApp.Workbench.ProgressMonitors.GetRunProgressMonitor (); var oper = DebuggingService.Run (executableFile, args, workingDir, envVars, monitor.Console); - opers.CurrentRunOperation = oper; + opers.AddRunOperation (oper); oper.Task.ContinueWith (t => { monitor.Dispose (); @@ -91,7 +87,7 @@ namespace MonoDevelop.Debugger var oper = DebuggingService.AttachToProcess (debugger, proc); - opers.CurrentRunOperation = oper; + opers.AddRunOperation (oper); return opers.CurrentRunOperation; } } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ImmediatePad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ImmediatePad.cs index 1b5489e8f8..603d108857 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ImmediatePad.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ImmediatePad.cs @@ -41,7 +41,7 @@ namespace MonoDevelop.Debugger { static readonly object mutex = new object(); DebuggerConsoleView view; - readonly List<uint> timersList = new List<uint>(); + readonly Dictionary<DebuggerSession, List<uint>> timersList = new Dictionary<DebuggerSession, List<uint>> (); protected override void Initialize (IPadWindow container) { @@ -77,7 +77,7 @@ namespace MonoDevelop.Debugger var val = frame.GetExpressionValue (expression, ops); if (val.IsEvaluating) { - WaitForCompleted (val); + WaitForCompleted (val, frame.DebuggerSession); return; } @@ -195,14 +195,15 @@ namespace MonoDevelop.Debugger view.Buffer.Insert (ref start, text + "\n"); } - void WaitForCompleted (ObjectValue val) + void WaitForCompleted (ObjectValue val, DebuggerSession session) { var mark = view.Buffer.CreateMark (null, view.InputLineEnd, true); var iteration = 0; uint timerId = 0; timerId = GLib.Timeout.Add (100, () => { - if (!timersList.Contains (timerId)) { + List<uint> list; + if (!timersList.TryGetValue (session, out list) || !list.Contains (timerId)) { SetLineText (GettextCatalog.GetString ("Debugging stopped"), mark); FinishPrinting (); return false; @@ -210,7 +211,7 @@ namespace MonoDevelop.Debugger if (!val.IsEvaluating) { if (iteration >= 5) DeleteLineAtMark (mark); - timersList.Remove (timerId); + list.Remove (timerId); PrintValue (val); @@ -226,7 +227,10 @@ namespace MonoDevelop.Debugger return true; }); - timersList.Add (timerId); + List<uint> tList; + if (!timersList.TryGetValue (session, out tList)) + timersList [session] = tList = new List<uint> (); + tList.Add (timerId); } void WaitChildForCompleted (ObjectValue val, IDictionary<ObjectValue, bool> evaluatingList, bool hasMore) @@ -279,18 +283,18 @@ namespace MonoDevelop.Debugger void DebuggerResumed (object sender, EventArgs e) { - view.Editable = false; + view.Editable = DebuggingService.IsPaused; } void DebuggerPaused (object sender, EventArgs e) { - view.Editable = true; + view.Editable = DebuggingService.IsPaused; } void DebuggerStopped (object sender, EventArgs e) { - timersList.Clear (); - view.Editable = false; + timersList.Remove ((DebuggerSession)sender); + view.Editable = DebuggingService.IsPaused; } } } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs index eb6217ecec..bb97027184 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ObjectValuePad.cs @@ -130,6 +130,8 @@ namespace MonoDevelop.Debugger protected virtual void OnDebuggerStopped (object s, EventArgs a) { + if (DebuggingService.IsDebugging) + return; tree.ResetChangeTracking (); tree.ClearAll (); lastFrame = null; diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/StackTracePad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/StackTracePad.cs index afb0bc8f64..7b981c588a 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/StackTracePad.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/StackTracePad.cs @@ -207,8 +207,10 @@ namespace MonoDevelop.Debugger }
void OnDebuggingServiceStopped (object sender, EventArgs e) - {
- if (store != null) + { + TreeIter iter; +
+ if (store != null && store.GetIterFirst (out iter) && (store.GetValue (iter, FrameColumn) as StackFrame)?.DebuggerSession == sender) store.Clear ();
} diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs index 93e504e2a2..460f3a3190 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs @@ -39,10 +39,11 @@ using MonoDevelop.Ide.Gui.Components; using MonoDevelop.Ide; using MonoDevelop.Components.AutoTest; using System.ComponentModel; +using System.Linq; namespace MonoDevelop.Debugger { - public class ThreadsPad: PadContent + public class ThreadsPad : PadContent { ThreadsPadWidget control = new ThreadsPadWidget (); @@ -66,7 +67,7 @@ namespace MonoDevelop.Debugger TreeStore store; bool needsUpdate; IPadWindow window; - + enum Columns { Icon, @@ -74,15 +75,16 @@ namespace MonoDevelop.Debugger Name, Object, Weight, - Location + Location, + Session } - + public ThreadsPadWidget () { this.ShadowType = ShadowType.None; - store = new TreeStore (typeof(string), typeof (string), typeof(string), typeof(object), typeof(int), typeof(string)); - SemanticModelAttribute modelAttr = new SemanticModelAttribute ("store__Icon", "store__Id","store_Name", + store = new TreeStore (typeof (string), typeof (string), typeof (string), typeof (object), typeof (int), typeof (string), typeof (object)); + SemanticModelAttribute modelAttr = new SemanticModelAttribute ("store__Icon", "store__Id", "store_Name", "store_Object", "store_Weight", "store_Location"); TypeDescriptor.AddAttributes (store, modelAttr); @@ -90,18 +92,18 @@ namespace MonoDevelop.Debugger tree.RulesHint = true; tree.HeadersVisible = true; treeViewState = new TreeViewState (tree, (int)Columns.Object); - + TreeViewColumn col = new TreeViewColumn (); CellRenderer crp = new CellRendererImage (); col.PackStart (crp, false); - col.AddAttribute (crp, "stock_id", (int) Columns.Icon); + col.AddAttribute (crp, "stock_id", (int)Columns.Icon); tree.AppendColumn (col); - + TreeViewColumn FrameCol = new TreeViewColumn (); FrameCol.Title = GettextCatalog.GetString ("Id"); FrameCol.PackStart (tree.TextRenderer, true); - FrameCol.AddAttribute (tree.TextRenderer, "text", (int) Columns.Id); - FrameCol.AddAttribute (tree.TextRenderer, "weight", (int) Columns.Weight); + FrameCol.AddAttribute (tree.TextRenderer, "text", (int)Columns.Id); + FrameCol.AddAttribute (tree.TextRenderer, "weight", (int)Columns.Weight); FrameCol.Resizable = true; FrameCol.Alignment = 0.0f; tree.AppendColumn (FrameCol); @@ -110,30 +112,56 @@ namespace MonoDevelop.Debugger col.Title = GettextCatalog.GetString ("Name"); col.Resizable = true; col.PackStart (tree.TextRenderer, false); - col.AddAttribute (tree.TextRenderer, "text", (int) Columns.Name); - col.AddAttribute (tree.TextRenderer, "weight", (int) Columns.Weight); + col.AddAttribute (tree.TextRenderer, "text", (int)Columns.Name); + col.AddAttribute (tree.TextRenderer, "weight", (int)Columns.Weight); tree.AppendColumn (col); col = new TreeViewColumn (); col.Title = GettextCatalog.GetString ("Location"); col.Resizable = true; col.PackStart (tree.TextRenderer, false); - col.AddAttribute (tree.TextRenderer, "text", (int) Columns.Location); - col.AddAttribute (tree.TextRenderer, "weight", (int) Columns.Weight); + col.AddAttribute (tree.TextRenderer, "text", (int)Columns.Location); + col.AddAttribute (tree.TextRenderer, "weight", (int)Columns.Weight); tree.AppendColumn (col); - + Add (tree); ShowAll (); - + UpdateDisplay (); - + tree.RowActivated += OnRowActivated; + tree.DoPopupMenu = ShowPopup; DebuggingService.CallStackChanged += OnStackChanged; DebuggingService.PausedEvent += OnDebuggerPaused; DebuggingService.ResumedEvent += OnDebuggerResumed; DebuggingService.StoppedEvent += OnDebuggerStopped; } - + + void ShowPopup (Gdk.EventButton evt) + { + TreeIter selected; + + if (!tree.Selection.GetSelected (out selected)) + return; + var process = store.GetValue (selected, (int)Columns.Object) as ProcessInfo; + if (process == null) + return;//User right-clicked on thread and not process + var session = store.GetValue (selected, (int)Columns.Session) as DebuggerSession; + var context_menu = new ContextMenu (); + var continueExecution = new ContextMenuItem (GettextCatalog.GetString ("Resume")); + continueExecution.Sensitive = !session.IsRunning; + continueExecution.Clicked += delegate { + session.Continue (); + }; + context_menu.Items.Add (continueExecution); + var pauseExecution = new ContextMenuItem (GettextCatalog.GetString ("Pause")); + pauseExecution.Sensitive = session.IsRunning; + pauseExecution.Clicked += delegate { + session.Stop (); + }; + context_menu.Items.Add (pauseExecution); + context_menu.Show (this, evt); + } public override void Dispose () { base.Dispose (); @@ -142,12 +170,12 @@ namespace MonoDevelop.Debugger DebuggingService.ResumedEvent -= OnDebuggerResumed; DebuggingService.StoppedEvent -= OnDebuggerStopped; } - + void OnStackChanged (object s, EventArgs a) { UpdateDisplay (); } - + public void Initialize (IPadWindow window) { this.window = window; @@ -156,7 +184,7 @@ namespace MonoDevelop.Debugger Update (); }; } - + public void UpdateDisplay () { if (window != null && window.ContentVisible) @@ -171,50 +199,58 @@ namespace MonoDevelop.Debugger tree.ScrollToPoint (0, 0); treeViewState.Save (); - - store.Clear (); - if (!DebuggingService.IsPaused) - return; + store.Clear (); try { - var processes = DebuggingService.DebuggerSession.GetProcesses (); - - if (processes.Length == 1) { - AppendThreads (TreeIter.Zero, processes[0]); - } else { - foreach (var process in processes) { - TreeIter iter = store.AppendValues (null, process.Id.ToString (), process.Name, process, (int) Pango.Weight.Normal, ""); - AppendThreads (iter, process); + if (DebuggingService.GetSessions ().SelectMany (s => s.GetProcesses ()).Count () > 1) { + foreach (var session in DebuggingService.GetSessions ()) { + foreach (var process in session.GetProcesses ()) { + var iter = store.AppendValues ( + session.IsRunning ? "md-continue-debug" : "md-pause-debug", + process.Id.ToString (), + process.Name, + process, + session == DebuggingService.DebuggerSession ? (int)Pango.Weight.Bold : (int)Pango.Weight.Normal, + "", + session); + if (session.IsRunning) + continue; + AppendThreads (iter, process, session); + } } + } else { + if (!DebuggingService.IsPaused) + return; + AppendThreads (TreeIter.Zero, DebuggingService.DebuggerSession.GetProcesses () [0], DebuggingService.DebuggerSession); } } catch (Exception ex) { LoggingService.LogInternalError (ex); } - + tree.ExpandAll (); - + treeViewState.Load (); } - void AppendThreads (TreeIter iter, ProcessInfo process) + void AppendThreads (TreeIter iter, ProcessInfo process, DebuggerSession session) { var threads = process.GetThreads (); Array.Sort (threads, (ThreadInfo t1, ThreadInfo t2) => t1.Id.CompareTo (t2.Id)); - DebuggingService.DebuggerSession.FetchFrames (threads); + session.FetchFrames (threads); + var activeThread = session.ActiveThread; foreach (var thread in threads) { - ThreadInfo activeThread = DebuggingService.DebuggerSession.ActiveThread; - var name = thread.Name == null && thread.Id == 1 ? GettextCatalog.GetString("Main Thread") : thread.Name; + var name = thread.Name == null && thread.Id == 1 ? GettextCatalog.GetString ("Main Thread") : thread.Name; var weight = thread == activeThread ? Pango.Weight.Bold : Pango.Weight.Normal; var icon = thread == activeThread ? Gtk.Stock.GoForward : null; if (iter.Equals (TreeIter.Zero)) - store.AppendValues (icon, thread.Id.ToString (), name, thread, (int) weight, thread.Location); + store.AppendValues (icon, thread.Id.ToString (), name, thread, (int)weight, thread.Location, session); else - store.AppendValues (iter, icon, thread.Id.ToString (), name, thread, (int) weight, thread.Location); + store.AppendValues (iter, icon, thread.Id.ToString (), name, thread, (int)weight, thread.Location, session); } } @@ -223,8 +259,8 @@ namespace MonoDevelop.Debugger var weight = thread == activeThread ? Pango.Weight.Bold : Pango.Weight.Normal; var icon = thread == activeThread ? Gtk.Stock.GoForward : null; - store.SetValue (iter, (int) Columns.Weight, (int) weight); - store.SetValue (iter, (int) Columns.Icon, icon); + store.SetValue (iter, (int)Columns.Weight, (int)weight); + store.SetValue (iter, (int)Columns.Icon, icon); } void UpdateThreads (ThreadInfo activeThread) @@ -235,16 +271,18 @@ namespace MonoDevelop.Debugger return; do { - var thread = store.GetValue (iter, (int) Columns.Object) as ThreadInfo; + var thread = store.GetValue (iter, (int)Columns.Object) as ThreadInfo; if (thread == null) { + store.SetValue (iter, (int)Columns.Weight, (int)(((ProcessInfo)store.GetValue (iter, (int)Columns.Object)).GetThreads ().Contains (activeThread) ? Pango.Weight.Bold : Pango.Weight.Normal)); + var sessionActiveThread = ((DebuggerSession)store.GetValue (iter, (int)Columns.Session)).ActiveThread; // this is a process... descend into our children TreeIter child; - if (store.IterChildren (out child)) { + if (store.IterChildren (out child, iter)) { do { - thread = store.GetValue (iter, (int) Columns.Object) as ThreadInfo; - UpdateThread (child, thread, activeThread); + thread = store.GetValue (child, (int)Columns.Object) as ThreadInfo; + UpdateThread (child, thread, sessionActiveThread); } while (store.IterNext (ref child)); } } else { @@ -252,7 +290,7 @@ namespace MonoDevelop.Debugger } } while (store.IterNext (ref iter)); } - + void OnRowActivated (object s, RowActivatedArgs args) { TreeIter selected; @@ -260,7 +298,7 @@ namespace MonoDevelop.Debugger if (!tree.Selection.GetSelected (out selected)) return; - var thread = store.GetValue (selected, (int) Columns.Object) as ThreadInfo; + var thread = store.GetValue (selected, (int)Columns.Object) as ThreadInfo; if (thread != null) { DebuggingService.CallStackChanged -= OnStackChanged; @@ -279,12 +317,12 @@ namespace MonoDevelop.Debugger { UpdateDisplay (); } - + void OnDebuggerResumed (object s, EventArgs a) { UpdateDisplay (); } - + void OnDebuggerStopped (object s, EventArgs a) { UpdateDisplay (); diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs index a7d6ca8c57..28c0ba6a20 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueTooltipProvider.cs @@ -59,7 +59,10 @@ namespace MonoDevelop.SourceEditor void TargetProcessExited (object sender, EventArgs e) { - if (tooltip != null) { + if (tooltip == null) + return; + var debuggerSession = tooltip.tree.Frame?.DebuggerSession; + if (debuggerSession == null || debuggerSession == sender) { tooltip.Destroy (); tooltip = null; } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueWindow.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueWindow.cs index 050df0b9d0..95ee36d940 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueWindow.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/DebugValueWindow.cs @@ -67,7 +67,7 @@ namespace MonoDevelop.SourceEditor class DebugValueWindow : PopoverWindow { - ObjectValueTreeView tree; + internal ObjectValueTreeView tree; ScrolledWindow sw; static readonly string innerTreeName = "MonoDevelop.SourceEditor.DebugValueWindow.ObjectValueTreeView"; diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs index 5cd8ea24ce..4d76a1dc1c 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs @@ -1228,6 +1228,8 @@ namespace MonoDevelop.SourceEditor void HandleTargetExited (object sender, EventArgs e) { + if (DebuggingService.IsDebugging) + return; foreach (var marker in currentErrorMarkers) { marker.IsVisible = true; } @@ -1343,7 +1345,7 @@ namespace MonoDevelop.SourceEditor return; } DocumentLine line = document.GetLine (bp.Line); - var status = bp.GetStatus (DebuggingService.DebuggerSession); + var status = DebuggingService.GetBreakpointStatus(bp); bool tracepoint = (bp.HitAction & HitAction.Break) == HitAction.None; if (line == null) diff --git a/main/src/addins/MonoDevelop.UnitTesting/Services/UnitTestService.cs b/main/src/addins/MonoDevelop.UnitTesting/Services/UnitTestService.cs index 5223c31fac..9e3edc791d 100644 --- a/main/src/addins/MonoDevelop.UnitTesting/Services/UnitTestService.cs +++ b/main/src/addins/MonoDevelop.UnitTesting/Services/UnitTestService.cs @@ -154,7 +154,7 @@ namespace MonoDevelop.UnitTesting OnTestSessionStarting (new TestSessionEventArgs { Session = session, Test = test }); if (checkCurrentRunOperation) - IdeApp.ProjectOperations.CurrentRunOperation = session; + IdeApp.ProjectOperations.AddRunOperation (session); try { await session.Start (); diff --git a/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/ComboMenu.cs b/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/ComboMenu.cs index ffa685e006..bb4c3ee9da 100644 --- a/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/ComboMenu.cs +++ b/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/ComboMenu.cs @@ -276,36 +276,68 @@ namespace WindowsPlatform.MainToolbar };
}
+ IEnumerable<RuntimeMenuItem> Flatten(IEnumerable<RuntimeMenuItem> list)
+ {
+ foreach (var item in list)
+ {
+ yield return item;
+ foreach (var child in Flatten(item.Items.OfType<RuntimeMenuItem>()))
+ yield return child;
+ }
+ }
+
IRuntimeModel active;
public override IRuntimeModel Active
{
get
{
return active;
- }
+ }
set
{
var menuItem = DropMenu.Items
- .OfType<RuntimeMenuItem> ()
- .FirstOrDefault (it => it.Model == value);
-
- if (menuItem == null) {
- active = null;
- DropMenuText = "Default";
- IsEnabled = false;
+ .OfType<RuntimeMenuItem>()
+ .FirstOrDefault(it => it.Model == value);
+ active = value;
+ if (menuItem == null)
+ {
+ if (value != null)
+ {
+ //We have MultipleTargets runtime
+ foreach (var item in value.Children)
+ {
+ menuItem = Flatten(DropMenu.Items
+ .OfType<RuntimeMenuItem>())
+ .FirstOrDefault(it => it.Model == item);
+ if (menuItem != null)
+ {
+ menuItem.Margin = new Thickness(0, 0, 0, 0);
+ menuItem.FontWeight = FontWeights.Normal;
+ }
+ }
+ using (var mutableModel = active.GetMutableModel())
+ {
+ DropMenuText = mutableModel.FullDisplayString;
+ IsEnabled = true;
+ }
+ }
+ else
+ {
+ DropMenuText = "Default";
+ IsEnabled = false;
+ }
return;
}
-
- active = menuItem.Model;
- menuItem.Margin = new Thickness (0, 0, 0, 0);
+ menuItem.Margin = new Thickness(0, 0, 0, 0);
menuItem.FontWeight = FontWeights.Normal;
- using (var mutableModel = active.GetMutableModel ()) {
+ using (var mutableModel = active.GetMutableModel())
+ {
DropMenuText = mutableModel.FullDisplayString;
IsEnabled = true;
}
- }
+ }
}
IEnumerable<IRuntimeModel> model;
@@ -336,38 +368,34 @@ namespace WindowsPlatform.MainToolbar }
}
- void OnMenuItemClicked (object sender, RoutedEventArgs args)
+ void OnMenuItemClicked(object sender, RoutedEventArgs args)
{
var item = (RuntimeMenuItem)sender;
-
- SelectionChanged?.Invoke (this, new SelectionChangedEventArgs<IRuntimeModel> (item.Model, Active));
+ args.Handled = true;
+ SelectionChanged?.Invoke(this, new SelectionChangedEventArgs<IRuntimeModel>(item.Model, Active));
}
- void FillSource (ItemCollection source, IEnumerable<IRuntimeModel> model)
+ void FillSource(ItemCollection source, IEnumerable<IRuntimeModel> model)
{
- foreach (var item in source.OfType<MenuItem> ()) {
- item.Click -= OnMenuItemClicked;
- foreach (var subItem in item.Items.OfType<MenuItem> ())
- subItem.Click -= OnMenuItemClicked;
- }
-
- source.Clear ();
-
- foreach (var item in model) {
- if (item.HasParent)
- continue;
+ Flatten(source.OfType<RuntimeMenuItem>()).ToList().ForEach(mi => mi.Click -= OnMenuItemClicked);
+ source.Clear();
+ RecursiveFillChildren(source, model);
+ }
+ void RecursiveFillChildren(ItemCollection source, IEnumerable<IRuntimeModel> children)
+ {
+ foreach (var item in children)
+ {
if (item.IsSeparator)
- source.Add (new Separator { UseLayoutRounding = true, });
- else {
- var menuItem = new RuntimeMenuItem (item);
+ {
+ source.Add(new Separator { UseLayoutRounding = true, });
+ }
+ else
+ {
+ var menuItem = new RuntimeMenuItem(item);
menuItem.Click += OnMenuItemClicked;
- foreach (var child in item.Children) {
- var childMenuItem = new RuntimeMenuItem (item);
- childMenuItem.Click += OnMenuItemClicked;
- menuItem.Items.Add (childMenuItem);
- }
- source.Add (menuItem);
+ source.Add(menuItem);
+ RecursiveFillChildren(menuItem.Items, item.Children);
}
}
}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ExecutionTarget.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ExecutionTarget.cs index b743fb60aa..c5917d9ea3 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ExecutionTarget.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ExecutionTarget.cs @@ -27,6 +27,7 @@ using System; using System.Collections; using System.Collections.Generic; +using MonoDevelop.Projects; namespace MonoDevelop.Core.Execution { @@ -203,4 +204,36 @@ namespace MonoDevelop.Core.Execution #endregion } + + public class MultiProjectExecutionTarget : ExecutionTarget + { + //string id; + + public override string Id { + get { + return "multiple-projects-5ce70911-0a07-4da6-ad3d-cc7a293f6656"; + } + } + + public override string Name { get; } = GettextCatalog.GetString ("Multiple"); + + Dictionary<SolutionItem, ExecutionTarget> list = new Dictionary<SolutionItem, ExecutionTarget> (); + + public void SetExecutionTarget (SolutionItem project, ExecutionTarget target) + { + if (target == null) + list.Remove (project); + else + list [project] = target; + //id = string.Join ("/", list.Select (p => p.Value.Id).OrderBy (id => id)); + } + + public ExecutionTarget GetTarget(SolutionItem project) + { + ExecutionTarget target; + if (list.TryGetValue (project, out target)) + return target; + return null; + } + } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MultiItemSolutionRunConfiguration.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MultiItemSolutionRunConfiguration.cs index 99b88483df..bcc66705aa 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MultiItemSolutionRunConfiguration.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MultiItemSolutionRunConfiguration.cs @@ -25,17 +25,195 @@ // THE SOFTWARE. using System; using System.Collections.Generic; +using MonoDevelop.Core.Execution; +using MonoDevelop.Core.Serialization; +using System.Linq; +using System.Text; +using MonoDevelop.Core; namespace MonoDevelop.Projects { - class MultiItemSolutionRunConfiguration: SolutionRunConfiguration + public sealed class MultiItemSolutionRunConfiguration : SolutionRunConfiguration { - public MultiItemSolutionRunConfiguration (string id, string name): base (id, name) + internal MultiItemSolutionRunConfiguration () { - Items = new List<SolutionItem> (); + Items = new StartupItemCollection (); + } + + public MultiItemSolutionRunConfiguration (string id, string name) : base (id, name) + { + Items = new StartupItemCollection (); + } + + public MultiItemSolutionRunConfiguration (MultiItemSolutionRunConfiguration other, string newName = null) : base (newName ?? other.Id, newName ?? other.Name) + { + Items = new StartupItemCollection (); + Items.AddRange (other.Items.Select (it => new StartupItem (it.SolutionItem, it.RunConfiguration))); + } + + public void CopyFrom (MultiItemSolutionRunConfiguration other) + { + Items.Clear (); + Items.AddRange (other.Items.Select (it => new StartupItem (it.SolutionItem, it.RunConfiguration))); + OnRunConfigurationsChanged (); + } + + public StartupItemCollection Items { get; } + + [ItemProperty ("Items")] + [ItemProperty ("StartupItem", ValueType = typeof (StartupItem), Scope = "*")] + StartupItem [] ItemArray { + get { + return Items.ToArray (); + } + set { + Items.Clear (); + Items.AddRange (value); + } + } + + internal void ReplaceItem (SolutionItem oldItem, SolutionItem newItem) + { + foreach (var si in Items) + if (si.SolutionItem == oldItem) + si.SolutionItem = newItem; + } + + internal void RemoveItem (SolutionItem item) + { + var i = Items.FindIndex (si => si.SolutionItem == item); + if (i != -1) + Items.RemoveAt (i); + } + + internal void OnRunConfigurationsChanged () + { + ParentSolution?.NotifyRunConfigurationsChanged (); + } + + internal void ResolveObjects (Solution sol) + { + foreach (var it in Items) + it.ResolveObjects (sol); + } + + public override string Summary { + get { + if (Items.Count == 0) + return GettextCatalog.GetString ("No projects selected to run"); + var sb = new StringBuilder (); + foreach (var it in Items) { + if (sb.Length > 0) + sb.Append (", "); + sb.Append (it.SolutionItem.Name).Append (" (").Append (it.RunConfiguration.Name).Append (")"); + } + return sb.ToString (); + } } - public List<SolutionItem> Items { get; set; } - } } + public sealed class StartupItem + { + StartupItem () + { + } + + public StartupItem (SolutionItem item, SolutionItemRunConfiguration configuration) + { + SolutionItem = item; + RunConfiguration = configuration; + } + + string itemId; + string configurationId; + + [ItemProperty] + string ItemId { + get { return itemId ?? SolutionItem?.ItemId; } + set { itemId = value; } + } + + [ItemProperty] + string ConfigurationId { + get { return configurationId ?? RunConfiguration?.Id; } + set { configurationId = value; } + } + + internal void ResolveObjects (Solution sol) + { + if (ItemId != null) { + SolutionItem = sol.GetSolutionItem (ItemId) as SolutionItem; + if (SolutionItem != null && ConfigurationId != null) + RunConfiguration = SolutionItem.GetRunConfigurations ().FirstOrDefault (c => c.Id == ConfigurationId); + ItemId = null; + ConfigurationId = null; + } + } + + public SolutionItem SolutionItem { get; internal set; } + public SolutionItemRunConfiguration RunConfiguration { get; internal set; } + } + + public sealed class MultiItemSolutionRunConfigurationCollection : ItemCollection<MultiItemSolutionRunConfiguration> + { + Solution parentSolution; + + public MultiItemSolutionRunConfigurationCollection () + { + } + + internal MultiItemSolutionRunConfigurationCollection (Solution parentSolution) + { + this.parentSolution = parentSolution; + } + + protected override void OnItemsAdded (IEnumerable<MultiItemSolutionRunConfiguration> items) + { + if (parentSolution != null) { + foreach (var conf in items) { + conf.ParentSolution = parentSolution; + conf.ResolveObjects (parentSolution); + } + } + base.OnItemsAdded (items); + parentSolution.OnRunConfigurationsAdded (items); + } + + protected override void OnItemsRemoved (IEnumerable<MultiItemSolutionRunConfiguration> items) + { + if (parentSolution != null) { + foreach (var conf in items) + conf.ParentSolution = null; + } + base.OnItemsRemoved (items); + parentSolution.OnRunConfigurationRemoved (items); + } + } + + public sealed class StartupItemCollection : ItemCollection<StartupItem> + { + MultiItemSolutionRunConfiguration parent; + + public StartupItemCollection () + { + } + + internal StartupItemCollection (MultiItemSolutionRunConfiguration parent) + { + this.parent = parent; + } + + protected override void OnItemsAdded (IEnumerable<StartupItem> items) + { + base.OnItemsAdded (items); + parent?.OnRunConfigurationsChanged (); + } + + protected override void OnItemsRemoved (IEnumerable<StartupItem> items) + { + base.OnItemsRemoved (items); + parent?.OnRunConfigurationsChanged (); + } + }} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs index b04eac04cc..0f58656485 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Solution.cs @@ -56,8 +56,10 @@ namespace MonoDevelop.Projects ReadOnlyCollection<SolutionItem> solutionItems; SolutionConfigurationCollection configurations; - SolutionRunConfigurationCollection runConfigurations; + MultiItemSolutionRunConfigurationCollection runConfigurations; + MultiItemSolutionRunConfiguration multiStartupConfig = new MultiItemSolutionRunConfiguration (MultiStartupConfigId, "Multi-Startup"); + const string MultiStartupConfigId = "MonoDevelop.Projects.MultiStartup"; MSBuildEngineManager msbuildEngineManager = new MSBuildEngineManager (); @@ -80,7 +82,7 @@ namespace MonoDevelop.Projects loadingFromConstructor = loading; Counters.SolutionsLoaded++; configurations = new SolutionConfigurationCollection (this); - runConfigurations = new SolutionRunConfigurationCollection (this); + runConfigurations = new MultiItemSolutionRunConfigurationCollection (this); format = MSBuildFileFormat.DefaultFormat; Initialize (this); } @@ -188,29 +190,30 @@ namespace MonoDevelop.Projects } } + [Obsolete ("Use StartupConfiguration")] public bool SingleStartup { get { return StartupConfiguration is SingleItemSolutionRunConfiguration; } set { - if (value == SingleStartup) - return; - if (value) { - StartupItem = multiStartupConfig.Items.FirstOrDefault () ?? GetRunConfigurations ().OfType<SingleItemSolutionRunConfiguration> ().Select (i => i.Item).FirstOrDefault (); - } else { - if (!runConfigurations.Contains (multiStartupConfig)) - runConfigurations.Add (multiStartupConfig); - StartupConfiguration = multiStartupConfig; - } } } + [Obsolete ("Use StartupConfiguration or MultiStartupRunConfigurations")] public List<SolutionItem> MultiStartupItems { get { - return multiStartupConfig.Items; + var sc = StartupConfiguration as MultiItemSolutionRunConfiguration; + if (sc != null) + return sc.Items.Select (it => it.SolutionItem).ToList (); + else + return new List<SolutionItem> (); } } + public MultiItemSolutionRunConfigurationCollection MultiStartupRunConfigurations { + get { return runConfigurations; } + } + /// <summary> /// Gets the author information for this solution. If no specific information is set for this solution, it /// will return the author defined in the global settings. @@ -244,6 +247,12 @@ namespace MonoDevelop.Projects bool startupConfigSet = false; + var mconfigs = UserProperties.GetValue<MultiItemSolutionRunConfiguration []> ("MultiItemStartupConfigurations"); + if (mconfigs != null) { + MultiStartupRunConfigurations.Clear (); + MultiStartupRunConfigurations.AddRange (mconfigs); + } + var sitem = UserProperties.GetValue<string> ("StartupItem"); if (!string.IsNullOrEmpty (sitem)) { // Old StartupItem property. Find the corresponding SingleItemSolutionRunConfiguration instance and get rid of the property. @@ -259,14 +268,16 @@ namespace MonoDevelop.Projects var sitems = UserProperties.GetValue<string []> ("StartupItems"); if (sitems != null && sitems.Length > 0) { // Old StartupItems property. Create a corresponding MultiItemSolutionRunConfiguration. + UserProperties.RemoveValue ("StartupItems"); var multiStartupItems = sitems.Select (p => (string)GetAbsoluteChildPath (p)).Select (FindSolutionItem).Where (i => i != null); - multiStartupConfig.Items.Clear (); - multiStartupConfig.Items.AddRange (multiStartupItems.ToArray ()); - runConfigurations.Add (multiStartupConfig); + var msc = new MultiItemSolutionRunConfiguration ("Multi-Startup", "Multi-Startup"); + foreach (var si in multiStartupItems) + msc.Items.Add (new StartupItem (si, si.GetDefaultRunConfiguration ())); + runConfigurations.Add (msc); if (!startupConfigSet) { // If the config has not been set by StartupItem it means that this is an old solution that had been configured with multiple startup. // Select the multi-startup config in this case. - StartupConfiguration = multiStartupConfig; + StartupConfiguration = msc; startupConfigSet = true; } } @@ -289,13 +300,9 @@ namespace MonoDevelop.Projects protected override async Task OnSaveUserProperties () { UserProperties.SetValue ("StartupConfiguration", (string)StartupConfiguration?.Id); - + UserProperties.SetValue ("MultiItemStartupConfigurations", MultiStartupRunConfigurations.ToArray ()); + // Save the multi-startup configuration only if it is the one that's selected - var msc = StartupConfiguration as MultiItemSolutionRunConfiguration; - if (msc != null) - UserProperties.SetValue ("StartupItems", msc.Items.Select (p => (string)GetRelativeChildPath (p.FileName)).ToArray ()); - else - UserProperties.RemoveValue ("StartupItems"); CollectItemProperties (UserProperties, RootFolder, "MonoDevelop.Ide.ItemProperties"); await base.OnSaveUserProperties (); @@ -439,7 +446,7 @@ namespace MonoDevelop.Projects public ReadOnlyCollection<Project> GetAllProjectsWithTopologicalSort (ConfigurationSelector configuration) { return RootFolder.GetAllProjectsWithTopologicalSort (configuration); - }
+ } public override IEnumerable<Project> GetProjectsContainingFile (FilePath fileName) { @@ -818,8 +825,13 @@ namespace MonoDevelop.Projects var msc = runConfiguration as MultiItemSolutionRunConfiguration; if (msc != null) { - foreach (SolutionItem it in msc.Items) { - if (it.CanExecute (context, configuration)) + var multiProject = context.ExecutionTarget as MultiProjectExecutionTarget; + foreach (StartupItem it in msc.Items) { + var localContext = context; + //Set project specific execution target to context if exists + if (multiProject?.GetTarget (it.SolutionItem) != null) + localContext = new ExecutionContext (context.ExecutionHandler, context.ExternalConsoleFactory, multiProject?.GetTarget (it.SolutionItem)); + if (it.SolutionItem.CanExecute (localContext, configuration, it.RunConfiguration)) return true; } return false; @@ -840,16 +852,18 @@ namespace MonoDevelop.Projects var monitors = new List<AggregatedProgressMonitor> (); monitor.BeginTask ("Executing projects", 1); - var secondaryContext = new ExecutionContext (Runtime.ProcessService.DefaultExecutionMode, context.ConsoleFactory, null); - - foreach (SolutionItem it in msc.Items) { - if (!it.CanExecute (context, configuration)) + var multiProject = context.ExecutionTarget as MultiProjectExecutionTarget; + foreach (StartupItem it in msc.Items) { + var localContext = context; + //Set project specific execution target to context if exists + if (multiProject?.GetTarget (it.SolutionItem) != null) + localContext = new ExecutionContext (context.ExecutionHandler, context.ConsoleFactory, multiProject?.GetTarget (it.SolutionItem)); + if (!it.SolutionItem.CanExecute (localContext, configuration, it.RunConfiguration)) continue; AggregatedProgressMonitor mon = new AggregatedProgressMonitor (); mon.AddFollowerMonitor (monitor, MonitorAction.ReportError | MonitorAction.ReportWarning | MonitorAction.FollowerCancel); monitors.Add (mon); - tasks.Add (it.Execute (mon, context, configuration)); - context = secondaryContext; + tasks.Add (it.SolutionItem.Execute (mon, localContext, configuration, it.RunConfiguration)); } try { await Task.WhenAll (tasks); @@ -915,10 +929,10 @@ namespace MonoDevelop.Projects bool OnGetSupportsFormat (MSBuildFileFormat format) { return GetAllItems<SolutionItem> ().All (p => p.SupportsFormat (format)); - }
-
+ } + protected override IEnumerable<FilePath> OnGetItemFiles (bool includeReferencedFiles) - {
+ { List<FilePath> files = base.OnGetItemFiles (includeReferencedFiles).ToList (); if (includeReferencedFiles) { foreach (SolutionItem item in GetAllItems<SolutionItem> ()) @@ -967,13 +981,12 @@ namespace MonoDevelop.Projects // Reuse the configuration information of the replaced item foreach (SolutionConfiguration conf in Configurations) conf.ReplaceItem ((SolutionItem)replacedItem, eitem); + if (StartupItem == replacedItem) StartupItem = eitem; - else { - int i = MultiStartupItems.IndexOf ((SolutionItem)replacedItem); - if (i != -1) - MultiStartupItems [i] = eitem; - } + + foreach (var sc in MultiStartupRunConfigurations.OfType<MultiItemSolutionRunConfiguration> ()) + sc.ReplaceItem ((SolutionItem)replacedItem, eitem); } } } @@ -1009,8 +1022,9 @@ namespace MonoDevelop.Projects if (StartupItem == item) StartupItem = null; - else - MultiStartupItems.Remove (item); + + foreach (var sc in MultiStartupRunConfigurations) + sc.RemoveItem (item); } // Update the file name because the file format may have changed diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionRunConfiguration.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionRunConfiguration.cs index 1e0d693b37..7780c20f4b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionRunConfiguration.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionRunConfiguration.cs @@ -24,13 +24,22 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; +using MonoDevelop.Core.Serialization; + namespace MonoDevelop.Projects { public class SolutionRunConfiguration: RunConfiguration { + [ItemProperty ("Id")] string id; + + [ItemProperty ("Name")] string name; + internal SolutionRunConfiguration () + { + } + public SolutionRunConfiguration (string id) { this.name = this.id = id; diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/ItemOptionPanels.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/ItemOptionPanels.addin.xml index ce11430353..4a059c141a 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/ItemOptionPanels.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/ItemOptionPanels.addin.xml @@ -80,16 +80,16 @@ <Section id="Run" _label="Run"> <Section id="General" _label="Configurations"> <Condition id="ItemType" value="Project"> -<!-- <Panel id = "RunOptionsPanel" _label = "Run" fill = "true" class = "MonoDevelop.Ide.Projects.OptionPanels.RunOptionsPanel"/>--> <Panel id = "RunConfigurationsPanel" _label = "Run Configurations" fill = "true" class = "MonoDevelop.Ide.Projects.OptionPanels.RunConfigurationsPanel"/> </Condition> + <Condition id="ItemType" value="Solution"> + <!--<Section id = "StartupOptionsPanel" _label = "Startup Project" fill = "true" class = "MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanel"/>--> + <Panel id = "SolutionRunConfigurationsPanel" _label = "Run Configurations" fill = "true" class = "MonoDevelop.Ide.Projects.OptionPanels.SolutionRunConfigurationsPanel" icon="md-prefs-startup-project" /> + </Condition> + <Condition id="ItemType" value="SolutionItem"> + <!-- <Section id = "CustomCommands" _label = "Custom Commands" fill = "true" class = "MonoDevelop.Ide.Projects.OptionPanels.ExecutionCustomCommandPanel" />--> + </Condition> </Section> - <Condition id="ItemType" value="Solution"> - <Section id = "StartupOptionsPanel" _label = "Startup Project" fill = "true" class = "MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanel" icon="md-prefs-startup-project" /> - </Condition> - <Condition id="ItemType" value="SolutionItem"> -<!-- <Section id = "CustomCommands" _label = "Custom Commands" fill = "true" class = "MonoDevelop.Ide.Projects.OptionPanels.ExecutionCustomCommandPanel" />--> - </Condition> </Section> <Section id="SourceCode" _label="Source Code"> <Section id="DotNetNamingPolicies" _label=".NET Naming Policies" icon="md-prefs-dotnet-naming-policies"> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ConfigurationMerger.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ConfigurationMerger.cs index bbbc46bd93..6071ec1e50 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ConfigurationMerger.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/ConfigurationMerger.cs @@ -54,7 +54,7 @@ namespace MonoDevelop.Components.MainToolbar /// <summary> /// Load configuration information for a solution /// </summary> - public void Load (Solution sol) + public void Load (Solution sol, SolutionItem project, SolutionItemRunConfiguration runConfig) { currentSolutionConfigurations.Clear (); currentTargetPartitions.Clear (); @@ -63,9 +63,6 @@ namespace MonoDevelop.Components.MainToolbar if (sol == null) return; - var project = sol.StartupItem; - var runConfig = (sol.StartupConfiguration as SingleItemSolutionRunConfiguration)?.RunConfiguration; - // Create a set of configuration partitions. Each partition will contain configurations // which are implicitly selected when selecting an execution target. For example, in // an iOS project we would have two partitions: diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs index 0a041a4baf..a0ad42f0b4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbar.cs @@ -518,9 +518,6 @@ namespace MonoDevelop.Components.MainToolbar runtimeModel = value; runtimeStore.Clear (); foreach (var item in value) { - if (item.HasParent) - continue; - var iter = runtimeStore.AppendValues (item); foreach (var subitem in item.Children) runtimeStore.AppendValues (iter, subitem); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs index cbfd4b690b..3007789dc6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs @@ -59,11 +59,11 @@ namespace MonoDevelop.Components.MainToolbar set { searchForMembers.Value = value; } } - ConfigurationMerger configurationMerger = new ConfigurationMerger (); + Dictionary<SolutionItem, ConfigurationMerger> configurationMergers = new Dictionary<SolutionItem, ConfigurationMerger> (); int ignoreConfigurationChangedCount, ignoreRuntimeChangedCount; Solution currentSolution; bool settingGlobalConfig; - SolutionItem currentStartupProject; + Tuple<SolutionItem, SolutionItemRunConfiguration> [] startupProjects = new Tuple<SolutionItem, SolutionItemRunConfiguration> [0]; EventHandler executionTargetsChanged; public MainToolbarController (IMainToolbarView toolbarView) @@ -141,7 +141,13 @@ namespace MonoDevelop.Components.MainToolbar if (settingGlobalConfig) return; - configurationMerger.Load (currentSolution); + TrackStartupProject (); + configurationMergers = new Dictionary<SolutionItem, ConfigurationMerger> (); + foreach (var project in startupProjects) { + var configurationMerger = new ConfigurationMerger (); + configurationMerger.Load (currentSolution, project.Item1, project.Item2); + configurationMergers [project.Item1] = configurationMerger; + } ignoreConfigurationChangedCount++; try { @@ -152,11 +158,13 @@ namespace MonoDevelop.Components.MainToolbar ToolbarView.RunConfigurationVisible = false; return; } - - ToolbarView.ConfigurationModel = configurationMerger - .SolutionConfigurations - .Distinct () - .Select (conf => new ConfigurationModel (conf)); + if (configurationMergers.Count == 1) + ToolbarView.ConfigurationModel = configurationMergers.First ().Value.SolutionConfigurations + .Distinct () + .Select (conf => new ConfigurationModel (conf)); + else + ToolbarView.ConfigurationModel = currentSolution?.Configurations.OfType<SolutionConfiguration> () + .Select (conf => new ConfigurationModel (conf.Id)) ?? new ConfigurationModel [0]; if (currentSolution != null) ToolbarView.RunConfigurationModel = currentSolution.GetRunConfigurations ().Select (rc => new RunConfigurationModel (rc)).ToArray (); @@ -179,65 +187,45 @@ namespace MonoDevelop.Components.MainToolbar ignoreRuntimeChangedCount++; try { ToolbarView.RuntimeModel = Enumerable.Empty<IRuntimeModel> (); - if (!IdeApp.Workspace.IsOpen || currentSolution == null || !currentSolution.SingleStartup || currentSolution.StartupItem == null) - return; - - // Check that the current startup project is enabled for the current configuration - var solConf = currentSolution.GetConfiguration (IdeApp.Workspace.ActiveConfiguration); - if (solConf == null || !solConf.BuildEnabledForItem (currentSolution.StartupItem)) + if (!IdeApp.Workspace.IsOpen || currentSolution == null) return; - - ExecutionTarget previous = null; + var list = new List<RuntimeModel> (); int runtimes = 0; + if (currentSolution.StartupConfiguration is MultiItemSolutionRunConfiguration) { + bool anyValid = false; + foreach (var startConf in ((MultiItemSolutionRunConfiguration)currentSolution.StartupConfiguration).Items) { + if (startConf?.SolutionItem == null) + continue; - var list = new List<RuntimeModel> (); - foreach (var target in configurationMerger.GetTargetsForConfiguration (IdeApp.Workspace.ActiveConfigurationId, true)) { - if (target is ExecutionTargetGroup) { - var devices = (ExecutionTargetGroup) target; - - if (previous != null) - list.Add (new RuntimeModel (this, target: null)); - - list.Add (new RuntimeModel (this, target)); - foreach (var device in devices) { - if (device is ExecutionTargetGroup) { - var versions = (ExecutionTargetGroup) device; - - if (versions.Count > 1) { - var parent = new RuntimeModel (this, device) { - IsIndented = true, - }; - list.Add (parent); - - foreach (var version in versions) { - list.Add (new RuntimeModel (this, version, parent)); - runtimes++; - } - } else { - list.Add (new RuntimeModel (this, versions[0]) { - IsIndented = true, - }); - runtimes++; - } - } else { - list.Add (new RuntimeModel (this, device) { - IsIndented = true, - }); - runtimes++; - } - } - } else { - if (previous is ExecutionTargetGroup) { - list.Add (new RuntimeModel (this, target: null)); + // Check that the current startup project is enabled for the current configuration + var solConf = currentSolution.GetConfiguration (IdeApp.Workspace.ActiveConfiguration); + if (solConf == null || !solConf.BuildEnabledForItem (startConf.SolutionItem)) + continue; + anyValid = true; + var projectList = new List<RuntimeModel> (); + FillRuntimesForProject (projectList, startConf.SolutionItem, ref runtimes); + var parent = new RuntimeModel (this, startConf.SolutionItem.Name); + parent.HasChildren = true; + list.Add (parent); + foreach (var p in projectList) { + parent.AddChild (p); } - - list.Add (new RuntimeModel (this, target)); - runtimes++; } + if (!anyValid) + return; + } else { + var startConf = currentSolution.StartupConfiguration as SingleItemSolutionRunConfiguration; + if (startConf == null || startConf.Item == null) + return; - previous = target; + // Check that the current startup project is enabled for the current configuration + var solConf = currentSolution.GetConfiguration (IdeApp.Workspace.ActiveConfiguration); + if (solConf == null || !solConf.BuildEnabledForItem (startConf.Item)) + return; + FillRuntimesForProject (list, startConf.Item, ref runtimes); } + var cmds = IdeApp.CommandService.CreateCommandEntrySet (TargetsMenuPath); if (cmds.Count > 0) { bool needsSeparator = runtimes > 0; @@ -251,7 +239,7 @@ namespace MonoDevelop.Components.MainToolbar var ci = IdeApp.CommandService.GetCommandInfo (cmd.Id, new CommandTargetRoute (lastCommandTarget)); if (ci.Visible) { if (needsSeparator) { - list.Add (new RuntimeModel (this, target: null)); + list.Add (new RuntimeModel (this, displayText: null)); needsSeparator = false; } list.Add (new RuntimeModel (this, cmd)); @@ -268,6 +256,57 @@ namespace MonoDevelop.Components.MainToolbar } } + void FillRuntimesForProject (List<RuntimeModel> list, SolutionItem project, ref int runtimes) + { + ExecutionTarget previous = null; + + foreach (var target in configurationMergers [project].GetTargetsForConfiguration (IdeApp.Workspace.ActiveConfigurationId, configurationMergers.Count < 2)) { + if (target is ExecutionTargetGroup) { + var devices = (ExecutionTargetGroup)target; + + if (previous != null) + list.Add (new RuntimeModel (this, displayText: null));//Seperator + + foreach (var device in devices) { + if (device is ExecutionTargetGroup) { + var versions = (ExecutionTargetGroup)device; + + if (versions.Count > 1) { + var parent = new RuntimeModel (this, device, true, project) { + IsIndented = true, + }; + list.Add (parent); + + foreach (var version in versions) { + parent.AddChild (new RuntimeModel (this, version, false, project)); + runtimes++; + } + } else { + list.Add (new RuntimeModel (this, versions [0], true, project) { + IsIndented = true, + }); + runtimes++; + } + } else { + list.Add (new RuntimeModel (this, device, true, project) { + IsIndented = true, + }); + runtimes++; + } + } + } else { + if (previous is ExecutionTargetGroup) { + list.Add (new RuntimeModel (this, displayText: null));//Seperator + } + + list.Add (new RuntimeModel (this, target, true, project)); + runtimes++; + } + + previous = target; + } + } + void HandleRuntimeChanged (object sender, HandledEventArgs e) { if (ignoreRuntimeChangedCount == 0) { @@ -278,6 +317,7 @@ namespace MonoDevelop.Components.MainToolbar } NotifyConfigurationChange (); + SelectActiveRuntime (runtime); } } @@ -296,14 +336,18 @@ namespace MonoDevelop.Components.MainToolbar void UpdateBuildConfiguration () { var config = ToolbarView.ActiveConfiguration; - if (config == null) + if (config == null || configurationMergers.Count == 0) + return; + if (configurationMergers.Count > 1) { + IdeApp.Workspace.ActiveConfigurationId = config.OriginalId; return; + } ExecutionTarget newTarget; string fullConfig; var runtime = (RuntimeModel)ToolbarView.ActiveRuntime; - configurationMerger.ResolveConfiguration (config.OriginalId, runtime != null ? runtime.ExecutionTarget : null, out fullConfig, out newTarget); + configurationMergers.Values.First ().ResolveConfiguration (config.OriginalId, runtime != null ? runtime.ExecutionTarget : null, out fullConfig, out newTarget); settingGlobalConfig = true; try { IdeApp.Workspace.ActiveExecutionTarget = newTarget; @@ -321,7 +365,7 @@ namespace MonoDevelop.Components.MainToolbar UpdateBuildConfiguration (); FillRuntimes (); - SelectActiveRuntime (); + SelectActiveRuntime (ToolbarView.ActiveRuntime as RuntimeModel); } void NotifyRunConfigurationChange () @@ -365,7 +409,12 @@ namespace MonoDevelop.Components.MainToolbar void SelectActiveConfiguration () { - string name = configurationMerger.GetUnresolvedConfiguration (IdeApp.Workspace.ActiveConfigurationId); + var allNames = configurationMergers.Values.Select (cm => cm.GetUnresolvedConfiguration (IdeApp.Workspace.ActiveConfigurationId)).Distinct (); + string name; + if (allNames.Count () == 1) + name = allNames.First (); + else + name = IdeApp.Workspace.ActiveConfigurationId; ignoreConfigurationChangedCount++; try { @@ -393,79 +442,86 @@ namespace MonoDevelop.Components.MainToolbar ignoreConfigurationChangedCount--; } - SelectActiveRuntime (); + SelectActiveRuntime (ToolbarView.ActiveRuntime as RuntimeModel); } - bool SelectActiveRuntime (ref bool selected, ref ExecutionTarget defaultTarget, ref int defaultIter) + IEnumerable<IRuntimeModel> AllRuntimes (IEnumerable<IRuntimeModel> runtimes) { - var runtimes = ToolbarView.RuntimeModel.Cast<RuntimeModel> ().ToList (); - string lastRuntimeForProject = currentStartupProject?.UserProperties.GetValue<string> ("PreferredExecutionTarget", defaultValue: null); - var activeTarget = IdeApp.Workspace.ActiveExecutionTarget; - var activeTargetId = activeTarget != null ? activeTarget.Id : null; + foreach (var runtime in runtimes) { + yield return runtime; + foreach (var childRuntime in AllRuntimes (runtime.Children)) + yield return childRuntime; + } + } - for (int iter = 0; iter < runtimes.Count; ++iter) { - var item = runtimes [iter]; + RuntimeModel SelectActiveRuntime (SolutionItem project, RuntimeModel preferedRuntimeModel) + { + var runtimes = AllRuntimes (ToolbarView.RuntimeModel).Cast<RuntimeModel> ().ToList (); + string lastRuntimeForProject = project.UserProperties.GetValue<string> ("PreferredExecutionTarget", defaultValue: null);
+ var activeTarget = preferedRuntimeModel?.Project == project ? preferedRuntimeModel.ExecutionTarget : null; + var multiProjectExecutionTarget = activeTarget as MultiProjectExecutionTarget; + if (multiProjectExecutionTarget != null) { + activeTarget = multiProjectExecutionTarget.GetTarget (project); + } + var activeTargetId = activeTarget?.Id; + RuntimeModel defaultRuntime = null; + + foreach (var item in runtimes) { using (var model = item.GetMutableModel ()) { if (!model.Enabled) continue; } var target = item.ExecutionTarget; - if (target == null || !target.Enabled) + if (target == null || !target.Enabled || item.Project != project) continue; if (target is ExecutionTargetGroup) if (item.HasChildren) continue; - if (defaultTarget == null || lastRuntimeForProject == target.Id) { - defaultTarget = target; - defaultIter = iter; + if (defaultRuntime == null || lastRuntimeForProject == target.Id) { + defaultRuntime = item; } if (target.Id == activeTargetId) { - IdeApp.Workspace.ActiveExecutionTarget = target; - ToolbarView.ActiveRuntime = ToolbarView.RuntimeModel.ElementAt (iter); - UpdateBuildConfiguration (); - selected = true; - return true; + project.UserProperties.SetValue ("PreferredExecutionTarget", target.Id); + return item; } if (target.Equals (activeTarget)) { - ToolbarView.ActiveRuntime = ToolbarView.RuntimeModel.ElementAt (iter); - UpdateBuildConfiguration (); - selected = true; + defaultRuntime = item; } } - - return false; + if (defaultRuntime?.ExecutionTarget?.Id != null)
+ project.UserProperties.SetValue("PreferredExecutionTarget", defaultRuntime.ExecutionTarget.Id); + return defaultRuntime; } - void SelectActiveRuntime () + void SelectActiveRuntime (RuntimeModel preferedRuntimeModel) { ignoreRuntimeChangedCount++; try { if (ToolbarView.RuntimeModel.Any ()) { - ExecutionTarget defaultTarget = null; - bool selected = false; - int defaultIter = 0; - - if (!SelectActiveRuntime (ref selected, ref defaultTarget, ref defaultIter) && !selected) { - if (defaultTarget != null) { - IdeApp.Workspace.ActiveExecutionTarget = defaultTarget; - ToolbarView.ActiveRuntime = ToolbarView.RuntimeModel.ElementAt (defaultIter); + if (startupProjects.Length > 1) { + var multiProjectTarget = new MultiProjectExecutionTarget (); + var multipleRuntime = new RuntimeModel (this, multiProjectTarget, false, null); + foreach (var startupProject in startupProjects) { + var runtimeModel = SelectActiveRuntime (startupProject.Item1, preferedRuntimeModel); + multiProjectTarget.SetExecutionTarget (startupProject.Item1, runtimeModel.ExecutionTarget); + multipleRuntime.AddChild (runtimeModel); } + ToolbarView.ActiveRuntime = multipleRuntime; + IdeApp.Workspace.ActiveExecutionTarget = multipleRuntime.ExecutionTarget; + } else if (startupProjects.Length == 1) { + var runtimeModel = SelectActiveRuntime (startupProjects.First ().Item1, preferedRuntimeModel); + ToolbarView.ActiveRuntime = runtimeModel; + IdeApp.Workspace.ActiveExecutionTarget = runtimeModel?.ExecutionTarget; UpdateBuildConfiguration (); - } - - if (currentStartupProject == null) { - return; - } - - var runtime = (RuntimeModel)ToolbarView.ActiveRuntime; - if (runtime != null && runtime.Command == null) { - currentStartupProject.UserProperties.SetValue<string> ("PreferredExecutionTarget", runtime.TargetId); + } else { + ToolbarView.ActiveRuntime = null; + IdeApp.Workspace.ActiveExecutionTarget = null; } } } finally { @@ -511,18 +567,28 @@ namespace MonoDevelop.Components.MainToolbar void TrackStartupProject () { - if (currentStartupProject != null && ((currentSolution != null && currentStartupProject != currentSolution.StartupItem) || currentSolution == null)) { - currentStartupProject.ExecutionTargetsChanged -= executionTargetsChanged; + Tuple<SolutionItem, SolutionItemRunConfiguration> [] projects; + if (currentSolution?.StartupConfiguration is MultiItemSolutionRunConfiguration) { + var multiRunConfigs = (MultiItemSolutionRunConfiguration)currentSolution.StartupConfiguration; + projects = multiRunConfigs.Items.Select (rc => new Tuple<SolutionItem, SolutionItemRunConfiguration> ( + rc.SolutionItem, + rc.RunConfiguration + )).ToArray (); + } else if (currentSolution?.StartupConfiguration is SingleItemSolutionRunConfiguration) { + var singleRunConfig = (SingleItemSolutionRunConfiguration)currentSolution.StartupConfiguration; + projects = new Tuple<SolutionItem, SolutionItemRunConfiguration> []{ new Tuple<SolutionItem, SolutionItemRunConfiguration> ( + singleRunConfig.Item, singleRunConfig.RunConfiguration)}; + } else { + projects = new Tuple<SolutionItem, SolutionItemRunConfiguration> [0]; } - - if (currentSolution != null) { - currentStartupProject = currentSolution.StartupItem; - if (currentStartupProject != null) { - currentStartupProject.ExecutionTargetsChanged += executionTargetsChanged; + if (!startupProjects.SequenceEqual (projects)) { + foreach (var item in startupProjects) { + item.Item1.ExecutionTargetsChanged -= executionTargetsChanged; } + foreach (var item in projects) + item.Item1.ExecutionTargetsChanged += executionTargetsChanged; + startupProjects = projects; } - else - currentStartupProject = null; } bool updatingCombos; @@ -842,31 +908,31 @@ namespace MonoDevelop.Components.MainToolbar List<IRuntimeModel> children = new List<IRuntimeModel> (); public object Command { get; private set; } public ExecutionTarget ExecutionTarget { get; private set; } + string DisplayText = null; + bool fullText; RuntimeModel (MainToolbarController controller) { Controller = controller; } - public RuntimeModel (MainToolbarController controller, ActionCommand command) : this (controller) + public RuntimeModel (MainToolbarController controller, string displayText) : this (controller) { - Command = command.Id; + DisplayText = displayText; } - public RuntimeModel (MainToolbarController controller, ExecutionTarget target) : this (controller) + public RuntimeModel (MainToolbarController controller, ActionCommand command) : this (controller) { - ExecutionTarget = target; + Command = command.Id; } - public RuntimeModel (MainToolbarController controller, ExecutionTarget target, RuntimeModel parent) : this (controller, target) + public RuntimeModel (MainToolbarController controller, ExecutionTarget target, bool fullText, SolutionItem project) : this (controller) { - if (parent == null) - HasParent = false; - else { - HasParent = true; - parent.HasChildren = true; - parent.AddChild (this); - } + if (target == null) + throw new ArgumentNullException (nameof (target)); + ExecutionTarget = target; + this.fullText = fullText; + Project = project; } public void AddChild (IRuntimeModel child) @@ -884,16 +950,11 @@ namespace MonoDevelop.Components.MainToolbar public bool HasChildren { get; - private set; - } - - public bool HasParent { - get; set; } public bool IsSeparator { - get { return Command == null && ExecutionTarget == null; } + get { return Command == null && ExecutionTarget == null && DisplayText == null; } } public bool IsIndented { @@ -916,14 +977,27 @@ namespace MonoDevelop.Components.MainToolbar } } + public SolutionItem Project { get; } + public IRuntimeMutableModel GetMutableModel () { - return Command != null ? new RuntimeMutableModel (Controller, Command) : new RuntimeMutableModel (ExecutionTarget, HasParent); + if (Command != null) + return new RuntimeMutableModel (Controller, Command); + else if (ExecutionTarget != null) + return new RuntimeMutableModel (ExecutionTarget, fullText); + else + return new RuntimeMutableModel (DisplayText); } } class RuntimeMutableModel : IRuntimeMutableModel { + public RuntimeMutableModel(string text) + { + Enabled = Visible = true; + DisplayString = FullDisplayString = text; + } + public RuntimeMutableModel (MainToolbarController controller, object command) { var ci = IdeApp.CommandService.GetCommandInfo (command, new CommandTargetRoute (controller.lastCommandTarget)); @@ -932,7 +1006,7 @@ namespace MonoDevelop.Components.MainToolbar DisplayString = FullDisplayString = RemoveUnderline (ci.Text); } - public RuntimeMutableModel (ExecutionTarget target, bool hasParent) + public RuntimeMutableModel (ExecutionTarget target, bool fullName) { Enabled = !(target is ExecutionTargetGroup); Visible = true; @@ -940,7 +1014,7 @@ namespace MonoDevelop.Components.MainToolbar DisplayString = FullDisplayString = string.Empty; else { FullDisplayString = target.FullName; - DisplayString = !hasParent ? target.FullName : target.Name; + DisplayString = fullName ? target.FullName : target.Name; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarModels.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarModels.cs index 7dd60d4b64..280e33fdac 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarModels.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarModels.cs @@ -84,13 +84,10 @@ namespace MonoDevelop.Components.MainToolbar bool Notable { get; } /// <summary> - /// Gets a value indicating whether this instance has a parent. + /// Gets the project to which this runtime belongs. /// </summary> - /// <remarks> - /// If a menu item has a parent, it means it is in this list just for easier traversal and it should not be displayed. - /// </remarks> - /// <value><c>true</c> if this instance has a parent; otherwise, <c>false</c>.</value> - bool HasParent { get; } + /// <value>The project.</value> + MonoDevelop.Projects.SolutionItem Project { get; } /// <summary> /// Gets the runtime combo item model. diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs index eacc488c97..128eb40283 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/ProjectCommands.cs @@ -300,7 +300,7 @@ namespace MonoDevelop.Ide.Commands protected override void Update (CommandInfo info) { IBuildTarget buildTarget = IdeApp.ProjectOperations.CurrentSelectedBuildTarget; - info.Enabled = ((buildTarget != null) && (!(buildTarget is Workspace)) && IdeApp.ProjectOperations.CanExecute (buildTarget) && IdeApp.ProjectOperations.CurrentRunOperation.IsCompleted); + info.Enabled = ((buildTarget != null) && (!(buildTarget is Workspace)) && IdeApp.ProjectOperations.CanExecute (buildTarget)); } protected override void Run () diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/SolutionNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/SolutionNodeBuilder.cs index 4852a7f502..c06160f4a7 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/SolutionNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ClassPad/SolutionNodeBuilder.cs @@ -116,6 +116,8 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad Solution solution = (Solution) dataObject; solution.NameChanged += OnCombineRenamed; solution.StartupItemChanged += OnStartupChanged; + solution.RunConfigurationsChanged += OnStartupChanged; + solution.StartupConfigurationChanged += OnStartupChanged; } public override void OnNodeRemoved (object dataObject) @@ -123,6 +125,8 @@ namespace MonoDevelop.Ide.Gui.Pads.ClassPad Solution solution = (Solution) dataObject; solution.NameChanged -= OnCombineRenamed; solution.StartupItemChanged -= OnStartupChanged; + solution.RunConfigurationsChanged -= OnStartupChanged; + solution.StartupConfigurationChanged -= OnStartupChanged; } void OnStartupChanged (object sender, EventArgs args) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs index 275e555542..f0e3b25b4c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectNodeBuilder.cs @@ -123,9 +123,10 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad } nodeInfo.Icon = Context.GetIcon (p.StockIcon); - if (p.ParentSolution != null && p.ParentSolution.SingleStartup && p.ParentSolution.StartupItem == p) + var sc = p.ParentSolution?.StartupConfiguration; + if (sc != null && IsStartupProject (p, sc)) { nodeInfo.Label = "<b>" + escapedProjectName + "</b>"; - else + } else nodeInfo.Label = escapedProjectName; // Gray out the project name if it is not selected in the current build configuration @@ -146,6 +147,15 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad } } + bool IsStartupProject (Project p, SolutionRunConfiguration sc) + { + var single = sc as SingleItemSolutionRunConfiguration; + if (single != null) + return single.Item == p; + var multi = sc as MultiItemSolutionRunConfiguration; + return multi != null && multi.Items.Any (si => si.SolutionItem == p); + } + public override void BuildChildNodes (ITreeBuilder builder, object dataObject) { Project project = (Project) dataObject; @@ -389,11 +399,7 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad { Project project = CurrentNode.DataItem as Project; project.ParentSolution.StartupItem = project; - if (!project.ParentSolution.SingleStartup) { - project.ParentSolution.SingleStartup = true; - await IdeApp.ProjectOperations.SaveAsync (project.ParentSolution); - } else - await project.ParentSolution.SaveUserProperties (); + await project.ParentSolution.SaveUserProperties (); } public override void DeleteItem () diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/SolutionNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/SolutionNodeBuilder.cs index 6b5fd12c36..16567bd651 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/SolutionNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/SolutionNodeBuilder.cs @@ -105,6 +105,8 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad Solution solution = (Solution) dataObject; solution.NameChanged += OnCombineRenamed; solution.StartupItemChanged += OnStartupChanged; + solution.RunConfigurationsChanged += OnStartupChanged; + solution.StartupConfigurationChanged += OnStartupChanged; solution.RootFolder.ItemAdded += OnEntryAdded; solution.RootFolder.ItemRemoved += OnEntryRemoved; solution.RootFolder.SolutionItemFileAdded += OnFileAdded; @@ -116,6 +118,8 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad Solution solution = (Solution) dataObject; solution.NameChanged -= OnCombineRenamed; solution.StartupItemChanged -= OnStartupChanged; + solution.RunConfigurationsChanged -= OnStartupChanged; + solution.StartupConfigurationChanged -= OnStartupChanged; solution.RootFolder.ItemAdded -= OnEntryAdded; solution.RootFolder.ItemRemoved -= OnEntryRemoved; solution.RootFolder.SolutionItemFileAdded -= OnFileAdded; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ProgressMonitors.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ProgressMonitors.cs index a5c5b8102e..bf9eedc9ad 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ProgressMonitors.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ProgressMonitors.cs @@ -80,7 +80,7 @@ namespace MonoDevelop.Ide.Gui public OutputProgressMonitor GetRunProgressMonitor () { - return GetOutputProgressMonitor ("MonoDevelop.Ide.ApplicationOutput", GettextCatalog.GetString ("Application Output"), Stock.RunProgramIcon, false, true); + return GetOutputProgressMonitor ("MonoDevelop.Ide.ApplicationOutput", GettextCatalog.GetString ("Application Output"), Stock.MessageLog, false, true); } public OutputProgressMonitor GetToolOutputProgressMonitor (bool bringToFront, CancellationTokenSource cs = null) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationPanel.cs new file mode 100644 index 0000000000..cc54643322 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationPanel.cs @@ -0,0 +1,124 @@ +// +// SolutionRunConfigurationPanel.cs +// +// Author: +// Lluis Sanchez Gual <lluis@xamarin.com> +// +// Copyright (c) 2016 Xamarin, Inc (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using MonoDevelop.Components; +using MonoDevelop.Ide.Gui.Dialogs; +using Xwt; +using MonoDevelop.Projects; +using System.Linq; +using MonoDevelop.Core; + +namespace MonoDevelop.Ide.Projects.OptionPanels +{ + public class SolutionRunConfigurationPanel: OptionsPanel + { + SolutionRunConfigInfo config; + ListStore store; + ListView listView; + DataField<bool> selectedField = new DataField<bool> (); + DataField<string> projectNameField = new DataField<string> (); + DataField<SolutionItem> projectField = new DataField<SolutionItem> (); + DataField<string> runConfigField = new DataField<string> (); + DataField<ItemCollection> projectRunConfigsField = new DataField<ItemCollection> (); + + public SolutionRunConfigurationPanel () + { + } + + public override void Initialize (OptionsDialog dialog, object dataObject) + { + base.Initialize (dialog, dataObject); + + config = (SolutionRunConfigInfo)dataObject; + + store = new ListStore (selectedField, projectNameField, projectField, runConfigField, projectRunConfigsField); + listView = new ListView (store); + + var col1 = new ListViewColumn (GettextCatalog.GetString ("Solution Item")); + var cb = new CheckBoxCellView (selectedField); + cb.Toggled += SelectionChanged; + cb.Editable = true; + col1.Views.Add (cb); + col1.Views.Add (new TextCellView (projectNameField)); + listView.Columns.Add (col1); + + var configSelView = new ComboBoxCellView (runConfigField); + configSelView.Editable = true; + configSelView.ItemsField = projectRunConfigsField; + var col2 = new ListViewColumn (GettextCatalog.GetString ("Run Configuration"), configSelView); + listView.Columns.Add (col2); + + foreach (var it in config.Solution.GetAllSolutionItems ().Where (si => si.SupportsExecute ()).OrderBy (si => si.Name)) { + var row = store.AddRow (); + var si = config.EditedConfig.Items.FirstOrDefault (i => i.SolutionItem == it); + var sc = si?.RunConfiguration?.Name ?? it.GetDefaultRunConfiguration ()?.Name; + var configs = new ItemCollection (); + foreach (var pc in it.GetRunConfigurations ()) + configs.Add (pc.Name); + store.SetValues (row, selectedField, si != null, projectNameField, it.Name, projectField, it, runConfigField, sc, projectRunConfigsField, configs); + } + } + + public override bool ValidateChanges () + { + return true; + } + + public override void ApplyChanges () + { + SaveChanges (); + if (config.ProjectConfig != null) + config.ProjectConfig.CopyFrom (config.EditedConfig); + } + + void SaveChanges () + { + config.EditedConfig.Items.Clear (); + for (int n = 0; n < store.RowCount; n++) { + if (store.GetValue (n, selectedField)) { + var proj = store.GetValue (n, projectField); + var rconf = store.GetValue (n, runConfigField); + var conf = rconf != null ? proj.GetRunConfigurations ().FirstOrDefault (c => c.Name == rconf) : null; + config.EditedConfig.Items.Add (new StartupItem (proj, conf)); + } + } + } + + public override Control CreatePanelWidget () + { + return new XwtControl (listView); + } + + void SelectionChanged (object sender, WidgetEventArgs e) + { + Xwt.Application.Invoke (delegate { + SaveChanges (); + var panel = ParentDialog.GetPanel<SolutionRunConfigurationsPanel> ("General"); + panel.RefreshList (); + }); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs new file mode 100644 index 0000000000..37f0020d2d --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/SolutionRunConfigurationsPanel.cs @@ -0,0 +1,283 @@ +// +// CodeFormattingPanelWidget.cs +// +// Author: +// Lluis Sanchez Gual <lluis@novell.com> +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using Mono.Addins; +using MonoDevelop.Ide.Gui.Dialogs; +using MonoDevelop.Ide.Extensions; +using MonoDevelop.Projects; +using MonoDevelop.Core; +using MonoDevelop.Projects.Policies; +using MonoDevelop.Components; +using System.Linq; +using RefactoringEssentials.CSharp.Diagnostics; +using Xwt.Backends; +using Xwt; + +namespace MonoDevelop.Ide.Projects.OptionPanels +{ + class SolutionRunConfigurationsPanel: OptionsPanel + { + List<SolutionRunConfigInfo> configs = new List<SolutionRunConfigInfo> (); + Dictionary<SolutionRunConfigInfo, SolutionRunConfigurationOptionsDialogSection> sections = new Dictionary<SolutionRunConfigInfo, SolutionRunConfigurationOptionsDialogSection> (); + SolutionRunConfigurationsPanelWidget widget; + XwtControl control; + + public override void Initialize (OptionsDialog dialog, object dataObject) + { + base.Initialize (dialog, dataObject); + + Solution = (Solution)dataObject; + + foreach (var rc in Solution.MultiStartupRunConfigurations) + configs.Add (new SolutionRunConfigInfo { ProjectConfig = rc, EditedConfig = new MultiItemSolutionRunConfiguration (rc) }); + + foreach (var c in configs) + AddPanel (c); + ParentDialog.ExpandChildren (this); + } + + public Solution Solution { get; set; } + + public List<SolutionRunConfigInfo> Configurations { + get { return configs; } + } + + public override void Dispose () + { + base.Dispose (); + } + + public void RefreshList () + { + if (widget != null) + widget.RefreshList (); + } + + void AddPanel (SolutionRunConfigInfo configInfo) + { + configInfo.Solution = Solution; + var sec = new SolutionRunConfigurationOptionsDialogSection (configInfo); + sec.Fill = true; + sections [configInfo] = sec; + ParentDialog.AddChildSection (this, sec, configInfo); + } + + void RemovePanel (SolutionRunConfigInfo rc) + { + var section = sections [rc]; + sections.Remove (rc); + ParentDialog.RemoveSection (section); + } + + internal void RemoveConfiguration (MultiItemSolutionRunConfiguration editedConfig) + { + var c = configs.First (ci => ci.EditedConfig == editedConfig); + configs.Remove (c); + RemovePanel (c); + } + + internal void AddConfiguration (MultiItemSolutionRunConfiguration editedConfig) + { + var c = new SolutionRunConfigInfo { EditedConfig = editedConfig }; + configs.Add (c); + AddPanel (c); + } + + internal void ReplaceConfiguration (MultiItemSolutionRunConfiguration oldConf, MultiItemSolutionRunConfiguration newConf) + { + var i = configs.FindIndex (ci => ci.EditedConfig == oldConf); + var oldc = configs [i]; + var newc = new SolutionRunConfigInfo { EditedConfig = newConf }; + configs [i] = newc; + RemovePanel (oldc); + AddPanel (newc); + } + + internal void ShowConfiguration (MultiItemSolutionRunConfiguration editedConfig) + { + var rc = configs.First (ci => ci.EditedConfig == editedConfig); + var section = sections [rc]; + ParentDialog.ShowPage (section); + } + + public override Control CreatePanelWidget () + { + widget = new SolutionRunConfigurationsPanelWidget (this, ParentDialog); + return control = new XwtControl (widget); + } + + public override void ApplyChanges () + { + foreach (var c in configs.Where (co => co.ProjectConfig == null)) { + c.ProjectConfig = new MultiItemSolutionRunConfiguration (c.EditedConfig); + Solution.MultiStartupRunConfigurations.Add (c.ProjectConfig); + } + foreach (var c in Solution.MultiStartupRunConfigurations.Where (co => !configs.Any (mc => mc.EditedConfig.Name == co.Name)).ToArray ()) + Solution.MultiStartupRunConfigurations.Remove (c); + } + } + + class SolutionRunConfigInfo + { + public Solution Solution { get; set; } + public MultiItemSolutionRunConfiguration ProjectConfig { get; set; } + public MultiItemSolutionRunConfiguration EditedConfig { get; set; } + } + + class SolutionRunConfigurationOptionsDialogSection : OptionsDialogSection + { + public SolutionRunConfigurationOptionsDialogSection (SolutionRunConfigInfo configInfo): base (typeof(SolutionRunConfigurationPanel)) + { + RunConfiguration = configInfo.EditedConfig; + Label = configInfo.EditedConfig.Name; + HeaderLabel = GettextCatalog.GetString ("Run Configuration: " + configInfo.EditedConfig.Name); + Icon = "md-prefs-play"; + } + + //this is used by the options dialog to look up the icon as needed, at required scales + public MultiItemSolutionRunConfiguration RunConfiguration { get; private set; } + } + + class SolutionRunConfigurationsPanelWidget: Xwt.VBox + { + SolutionRunConfigurationsPanel panel; + RunConfigurationsList list; + + Xwt.Button removeButton; + Xwt.Button copyButton; + Xwt.Button renameButton; + + public SolutionRunConfigurationsPanelWidget (SolutionRunConfigurationsPanel panel, OptionsDialog dialog) + { + this.panel = panel; + + Margin = 6; + Spacing = 6; + + list = new RunConfigurationsList (); + PackStart (list, true); + + var box = new Xwt.HBox (); + box.Spacing = 6; + + var btn = new Xwt.Button (GettextCatalog.GetString ("New")); + btn.Clicked += OnAddConfiguration; + box.PackStart (btn, false); + + copyButton = new Xwt.Button (GettextCatalog.GetString ("Duplicate")); + copyButton.Clicked += OnCopyConfiguration; + box.PackStart (copyButton, false); + + renameButton = new Xwt.Button (GettextCatalog.GetString ("Rename")); + renameButton.Clicked += OnRenameConfiguration; + box.PackStart (renameButton, false); + + removeButton = new Xwt.Button (GettextCatalog.GetString ("Remove")); + removeButton.Clicked += OnRemoveConfiguration; + box.PackEnd (removeButton, false); + + Fill (); + + PackStart (box, false); + + list.SelectionChanged += (sender, e) => UpdateButtons (); + list.RowActivated += (sender, e) => panel.ShowConfiguration ((MultiItemSolutionRunConfiguration)list.SelectedConfiguration); + UpdateButtons (); + } + + void Fill () + { + list.Fill (panel.Configurations.Select (c => c.EditedConfig).ToArray ()); + } + + public void RefreshList () + { + Fill (); + UpdateButtons (); + } + + void UpdateButtons () + { + var selection = list.SelectedConfiguration != null; + removeButton.Sensitive = selection; + copyButton.Sensitive = selection; + renameButton.Sensitive = selection; + } + + void OnAddConfiguration (object sender, EventArgs e) + { + var okCommand = new Command (GettextCatalog.GetString ("Create")); + using (var dlg = new RunConfigurationNameDialog (ParentWindow, "", okCommand, panel.Configurations.Select (c => c.EditedConfig.Name))) { + dlg.Title = GettextCatalog.GetString ("New Configuration"); + if (dlg.Run () == okCommand) { + var config = new MultiItemSolutionRunConfiguration (dlg.NewName, dlg.NewName); + panel.AddConfiguration (config); + Fill (); + } + } + } + + void OnCopyConfiguration (object sender, EventArgs e) + { + var config = (MultiItemSolutionRunConfiguration)list.SelectedConfiguration; + var okCommand = new Command (GettextCatalog.GetString ("Create")); + using (var dlg = new RunConfigurationNameDialog (ParentWindow, config.Name, okCommand, panel.Configurations.Select (c => c.EditedConfig.Name))) { + dlg.Title = GettextCatalog.GetString ("Duplicate Configuration"); + if (dlg.Run () == okCommand) { + var copy = new MultiItemSolutionRunConfiguration (config, dlg.NewName); + panel.AddConfiguration (copy); + Fill (); + } + } + } + + void OnRenameConfiguration (object sender, EventArgs e) + { + var config = (MultiItemSolutionRunConfiguration)list.SelectedConfiguration; + var okCommand = new Command (GettextCatalog.GetString ("Rename")); + using (var dlg = new RunConfigurationNameDialog (ParentWindow, config.Name, okCommand, panel.Configurations.Select (c => c.EditedConfig.Name))) { + dlg.Title = GettextCatalog.GetString ("Rename Configuration"); + if (dlg.Run () != Command.Cancel) { + var copy = new MultiItemSolutionRunConfiguration (config, dlg.NewName); + panel.ReplaceConfiguration (config, copy); + Fill (); + } + } + } + + void OnRemoveConfiguration (object sender, EventArgs e) + { + var config = (MultiItemSolutionRunConfiguration)list.SelectedConfiguration; + if (MessageService.Confirm (GettextCatalog.GetString ("Are you sure you want to remove the configuration '{0}'?", config.Name), AlertButton.Remove)) { + panel.RemoveConfiguration (config); + Fill (); + } + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/StartupOptionsPanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/StartupOptionsPanel.cs deleted file mode 100644 index cc17543aee..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/StartupOptionsPanel.cs +++ /dev/null @@ -1,207 +0,0 @@ -// StartupOptionsPanel.cs -// -// Author: -// Lluis Sanchez Gual <lluis@novell.com> -// -// Copyright (c) 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// - -using System; -using System.Collections.Generic; -using Gtk; -using MonoDevelop.Components; -using MonoDevelop.Projects; -using MonoDevelop.Core; -using MonoDevelop.Core.Execution; -using MonoDevelop.Ide.Gui.Dialogs; - -namespace MonoDevelop.Ide.Projects.OptionPanels -{ - [System.ComponentModel.Category("MonoDevelop.Projects.Gui")] - [System.ComponentModel.ToolboxItem(true)] - partial class StartupOptionsPanelWidget : Gtk.Bin - { - Solution sol; - ListStore listStore; - List<SolutionItem> startupItems; - - public StartupOptionsPanelWidget (Solution sol) - { - this.Build(); - this.sol = sol; - - startupItems = new List<SolutionItem> (); - foreach (SolutionItem it in sol.GetAllItems<SolutionItem> ()) { - // Include in the list if it can run in any of the existing execution modes and configurations - foreach (IExecutionModeSet mset in Runtime.ProcessService.GetExecutionModes ()) { - bool matched = false; - foreach (IExecutionMode mode in mset.ExecutionModes) { - foreach (SolutionConfiguration sc in sol.Configurations) { - if (it.CanExecute (new ExecutionContext (mode, null, IdeApp.Workspace.ActiveExecutionTarget), sc.Selector)) { - startupItems.Add (it); - matched = true; - break; - } - } - if (matched) - break; - } - if (matched) - break; - } - } - - listStore = new ListStore (typeof(SolutionFolderItem), typeof(bool), typeof(string)); - treeItems.Model = listStore; - treeItems.SearchColumn = -1; // disable the interactive search - - CellRendererToggle crt = new CellRendererToggle (); - treeItems.AppendColumn ("", crt, "active", 1); - treeItems.AppendColumn (GettextCatalog.GetString ("Project"), new CellRendererText (), "text", 2); - - if (startupItems.Count > 0) { - for (int n=0; n<startupItems.Count; n++) { - SolutionItem it = startupItems [n]; - comboItems.AppendText (it.Name); - listStore.AppendValues (it, sol.MultiStartupItems.Contains (it), it.Name); - if (sol.StartupItem == it) - comboItems.Active = n; - } - } - else { - comboItems.AppendText (GettextCatalog.GetString ("The solution does not contain any executable project")); - comboItems.Active = 0; - comboItems.Sensitive = false; - radioMulti.Sensitive = false; - radioSingle.Sensitive = false; - } - - radioSingle.Active = sol.SingleStartup; - radioMulti.Active = !sol.SingleStartup; - UpdateButtons (); - - crt.Toggled += OnItemToggled; - treeItems.Selection.Changed += OnSelectionChanged; - } - - void UpdateButtons () - { - TreeIter iter; - if (radioSingle.Active || !treeItems.Selection.GetSelected (out iter)) { - buttonUp.Sensitive = false; - buttonDown.Sensitive = false; - } - else { - TreeIter first; - listStore.GetIterFirst (out first); - buttonUp.Sensitive = !listStore.GetPath (iter).Equals (listStore.GetPath (first)); - buttonDown.Sensitive = listStore.IterNext (ref iter); - } - - treeItems.Sensitive = !radioSingle.Active; - comboItems.Sensitive = radioSingle.Active; - } - - void OnItemToggled (object s, ToggledArgs args) - { - Gtk.TreeIter it; - listStore.GetIterFromString (out it, args.Path); - bool run = (bool) listStore.GetValue (it, 1); - listStore.SetValue (it, 1, !run); - } - - protected virtual void OnButtonUpClicked (object sender, System.EventArgs e) - { - TreeIter iter; - if (!treeItems.Selection.GetSelected (out iter)) - return; - - TreePath tp = listStore.GetPath (iter); - Gtk.TreeIter pi; - if (tp.Prev () && listStore.GetIter (out pi, tp)) { - listStore.Swap (pi, iter); - treeItems.ScrollToCell (listStore.GetPath (iter), treeItems.Columns[0], false, 0, 0); - UpdateButtons (); - } - } - - protected virtual void OnButtonDownClicked (object sender, System.EventArgs e) - { - TreeIter iter; - if (!treeItems.Selection.GetSelected (out iter)) - return; - - TreeIter pit = iter; - listStore.IterNext (ref iter); - listStore.Swap (pit, iter); - treeItems.ScrollToCell (listStore.GetPath (pit), treeItems.Columns[0], false, 0, 0); - UpdateButtons (); - } - - void OnSelectionChanged (object s, EventArgs args) - { - UpdateButtons (); - } - - public void ApplyChanges () - { - sol.SingleStartup = radioSingle.Active; - sol.MultiStartupItems.Clear (); - - if (sol.SingleStartup) { - if (comboItems.Active != -1 && startupItems.Count > 0) - sol.StartupItem = startupItems [comboItems.Active]; - else - sol.StartupItem = null; - } else { - TreeIter it; - if (listStore.GetIterFirst (out it)) { - do { - if ((bool) listStore.GetValue (it, 1)) - sol.MultiStartupItems.Add ((SolutionItem) listStore.GetValue (it, 0)); - } while (listStore.IterNext (ref it)); - } - sol.StartupItem = null; - } - } - - protected virtual void OnRadioSingleToggled (object sender, System.EventArgs e) - { - UpdateButtons (); - } - } - - class StartupOptionsPanel: ItemOptionsPanel - { - StartupOptionsPanelWidget widget; - - public override Control CreatePanelWidget () - { - return widget = new StartupOptionsPanelWidget (ConfiguredSolution); - } - - public override void ApplyChanges () - { - widget.ApplyChanges (); - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index 2c1d93b97b..2575987ef9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -1,4 +1,4 @@ -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> @@ -8509,7 +8509,6 @@ <Compile Include="gtk-gui\MonoDevelop.Ide.Projects.OptionPanels.RunOptionsPanelWidget.cs" /> <Compile Include="gtk-gui\MonoDevelop.Ide.Projects.OptionPanels.RuntimeOptionsPanelWidget.cs" /> <Compile Include="gtk-gui\MonoDevelop.Ide.Projects.OptionPanels.CombineEntryConfigurationsPanelWidget.cs" /> - <Compile Include="gtk-gui\MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget.cs" /> <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\BaseDirectoryPanel.cs" /> <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\BaseDirectoryPanelWidget.cs" /> <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\CodeFormattingPanel.cs" /> @@ -8526,7 +8525,6 @@ <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\RunOptionsPanel.cs" /> <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\RuntimeOptionsPanel.cs" /> <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\SolutionItemConfigurationsPanel.cs" /> - <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\StartupOptionsPanel.cs" /> <Compile Include="gtk-gui\MonoDevelop.Ide.Projects.AddMimeTypeDialog.cs" /> <Compile Include="gtk-gui\MonoDevelop.Ide.Projects.ConfirmProjectDeleteDialog.cs" /> <Compile Include="gtk-gui\MonoDevelop.Ide.Projects.DeleteConfigDialog.cs" /> @@ -9072,6 +9070,8 @@ <Compile Include="gtk-gui\MonoDevelop.Ide.Editor.TextMate.TextMateBundleOptionsPanelWidget.cs" /> <Compile Include="MonoDevelop.Ide.Editor.Highlighting\LanguageBundle.cs" /> <Compile Include="MonoDevelop.Ide.Editor.Highlighting\StackMatchExpression.cs" /> + <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\SolutionRunConfigurationsPanel.cs" /> + <Compile Include="MonoDevelop.Ide.Projects.OptionPanels\SolutionRunConfigurationPanel.cs" /> <Compile Include="MonoDevelop.Ide.GettingStarted\GettingStarted.cs" /> <Compile Include="MonoDevelop.Ide.GettingStarted\GettingStartedNode.cs" /> <Compile Include="MonoDevelop.Ide.GettingStarted\GettingStartedNodeBuilder.cs" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs index adea023c87..dd1d1601b0 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs @@ -66,9 +66,9 @@ namespace MonoDevelop.Ide public class ProjectOperations { AsyncOperation<BuildResult> currentBuildOperation = new AsyncOperation<BuildResult> (Task.FromResult (BuildResult.CreateSuccess ()), null); - AsyncOperation currentRunOperation = AsyncOperation.CompleteOperation; + MultipleAsyncOperation currentRunOperation = MultipleAsyncOperation.CompleteMultipleOperation; IBuildTarget currentBuildOperationOwner; - IBuildTarget currentRunOperationOwner; + List<IBuildTarget> currentRunOperationOwners = new List<IBuildTarget> (); SelectReferenceDialog selDialog = null; @@ -160,9 +160,21 @@ namespace MonoDevelop.Ide public AsyncOperation CurrentRunOperation { get { return currentRunOperation; } - set { - currentRunOperation = value ?? AsyncOperation.CompleteOperation; + set { AddRunOperation (value); } + } + + public void AddRunOperation (AsyncOperation runOperation) + { + if (runOperation == null) + return; + if (runOperation.IsCompleted)//null or complete doesn't change anything, just ignore + return; + if (currentRunOperation.IsCompleted) {//if MultipleAsyncOperations is complete, we can't just restart Task.. start new one + currentRunOperation = new MultipleAsyncOperation (); + currentRunOperation.AddOperation (runOperation); OnCurrentRunOperationChanged (EventArgs.Empty); + } else {//Some process is already running... attach this one to it... + currentRunOperation.AddOperation (runOperation); } } @@ -171,11 +183,15 @@ namespace MonoDevelop.Ide var owner = currentBuildOperationOwner as WorkspaceObject; return owner != null && !currentBuildOperation.IsCompleted && ContainsTarget (ob, owner); } - + public bool IsRunning (WorkspaceObject target) { - var owner = currentRunOperationOwner as WorkspaceObject; - return owner != null && !currentRunOperation.IsCompleted && ContainsTarget (target, owner); + foreach (var currentRunOperationOwner in currentRunOperationOwners) { + var owner = currentRunOperationOwner as WorkspaceObject; + if (owner != null && !currentRunOperation.IsCompleted && ContainsTarget (target, owner)) + return true; + } + return false; } internal static bool ContainsTarget (WorkspaceObject owner, WorkspaceObject target) @@ -1023,8 +1039,6 @@ namespace MonoDevelop.Ide public AsyncOperation Execute (IBuildTarget entry, ExecutionContext context, bool buildBeforeExecuting = true) { - if (currentRunOperation != null && !currentRunOperation.IsCompleted) return currentRunOperation; - var cs = new CancellationTokenSource (); return new AsyncOperation (ExecuteAsync (entry, context, cs, IdeApp.Workspace.ActiveConfiguration, null, buildBeforeExecuting), cs); } @@ -1061,15 +1075,15 @@ namespace MonoDevelop.Ide var t = ExecuteSolutionItemAsync (monitor, entry, context, configuration, runConfiguration); var op = new AsyncOperation (t, cs); - CurrentRunOperation = op; - currentRunOperationOwner = entry; + AddRunOperation (op); + currentRunOperationOwners.Add (entry); await t; var error = monitor.Errors.FirstOrDefault (); if (error != null) IdeApp.Workbench.StatusBar.ShowError (error.DisplayMessage); - currentRunOperationOwner = null; + currentRunOperationOwners.Remove (entry); } async Task ExecuteSolutionItemAsync (ProgressMonitor monitor, IBuildTarget entry, ExecutionContext context, ConfigurationSelector configuration, RunConfiguration runConfiguration) @@ -2233,7 +2247,7 @@ namespace MonoDevelop.Ide if (IsRunning (args.Item)) { if (MessageService.Confirm (GettextCatalog.GetString ( "The project '{0}' is currently running and will have to be stopped. Do you want to continue closing it?", - currentRunOperationOwner.Name), + args.Item.Name), new AlertButton (GettextCatalog.GetString ("Close Project")))) { CurrentRunOperation.Cancel (); } else @@ -2304,6 +2318,45 @@ namespace MonoDevelop.Ide ITextDocument GetEditableTextFile (FilePath filePath); } + class MultipleAsyncOperation : AsyncOperation + { + public static MultipleAsyncOperation CompleteMultipleOperation = new MultipleAsyncOperation (true); + + List<AsyncOperation> Operations = new List<AsyncOperation> (); + TaskCompletionSource<int> TaskCompletionSource = new TaskCompletionSource<int> (); + + public MultipleAsyncOperation () + { + Task = TaskCompletionSource.Task; + CancellationTokenSource.Token.Register (MultiCancel); + } + + MultipleAsyncOperation (bool completed) + { + if (completed) + TaskCompletionSource.SetResult (0); + } + + public void AddOperation (AsyncOperation op) + { + Operations.Add (op); + op.Task.ContinueWith (CheckForCompletion); + } + + void CheckForCompletion (Task obj) + { + if (Operations.All (op => op.IsCompleted)) + TaskCompletionSource.SetResult (0); + } + + void MultiCancel () + { + foreach (var op in Operations) { + op.Cancel (); + } + } + } + public class TextFileProvider : ITextFileProvider { static TextFileProvider instance = new TextFileProvider (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs index 3dac774169..73be2cc5b8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs @@ -108,6 +108,11 @@ namespace MonoDevelop.Ide ActiveConfigurationChanged (this, EventArgs.Empty); } + public ExecutionTarget GetActiveExecutionTarget (SolutionItem project) + { + return (activeExecutionTarget as MultiProjectExecutionTarget)?.GetTarget (project) ?? activeExecutionTarget; + } + ExecutionTarget activeExecutionTarget; public ExecutionTarget ActiveExecutionTarget { get { return activeExecutionTarget; } diff --git a/main/src/core/MonoDevelop.Ide/gtk-gui/MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget.cs b/main/src/core/MonoDevelop.Ide/gtk-gui/MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget.cs deleted file mode 100644 index 9657683f0b..0000000000 --- a/main/src/core/MonoDevelop.Ide/gtk-gui/MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget.cs +++ /dev/null @@ -1,150 +0,0 @@ - -// This file has been generated by the GUI designer. Do not modify. -namespace MonoDevelop.Ide.Projects.OptionPanels -{ - internal partial class StartupOptionsPanelWidget - { - private global::Gtk.VBox vbox2; - private global::Gtk.RadioButton radioSingle; - private global::Gtk.HBox hbox2; - private global::Gtk.Label label1; - private global::Gtk.ComboBox comboItems; - private global::Gtk.RadioButton radioMulti; - private global::Gtk.HBox hbox1; - private global::Gtk.Label label2; - private global::Gtk.ScrolledWindow GtkScrolledWindow; - private global::Gtk.TreeView treeItems; - private global::Gtk.VBox vbox3; - private global::Gtk.Button buttonUp; - private global::Gtk.Button buttonDown; - - protected virtual void Build () - { - global::Stetic.Gui.Initialize (this); - // Widget MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget - global::Stetic.BinContainer.Attach (this); - this.Name = "MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget"; - // Container child MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget.Gtk.Container+ContainerChild - this.vbox2 = new global::Gtk.VBox (); - this.vbox2.Name = "vbox2"; - this.vbox2.Spacing = 6; - // Container child vbox2.Gtk.Box+BoxChild - this.radioSingle = new global::Gtk.RadioButton (global::Mono.Unix.Catalog.GetString ("Single startup project:")); - this.radioSingle.CanFocus = true; - this.radioSingle.Name = "radioSingle"; - this.radioSingle.DrawIndicator = true; - this.radioSingle.UseUnderline = true; - this.radioSingle.Group = new global::GLib.SList (global::System.IntPtr.Zero); - this.vbox2.Add (this.radioSingle); - global::Gtk.Box.BoxChild w1 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.radioSingle])); - w1.Position = 0; - w1.Expand = false; - w1.Fill = false; - // Container child vbox2.Gtk.Box+BoxChild - this.hbox2 = new global::Gtk.HBox (); - this.hbox2.Name = "hbox2"; - this.hbox2.Spacing = 6; - // Container child hbox2.Gtk.Box+BoxChild - this.label1 = new global::Gtk.Label (); - this.label1.WidthRequest = 12; - this.label1.Name = "label1"; - this.hbox2.Add (this.label1); - global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.label1])); - w2.Position = 0; - w2.Expand = false; - w2.Fill = false; - // Container child hbox2.Gtk.Box+BoxChild - this.comboItems = global::Gtk.ComboBox.NewText (); - this.comboItems.Name = "comboItems"; - this.hbox2.Add (this.comboItems); - global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.comboItems])); - w3.Position = 1; - this.vbox2.Add (this.hbox2); - global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox2])); - w4.Position = 1; - w4.Expand = false; - w4.Fill = false; - // Container child vbox2.Gtk.Box+BoxChild - this.radioMulti = new global::Gtk.RadioButton (global::Mono.Unix.Catalog.GetString ("Multiple startup projects:")); - this.radioMulti.CanFocus = true; - this.radioMulti.Name = "radioMulti"; - this.radioMulti.DrawIndicator = true; - this.radioMulti.UseUnderline = true; - this.radioMulti.Group = this.radioSingle.Group; - this.vbox2.Add (this.radioMulti); - global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.radioMulti])); - w5.Position = 2; - w5.Expand = false; - w5.Fill = false; - // Container child vbox2.Gtk.Box+BoxChild - this.hbox1 = new global::Gtk.HBox (); - this.hbox1.Name = "hbox1"; - this.hbox1.Spacing = 6; - // Container child hbox1.Gtk.Box+BoxChild - this.label2 = new global::Gtk.Label (); - this.label2.WidthRequest = 12; - this.label2.Name = "label2"; - this.hbox1.Add (this.label2); - global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.label2])); - w6.Position = 0; - w6.Expand = false; - w6.Fill = false; - // Container child hbox1.Gtk.Box+BoxChild - this.GtkScrolledWindow = new global::Gtk.ScrolledWindow (); - this.GtkScrolledWindow.Name = "GtkScrolledWindow"; - this.GtkScrolledWindow.ShadowType = ((global::Gtk.ShadowType)(1)); - // Container child GtkScrolledWindow.Gtk.Container+ContainerChild - this.treeItems = new global::Gtk.TreeView (); - this.treeItems.CanFocus = true; - this.treeItems.Name = "treeItems"; - this.GtkScrolledWindow.Add (this.treeItems); - this.hbox1.Add (this.GtkScrolledWindow); - global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.GtkScrolledWindow])); - w8.Position = 1; - // Container child hbox1.Gtk.Box+BoxChild - this.vbox3 = new global::Gtk.VBox (); - this.vbox3.Name = "vbox3"; - this.vbox3.Spacing = 6; - // Container child vbox3.Gtk.Box+BoxChild - this.buttonUp = new global::Gtk.Button (); - this.buttonUp.CanFocus = true; - this.buttonUp.Name = "buttonUp"; - this.buttonUp.UseStock = true; - this.buttonUp.UseUnderline = true; - this.buttonUp.Label = "gtk-go-up"; - this.vbox3.Add (this.buttonUp); - global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.buttonUp])); - w9.Position = 0; - w9.Expand = false; - w9.Fill = false; - // Container child vbox3.Gtk.Box+BoxChild - this.buttonDown = new global::Gtk.Button (); - this.buttonDown.CanFocus = true; - this.buttonDown.Name = "buttonDown"; - this.buttonDown.UseStock = true; - this.buttonDown.UseUnderline = true; - this.buttonDown.Label = "gtk-go-down"; - this.vbox3.Add (this.buttonDown); - global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.buttonDown])); - w10.Position = 1; - w10.Expand = false; - w10.Fill = false; - this.hbox1.Add (this.vbox3); - global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox3])); - w11.Position = 2; - w11.Expand = false; - w11.Fill = false; - this.vbox2.Add (this.hbox1); - global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox1])); - w12.Position = 3; - this.Add (this.vbox2); - if ((this.Child != null)) { - this.Child.ShowAll (); - } - this.Hide (); - this.radioSingle.Toggled += new global::System.EventHandler (this.OnRadioSingleToggled); - this.buttonUp.Clicked += new global::System.EventHandler (this.OnButtonUpClicked); - this.buttonDown.Clicked += new global::System.EventHandler (this.OnButtonDownClicked); - } - } -} diff --git a/main/src/core/MonoDevelop.Ide/gtk-gui/gui.stetic b/main/src/core/MonoDevelop.Ide/gtk-gui/gui.stetic index 711ad58b13..c4c962dfed 100644 --- a/main/src/core/MonoDevelop.Ide/gtk-gui/gui.stetic +++ b/main/src/core/MonoDevelop.Ide/gtk-gui/gui.stetic @@ -8690,175 +8690,6 @@ All solutions</property> </widget> </child> </widget> - <widget class="Gtk.Bin" id="MonoDevelop.Ide.Projects.OptionPanels.StartupOptionsPanelWidget" design-size="458 300"> - <property name="MemberName" /> - <property name="Visible">False</property> - <property name="GeneratePublic">False</property> - <child> - <widget class="Gtk.VBox" id="vbox2"> - <property name="MemberName" /> - <property name="Spacing">6</property> - <child> - <widget class="Gtk.RadioButton" id="radioSingle"> - <property name="MemberName" /> - <property name="CanFocus">True</property> - <property name="Label" translatable="yes">Single startup project:</property> - <property name="DrawIndicator">True</property> - <property name="HasLabel">True</property> - <property name="UseUnderline">True</property> - <property name="Group">group1</property> - <signal name="Toggled" handler="OnRadioSingleToggled" /> - </widget> - <packing> - <property name="Position">0</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - <child> - <widget class="Gtk.HBox" id="hbox2"> - <property name="MemberName" /> - <property name="Spacing">6</property> - <child> - <widget class="Gtk.Label" id="label1"> - <property name="MemberName" /> - <property name="WidthRequest">12</property> - </widget> - <packing> - <property name="Position">0</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - <child> - <widget class="Gtk.ComboBox" id="comboItems"> - <property name="MemberName" /> - <property name="IsTextCombo">True</property> - <property name="Items" translatable="yes" /> - </widget> - <packing> - <property name="Position">1</property> - <property name="AutoSize">False</property> - </packing> - </child> - </widget> - <packing> - <property name="Position">1</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - <child> - <widget class="Gtk.RadioButton" id="radioMulti"> - <property name="MemberName" /> - <property name="CanFocus">True</property> - <property name="Label" translatable="yes">Multiple startup projects:</property> - <property name="DrawIndicator">True</property> - <property name="HasLabel">True</property> - <property name="UseUnderline">True</property> - <property name="Group">group1</property> - </widget> - <packing> - <property name="Position">2</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - <child> - <widget class="Gtk.HBox" id="hbox1"> - <property name="MemberName" /> - <property name="Spacing">6</property> - <child> - <widget class="Gtk.Label" id="label2"> - <property name="MemberName" /> - <property name="WidthRequest">12</property> - </widget> - <packing> - <property name="Position">0</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - <child> - <widget class="Gtk.ScrolledWindow" id="GtkScrolledWindow"> - <property name="MemberName" /> - <property name="ShadowType">In</property> - <child> - <widget class="Gtk.TreeView" id="treeItems"> - <property name="MemberName" /> - <property name="CanFocus">True</property> - <property name="ShowScrollbars">True</property> - </widget> - </child> - </widget> - <packing> - <property name="Position">1</property> - <property name="AutoSize">True</property> - </packing> - </child> - <child> - <widget class="Gtk.VBox" id="vbox3"> - <property name="MemberName" /> - <property name="Spacing">6</property> - <child> - <widget class="Gtk.Button" id="buttonUp"> - <property name="MemberName" /> - <property name="CanFocus">True</property> - <property name="UseStock">True</property> - <property name="Type">StockItem</property> - <property name="StockId">gtk-go-up</property> - <signal name="Clicked" handler="OnButtonUpClicked" /> - <property name="label">gtk-go-up</property> - </widget> - <packing> - <property name="Position">0</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - <child> - <widget class="Gtk.Button" id="buttonDown"> - <property name="MemberName" /> - <property name="CanFocus">True</property> - <property name="UseStock">True</property> - <property name="Type">StockItem</property> - <property name="StockId">gtk-go-down</property> - <signal name="Clicked" handler="OnButtonDownClicked" /> - <property name="label">gtk-go-down</property> - </widget> - <packing> - <property name="Position">1</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - <child> - <placeholder /> - </child> - </widget> - <packing> - <property name="Position">2</property> - <property name="AutoSize">True</property> - <property name="Expand">False</property> - <property name="Fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="Position">3</property> - <property name="AutoSize">False</property> - </packing> - </child> - </widget> - </child> - </widget> <widget class="Gtk.Bin" id="MonoDevelop.Ide.Projects.OptionPanels.RunOptionsPanelWidget" design-size="473 371"> <property name="MemberName" /> <child> |