diff options
139 files changed, 6975 insertions, 3658 deletions
diff --git a/main/Directory.Build.props b/main/Directory.Build.props index 1108f347a8..a63e497e15 100644 --- a/main/Directory.Build.props +++ b/main/Directory.Build.props @@ -1,4 +1,5 @@ <Project> + <Import Project="$(MSBuildThisFileDirectory)msbuild\RoslynVersion.props"/> <PropertyGroup> <RootDirectory>$(MSBuildThisFileDirectory)</RootDirectory> @@ -19,7 +20,6 @@ <NuGetVersionNuGet>5.2.0-rtm.6067</NuGetVersionNuGet> <NuGetVersionNUnit2>2.7.0</NuGetVersionNUnit2> <NuGetVersionNUnit3>3.9.0</NuGetVersionNUnit3> - <NuGetVersionRoslyn>3.3.0-beta1-19360-03</NuGetVersionRoslyn> <NuGetVersionVSCodeDebugProtocol>15.8.20719.1</NuGetVersionVSCodeDebugProtocol> <NuGetVersionVSComposition>15.8.112</NuGetVersionVSComposition> <NuGetVersionVSEditor>16.1.28-g2ad4df7366</NuGetVersionVSEditor> diff --git a/main/build/MacOSX/monostub.mm b/main/build/MacOSX/monostub.mm index 36aca3a6ef..daec2cb615 100644 --- a/main/build/MacOSX/monostub.mm +++ b/main/build/MacOSX/monostub.mm @@ -260,7 +260,7 @@ main (int argc, char **argv) } // can be overridden with plist string MonoMinVersion - NSString *req_mono_version = @"6.0.0.296"; + NSString *req_mono_version = @"6.4.0.94"; NSDictionary *plist = [mainBundle infoDictionary]; if (plist) { diff --git a/main/external/debugger-libs b/main/external/debugger-libs -Subproject 694e1afd1f525e183cc60156d1bd929321bf87c +Subproject 15f8803077ae5f921c7e350c09df0ea2d221df6 diff --git a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/RoslynHelpers.fs b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/RoslynHelpers.fs index 7df93b10ee..11215f1ea8 100644 --- a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/RoslynHelpers.fs +++ b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/Services/RoslynHelpers.fs @@ -40,4 +40,5 @@ module RoslynHelpers = member x.ToMinimalDisplayString (_semanticModel, _position, _format) = symbolUse.Symbol.DisplayName //TODO format? member x.ToMinimalDisplayParts (_semanticModel, _position, _format) = ImmutableArray.Empty //TODO member x.HasUnsupportedMetadata = false //TODO + member x.Equals (other:Microsoft.CodeAnalysis.ISymbol, equalityComparer:Microsoft.CodeAnalysis.SymbolEqualityComparer) = equalityComparer.Equals(x, other) member x.Equals (other:Microsoft.CodeAnalysis.ISymbol) = x.Equals(other) diff --git a/main/external/vs-editor-api b/main/external/vs-editor-api -Subproject 5f7f209e67b239bf8a05a63cfd1d90ac2bbd65c +Subproject bf08f61283c38940a96fab59bfaa6e74f255207 diff --git a/main/external/xwt b/main/external/xwt -Subproject dfd9f3b4ccc103c8463332a67985a1363678811 +Subproject 8379063a5c0beee41ad795593a09f132486194d diff --git a/main/msbuild/RoslynVersion.props b/main/msbuild/RoslynVersion.props new file mode 100644 index 0000000000..cc1f50e536 --- /dev/null +++ b/main/msbuild/RoslynVersion.props @@ -0,0 +1,5 @@ +<Project> + <PropertyGroup> + <NuGetVersionRoslyn>3.3.0-beta2-19401-05</NuGetVersionRoslyn> + </PropertyGroup> +</Project>
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs index a4b6937b34..b34a637c1f 100644 --- a/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs +++ b/main/src/addins/MonoDevelop.AssemblyBrowser/MonoDevelop.AssemblyBrowser/AssemblyBrowserWidget.cs @@ -917,31 +917,36 @@ namespace MonoDevelop.AssemblyBrowser internal void Open (string url, AssemblyLoader currentAssembly = null, bool expandNode = true) { - Task.WhenAll (this.definitions.Select (d => d.LoadingTask)).ContinueWith (d => { - // At least one of them failed. - if (d.IsFaulted) { - LoggingService.LogError ("Failed to load assemblies", d.Exception); - - // It's possible the assembly in which the type we're looking for exists - // so try probing for it regardless. - } + try { + Task.WhenAll (this.definitions.Select (d => d.LoadingTask)).ContinueWith (d => { + // At least one of them failed. + if (d.IsFaulted) { + LoggingService.LogError ("Failed to load assemblies", d.Exception); + + // It's possible the assembly in which the type we're looking for exists + // so try probing for it regardless. + } - suspendNavigation = false; - ITreeNavigator nav = SearchMember (url, expandNode); - if (definitions.Count == 0) // we've been disposed - return; - if (nav != null) - return; - try { - if (currentAssembly != null) { - OpenFromAssembly (url, currentAssembly); - } else { - OpenFromAssemblyNames (url); + suspendNavigation = false; + ITreeNavigator nav = SearchMember (url, expandNode); + if (definitions.Count == 0) // we've been disposed + return; + if (nav != null) + return; + try { + if (currentAssembly != null) { + OpenFromAssembly (url, currentAssembly); + } else { + OpenFromAssemblyNames (url); + } + } catch (Exception e) { + LoggingService.LogError ("Error while opening the assembly browser with id:" + url, e); } - } catch (Exception e) { - LoggingService.LogError ("Error while opening the assembly browser with id:" + url, e); - } - }, Runtime.MainTaskScheduler).Ignore (); + }, Runtime.MainTaskScheduler).Ignore (); + } catch (Exception e) { + LoggingService.LogError ("Error while opening assembly :" + url); + MessageService.ShowError (GettextCatalog.GetString ("Error while opening assembly {0}.", url), e); + } } void OpenFromAssembly (string url, AssemblyLoader currentAssembly, bool expandNode = true) diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/BreakpointsAndSteppingTests.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/BreakpointsAndSteppingTests.cs new file mode 100644 index 0000000000..8966cc23c5 --- /dev/null +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/BreakpointsAndSteppingTests.cs @@ -0,0 +1,86 @@ +// +// BreakpointStoreTests.cs +// +// Author: +// Greg Munn <gregm@microsoft.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 NUnit.Framework; +using Mono.Debugging.Client; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Mono.Debugging.Tests +{ + [TestFixture] + public class BreakpointStoreTests + { + + [Test] + public async Task WhenStoreModifiedExternallyThenIteratingOverBreakpointsShouldNotThrow () + { + var store = new BreakpointStore (); + + Action<int> threadAddAction = (x) => { + store.Add (new Breakpoint ($"fileName{x}.cs", 10)); + }; + + Action threadEnumerateAction = () => { + foreach (var bk in store.GetBreakpointsAtFile ("fileName1.cs")) { + } + }; + + List<Task> tasks = new List<Task> (); + + for (int i = 0; i < 10; i++) { + tasks.Add (Task.Run (() => { + threadAddAction (i); + })); + + tasks.Add (Task.Run (() => { + threadEnumerateAction (); + })); + } + + foreach (var task in tasks) { + await task; + } + } + + [Test] + public void GetBreakpointsAtFileReturnsCorrectFiles () + { + var store = new BreakpointStore (); + + store.Add (new Breakpoint ("fileName1.cs", 10)); + store.Add (new Breakpoint ("fileName1.cs", 20)); + store.Add (new Breakpoint ("fileName2.cs", 10)); + store.Add (new Breakpoint ("fileName3.cs", 15)); + + int count = 0; + foreach (var bk in store.GetBreakpointsAtFile ("fileName1.cs")) { + count++; + Assert.AreEqual ("fileName1.cs", bk.FileName); + } + + Assert.AreEqual (2, count); + } + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj index 75a26167f2..6007317a64 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.Tests/MonoDevelop.Debugger.Tests.csproj @@ -22,6 +22,7 @@ <Compile Include="DebugTests.MonoDevelop.cs" /> <Compile Include="TextFile.cs" /> <Compile Include="VsCodeStackFrameTests.cs" /> + <Compile Include="BreakpointsAndSteppingTests.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\MonoDevelop.Debugger.csproj"> diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs index 9e7aff8e03..4b307559c2 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.VSTextView/BreakpointManager.cs @@ -68,13 +68,11 @@ namespace MonoDevelop.Debugger var bps = new List<Breakpoint> (); var needsUpdate = false; - lock (breakpointStore) { - foreach (var breakpoint in breakpointStore.GetBreakpointsAtFile (textDocument.FilePath)) { - if (breakpoint.Line > snapshot.LineCount) - continue; + foreach (var breakpoint in breakpointStore.GetBreakpointsAtFile (textDocument.FilePath)) { + if (breakpoint.Line > snapshot.LineCount) + continue; - bps.Add (breakpoint); - } + bps.Add (breakpoint); } foreach (var breakpoint in bps) { @@ -82,7 +80,7 @@ namespace MonoDevelop.Debugger needsUpdate = true; var line = snapshot.GetLineFromLineNumber (breakpoint.Line - 1); - var position = line.Start.Position + breakpoint.Column; + var position = line.Start.Position + breakpoint.Column - 1; var span = await DebuggingService.GetBreakpointSpanAsync (textDocument, position); if (breakpoints.TryGetValue (breakpoint, out var existingBreakpoint)) { diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/BreakpointPad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/BreakpointPad.cs index 40b79833c5..121c4f12fd 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/BreakpointPad.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/BreakpointPad.cs @@ -292,8 +292,7 @@ namespace MonoDevelop.Debugger continue; var bp = (BreakEvent) store.GetValue (iter, (int) Columns.Breakpoint); - lock (breakpoints) - breakpoints.Remove (bp); + breakpoints.Remove (bp); deleted = true; } } finally { @@ -421,32 +420,30 @@ namespace MonoDevelop.Debugger store.Clear (); if (breakpoints != null) { - lock (breakpoints) { - foreach (BreakEvent be in breakpoints) { - if (be.NonUserBreakpoint) - continue; - - string hitCount = be.HitCountMode != HitCountMode.None ? be.CurrentHitCount.ToString () : ""; - string traceExp = (be.HitAction & HitAction.PrintExpression) != HitAction.None ? be.TraceExpression : ""; - string traceVal = (be.HitAction & HitAction.PrintExpression) != HitAction.None ? be.LastTraceValue : ""; - string name, condition = null; - - if (be is FunctionBreakpoint fb) { - if (fb.ParamTypes != null) - name = fb.FunctionName + "(" + string.Join (", ", fb.ParamTypes) + ")"; - else - name = fb.FunctionName; - } else if (be is Breakpoint bp) { - name = string.Format ("{0}:{1},{2}", bp.FileName, bp.Line, bp.Column); - condition = bp.ConditionExpression; - } else if (be is Catchpoint cp) { - name = cp.ExceptionName; - } else { - name = ""; - } - - store.AppendValues (GetIconId (be), be.Enabled, name, be, condition, traceExp, hitCount, traceVal); + foreach (BreakEvent be in breakpoints) { + if (be.NonUserBreakpoint) + continue; + + string hitCount = be.HitCountMode != HitCountMode.None ? be.CurrentHitCount.ToString () : ""; + string traceExp = (be.HitAction & HitAction.PrintExpression) != HitAction.None ? be.TraceExpression : ""; + string traceVal = (be.HitAction & HitAction.PrintExpression) != HitAction.None ? be.LastTraceValue : ""; + string name, condition = null; + + if (be is FunctionBreakpoint fb) { + if (fb.ParamTypes != null) + name = fb.FunctionName + "(" + string.Join (", ", fb.ParamTypes) + ")"; + else + name = fb.FunctionName; + } else if (be is Breakpoint bp) { + name = string.Format ("{0}:{1},{2}", bp.FileName, bp.Line, bp.Column); + condition = bp.ConditionExpression; + } else if (be is Catchpoint cp) { + name = cp.ExceptionName; + } else { + name = ""; } + + store.AppendValues (GetIconId (be), be.Enabled, name, be, condition, traceExp, hitCount, traceVal); } } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs index 4b0b795f29..8f519906e9 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs @@ -322,22 +322,20 @@ namespace MonoDevelop.Debugger { var breakpoints = DebuggingService.Breakpoints; - lock (breakpoints) { - if (!breakpoints.IsReadOnly) { - foreach (var be in breakpoints) { - if (be is Breakpoint bp) { - if (!bp.NonUserBreakpoint) { - info.Enabled = true; - break; - } - } else { + if (!breakpoints.IsReadOnly) { + foreach (var be in breakpoints) { + if (be is Breakpoint bp) { + if (!bp.NonUserBreakpoint) { info.Enabled = true; break; } + } else { + info.Enabled = true; + break; } - } else { - info.Enabled = false; } + } else { + info.Enabled = false; } info.Visible = DebuggingService.IsFeatureSupported (DebuggerFeatures.Breakpoints); @@ -355,8 +353,7 @@ namespace MonoDevelop.Debugger var (caretLine, caretColumn) = textView.MDCaretLineAndColumn (); var point = textView.Caret.Position.BufferPosition; - lock (breakpoints) - bp = breakpoints.Toggle (IdeApp.Workbench.ActiveDocument.FileName, caretLine, caretColumn); + bp = breakpoints.Toggle (IdeApp.Workbench.ActiveDocument.FileName, caretLine, caretColumn); // If the breakpoint could not be inserted in the caret location, move the caret // to the real line of the breakpoint, so that if the Toggle command is run again, @@ -381,10 +378,8 @@ namespace MonoDevelop.Debugger { var breakpoints = DebuggingService.Breakpoints; - lock (breakpoints) { - foreach (var bp in breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ())) - bp.Enabled = !bp.Enabled; - } + foreach (var bp in breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ())) + bp.Enabled = !bp.Enabled; } protected override void Update (CommandInfo info) @@ -396,15 +391,13 @@ namespace MonoDevelop.Debugger IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true) != null && IdeApp.Workbench.ActiveDocument.FileName != FilePath.Null && !breakpoints.IsReadOnly) { - lock (breakpoints) { - var bpInLine = breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine()); - info.Enabled = bpInLine.Count > 0; - info.Text = GettextCatalog.GetString ("Disable Breakpoint"); - foreach (var bp in bpInLine) { - if (!bp.Enabled) - info.Text = GettextCatalog.GetString ("Enable Breakpoint"); - break; - } + var bpInLine = breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine()); + info.Enabled = bpInLine.Count > 0; + info.Text = GettextCatalog.GetString ("Disable Breakpoint"); + foreach (var bp in bpInLine) { + if (!bp.Enabled) + info.Text = GettextCatalog.GetString ("Enable Breakpoint"); + break; } } else { info.Enabled = false; @@ -416,20 +409,18 @@ namespace MonoDevelop.Debugger { protected override void Run () { - var breakpoints = DebuggingService.Breakpoints; + var breakpoints = DebuggingService.Breakpoints.ToList(); bool enable = false; - lock (breakpoints) { - foreach (BreakEvent bp in breakpoints) { - if (!bp.Enabled) { - enable = true; - break; - } + foreach (BreakEvent bp in breakpoints) { + if (!bp.Enabled) { + enable = true; + break; } + } - foreach (BreakEvent bp in breakpoints) { - bp.Enabled = enable; - } + foreach (BreakEvent bp in breakpoints) { + bp.Enabled = enable; } } @@ -437,20 +428,19 @@ namespace MonoDevelop.Debugger { var breakpoints = DebuggingService.Breakpoints; - lock (breakpoints) { - info.Enabled = !breakpoints.IsReadOnly && breakpoints.Count > 0; - bool enable = false; - foreach (BreakEvent bp in breakpoints) { - if (!bp.Enabled) { - enable = true; - break; - } + info.Enabled = !breakpoints.IsReadOnly && breakpoints.Count > 0; + bool enable = false; + foreach (BreakEvent bp in breakpoints) { + if (!bp.Enabled) { + enable = true; + break; } - if (enable) - info.Text = GettextCatalog.GetString ("Enable All Breakpoints"); - else - info.Text = GettextCatalog.GetString ("Disable All Breakpoints"); } + if (enable) + info.Text = GettextCatalog.GetString ("Enable All Breakpoints"); + else + info.Text = GettextCatalog.GetString ("Disable All Breakpoints"); + info.Visible = DebuggingService.IsFeatureSupported (DebuggerFeatures.Breakpoints); } } @@ -475,15 +465,13 @@ namespace MonoDevelop.Debugger { var breakpoints = DebuggingService.Breakpoints; - lock (breakpoints) { - IEnumerable<Breakpoint> brs = breakpoints.GetBreakpointsAtFileLine ( - IdeApp.Workbench.ActiveDocument.FileName, - IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()); + IEnumerable<Breakpoint> brs = breakpoints.GetBreakpointsAtFileLine ( + IdeApp.Workbench.ActiveDocument.FileName, + IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()); - List<Breakpoint> list = new List<Breakpoint> (brs); - foreach (Breakpoint bp in list) - breakpoints.Remove (bp); - } + List<Breakpoint> list = new List<Breakpoint> (brs); + foreach (Breakpoint bp in list) + breakpoints.Remove (bp); } protected override void Update (CommandInfo info) @@ -495,8 +483,7 @@ namespace MonoDevelop.Debugger IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true) != null && IdeApp.Workbench.ActiveDocument.FileName != FilePath.Null && !breakpoints.IsReadOnly) { - lock (breakpoints) - info.Enabled = breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()).Count > 0; + info.Enabled = breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()).Count > 0; } else { info.Enabled = false; } @@ -511,8 +498,7 @@ namespace MonoDevelop.Debugger if (DebuggingService.ShowBreakpointProperties (ref bp)) { var breakpoints = DebuggingService.Breakpoints; - lock (breakpoints) - breakpoints.Add (bp); + breakpoints.Add (bp); } } @@ -531,8 +517,7 @@ namespace MonoDevelop.Debugger if (DebuggingService.ShowBreakpointProperties (ref bp, BreakpointType.Function)) { var breakpoints = DebuggingService.Breakpoints; - lock (breakpoints) - breakpoints.Add (bp); + breakpoints.Add (bp); } } @@ -551,8 +536,7 @@ namespace MonoDevelop.Debugger if (DebuggingService.ShowBreakpointProperties (ref bp, BreakpointType.Catchpoint)) { var breakpoints = DebuggingService.Breakpoints; - lock (breakpoints) - breakpoints.Add (bp); + breakpoints.Add (bp); } } @@ -632,11 +616,9 @@ namespace MonoDevelop.Debugger var breakpoints = DebuggingService.Breakpoints; IList<Breakpoint> brs; - lock (breakpoints) { - brs = breakpoints.GetBreakpointsAtFileLine ( - IdeApp.Workbench.ActiveDocument.FileName, - IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()); - } + brs = breakpoints.GetBreakpointsAtFileLine ( + IdeApp.Workbench.ActiveDocument.FileName, + IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()); if (brs.Count > 0) { BreakEvent be = brs [0]; @@ -653,8 +635,7 @@ namespace MonoDevelop.Debugger IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true) != null && IdeApp.Workbench.ActiveDocument.FileName != FilePath.Null && !breakpoints.IsReadOnly) { - lock (breakpoints) - info.Enabled = breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()).Count > 0; + info.Enabled = breakpoints.GetBreakpointsAtFileLine (IdeApp.Workbench.ActiveDocument.FileName, IdeApp.Workbench.ActiveDocument.GetContent<ITextView> (true).MDCaretLine ()).Count > 0; } else { info.Enabled = false; } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs index 5ed2950d2e..e9139d5fa0 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs @@ -186,12 +186,10 @@ namespace MonoDevelop.Debugger if (liveUpdate) { var bp = pinnedWatches.CreateLiveUpdateBreakpoint (watch); pinnedWatches.Bind (watch, bp); - lock (breakpoints) - breakpoints.Add(bp); + breakpoints.Add(bp); } else { pinnedWatches.Bind (watch, null); - lock (breakpoints) - breakpoints.Remove (watch.BoundTracer); + breakpoints.Remove (watch.BoundTracer); } } @@ -1347,26 +1345,24 @@ namespace MonoDevelop.Debugger static void OnLineCountChanged (object ob, LineCountEventArgs a) { - lock (breakpoints) { - foreach (var bp in breakpoints.GetBreakpoints ()) { - if (bp.FileName == a.TextFile.Name) { - if (bp.Line > a.LineNumber) { - var startIndex = a.TextFile.GetPositionFromLineColumn (bp.Line, bp.Column); - var endIndex = a.TextFile.GetPositionFromLineColumn (bp.Line + 1, 0) - 1; + foreach (var bp in breakpoints.GetBreakpoints ()) { + if (bp.FileName == a.TextFile.Name) { + if (bp.Line > a.LineNumber) { + var startIndex = a.TextFile.GetPositionFromLineColumn (bp.Line, bp.Column); + var endIndex = a.TextFile.GetPositionFromLineColumn (bp.Line + 1, 0) - 1; - if (endIndex < startIndex) - endIndex = startIndex; + if (endIndex < startIndex) + endIndex = startIndex; - var text = a.TextFile.GetText (startIndex, endIndex); + var text = a.TextFile.GetText (startIndex, endIndex); - // If the line that has the breakpoint is deleted, delete the breakpoint, otherwise update the line #. - if (bp.Line + a.LineCount >= a.LineNumber && !string.IsNullOrWhiteSpace (text)) - breakpoints.UpdateBreakpointLine (bp, bp.Line + a.LineCount); - else - breakpoints.Remove (bp); - } else if (bp.Line == a.LineNumber && a.LineCount < 0) + // If the line that has the breakpoint is deleted, delete the breakpoint, otherwise update the line #. + if (bp.Line + a.LineCount >= a.LineNumber && !string.IsNullOrWhiteSpace (text)) + breakpoints.UpdateBreakpointLine (bp, bp.Line + a.LineCount); + else breakpoints.Remove (bp); - } + } else if (bp.Line == a.LineNumber && a.LineCount < 0) + breakpoints.Remove (bp); } } } @@ -1374,8 +1370,7 @@ namespace MonoDevelop.Debugger static void OnStoreUserPrefs (object s, UserPreferencesEventArgs args) { var baseDir = (args.Item as Solution)?.BaseDirectory; - lock (breakpoints) - args.Properties.SetValue ("MonoDevelop.Ide.DebuggingService.Breakpoints", breakpoints.Save (baseDir)); + args.Properties.SetValue ("MonoDevelop.Ide.DebuggingService.Breakpoints", breakpoints.Save (baseDir)); args.Properties.SetValue ("MonoDevelop.Ide.DebuggingService.PinnedWatches", pinnedWatches); } @@ -1385,27 +1380,23 @@ namespace MonoDevelop.Debugger if (elem != null) { var baseDir = (args.Item as Solution)?.BaseDirectory; - lock (breakpoints) - breakpoints.Load (elem, baseDir); + breakpoints.Load (elem, baseDir); } PinnedWatchStore wstore = args.Properties.GetValue<PinnedWatchStore> ("MonoDevelop.Ide.DebuggingService.PinnedWatches"); if (wstore != null) pinnedWatches.LoadFrom (wstore); - lock (breakpoints) - pinnedWatches.BindAll (breakpoints); + pinnedWatches.BindAll (breakpoints); - lock (breakpoints) - pinnedWatches.SetAllLiveUpdateBreakpoints (breakpoints); + pinnedWatches.SetAllLiveUpdateBreakpoints (breakpoints); return Task.FromResult (true); } static void OnSolutionClosed (object s, EventArgs args) { - lock (breakpoints) - breakpoints.Clear (); + breakpoints.Clear (); } static Microsoft.CodeAnalysis.ISymbol GetLanguageItem (MonoDevelop.Ide.Gui.Document document, SourceLocation sourceLocation, string identifier) diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs index e65c855147..8389942e4c 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs @@ -100,7 +100,7 @@ namespace MonoDevelop.Debugger // Shared ObjectValueTreeSelectedTextColor = Ide.Gui.Styles.BaseSelectionTextColor.ToHexString (false); - ObjectValueTreeForegroundTextColor = Ide.Gui.Styles.BaseSelectionTextColor.ToHexString (false); + ObjectValueTreeForegroundTextColor = Ide.Gui.Styles.BaseForegroundColor.ToHexString (false); ObjectValueTreeExternalCodeForegroundTextColor = ExceptionCaughtDialog.ExternalCodeTextColor.ToHexString (false); ObjectValueTreeValueErrorText = Ide.Gui.Styles.WarningForegroundColor; diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs index 0435aeb74c..fac52ab751 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ThreadsPad.cs @@ -159,18 +159,21 @@ namespace MonoDevelop.Debugger if (process != null) { //User right-clicked on thread and not process context_menu.Items.Add (new SeparatorContextMenuItem ()); var session = store.GetValue (selected, (int)Columns.Session) as DebuggerSession; - 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); + + if (session != null) { + 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); } diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Commands/DependenciesNodeCommandHandler.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Commands/DependenciesNodeCommandHandler.cs new file mode 100644 index 0000000000..b473f188cb --- /dev/null +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Commands/DependenciesNodeCommandHandler.cs @@ -0,0 +1,41 @@ +// +// DependenciesNodeCommandHandler.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// 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 MonoDevelop.Ide;
+using MonoDevelop.Ide.Gui.Components; +using MonoDevelop.PackageManagement; + +namespace MonoDevelop.DotNetCore.Commands +{ + class DependenciesNodeCommandHandler : NodeCommandHandler + { + public override void ActivateItem () + { + var runner = new ManagePackagesDialogRunner (); + runner.Run (IdeApp.ProjectOperations.CurrentSelectedProject); + } + } +} diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Commands/PackageDependenciesNodeCommandHandler.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Commands/PackageDependenciesNodeCommandHandler.cs index 78ee767357..cf2b31370a 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Commands/PackageDependenciesNodeCommandHandler.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Commands/PackageDependenciesNodeCommandHandler.cs @@ -24,6 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using MonoDevelop.Ide; using MonoDevelop.Ide.Gui.Components; using MonoDevelop.PackageManagement; @@ -33,8 +34,8 @@ namespace MonoDevelop.DotNetCore.Commands { public override void ActivateItem () { - var runner = new AddPackagesDialogRunner (); - runner.Run (); + var runner = new ManagePackagesDialogRunner (); + runner.Run (IdeApp.ProjectOperations.CurrentSelectedProject); } } } diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.NodeBuilders/DependenciesNodeBuilder.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.NodeBuilders/DependenciesNodeBuilder.cs index 8c00cd2380..107c754e6b 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.NodeBuilders/DependenciesNodeBuilder.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.NodeBuilders/DependenciesNodeBuilder.cs @@ -25,6 +25,7 @@ // THE SOFTWARE. using System; +using MonoDevelop.DotNetCore.Commands; using MonoDevelop.Ide.Gui.Components; namespace MonoDevelop.DotNetCore.NodeBuilders @@ -40,6 +41,10 @@ namespace MonoDevelop.DotNetCore.NodeBuilders return DependenciesNode.NodeName; } + public override Type CommandHandlerType { + get { return typeof (DependenciesNodeCommandHandler); } + } + public override void BuildNode (ITreeBuilder treeBuilder, object dataObject, NodeInfo nodeInfo) { var node = (DependenciesNode)dataObject; diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Templating/DotNetCoreProjectTemplateStringTagProvider.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Templating/DotNetCoreProjectTemplateStringTagProvider.cs index e3c1a79ef9..df44c28c89 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Templating/DotNetCoreProjectTemplateStringTagProvider.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Templating/DotNetCoreProjectTemplateStringTagProvider.cs @@ -25,6 +25,7 @@ // THE SOFTWARE. using System;
+using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -140,16 +141,32 @@ namespace MonoDevelop.DotNetCore.Templating return null; string templatesDirectory = Path.Combine ( - DotNetCoreSdk.SdkRootPath, - dotNetCoreSdk.OriginalString, - "Templates" - ); + DotNetCoreSdk.SdkRootPath, + dotNetCoreSdk.OriginalString, + "Templates" + ); - if (DirectoryExists (templatesDirectory)) { - return templatesDirectory; + if (!DirectoryExists (templatesDirectory)) { + // .NET Core 3.0 Preview 8 and higher place templates in the root dir + // and versioned by runtime, not SDK + var baseTemplatesDir = Path.Combine (Directory.GetParent (DotNetCoreSdk.SdkRootPath).FullName, "templates"); + if (DirectoryExists (baseTemplatesDir)) { + var availableTemplates = new Dictionary<DotNetCoreVersion, string> (); + foreach (var dir in EnumerateDirectories (baseTemplatesDir)) { + if (DotNetCoreVersion.TryParse (Path.GetFileName (dir), out var version)) { + availableTemplates [version] = dir; + } + } + + templatesDirectory = availableTemplates.Keys + .Where (v => v.Major < dotNetCoreSdk.Major || (v.Major == dotNetCoreSdk.Major && v.Minor <= dotNetCoreSdk.Minor)) + .OrderByDescending (v => v) + .Select (v => availableTemplates [v]) + .FirstOrDefault (); + } } - return string.Empty; + return DirectoryExists (templatesDirectory) ? templatesDirectory : string.Empty; } DotNetCoreVersion GetDotNetCoreSdkVersion (DotNetCoreVersion version) @@ -172,5 +189,10 @@ namespace MonoDevelop.DotNetCore.Templating /// Used by unit tests. /// </summary> internal Func<string, IEnumerable<string>> EnumerateFiles = Directory.EnumerateFiles; + + /// <summary> + /// Used by unit tests. + /// </summary> + internal Func<string, IEnumerable<string>> EnumerateDirectories = Directory.EnumerateDirectories; } } diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateStringTagProviderTests.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateStringTagProviderTests.cs index a9720ac747..4053f3f526 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateStringTagProviderTests.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.Tests/MonoDevelop.DotNetCore.Tests/DotNetCoreProjectTemplateStringTagProviderTests.cs @@ -24,9 +24,10 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -using System;
+using System; using System.Collections.Generic; using System.IO; +using System.Linq; using MonoDevelop.DotNetCore.Templating; using NUnit.Framework; @@ -52,6 +53,7 @@ namespace MonoDevelop.DotNetCore.Tests provider = new DotNetCoreProjectTemplateStringTagProvider (); provider.DirectoryExists = DirectoryExists; provider.EnumerateFiles = EnumerateFiles; + provider.EnumerateDirectories = EnumerateDirectories; } bool DirectoryExists (string directory) @@ -64,6 +66,11 @@ namespace MonoDevelop.DotNetCore.Tests return templateFiles; } + IEnumerable<string> EnumerateDirectories (string directory) + { + return directories.Where (dir => Directory.GetParent (dir).FullName == directory); + } + static string ToNativePath (string filePath) { if (Path.DirectorySeparatorChar == '\\') @@ -77,6 +84,34 @@ namespace MonoDevelop.DotNetCore.Tests return filePath.Replace ('\\', Path.DirectorySeparatorChar); } + protected void DotNetCoreSdksInstalled (string sdkVersion, string templatesVersion, bool globalTemplates) + { + DotNetCoreSdksInstalled (sdkVersion); + if (globalTemplates) { + AddGlobalProjectTemplateFile ( + templatesVersion, + $"microsoft.dotnet.common.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.common.projecttemplates.{templatesVersion}.nupkg", + $"microsoft.dotnet.test.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.test.projecttemplates.{templatesVersion}.nupkg", + $"microsoft.dotnet.web.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.web.projecttemplates.{templatesVersion}.nupkg", + $"microsoft.dotnet.web.spa.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.web.spa.projecttemplates.{templatesVersion}.nupkg"); + } else { + AddProjectTemplateFile ( + sdkVersion, + $"microsoft.dotnet.common.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.common.projecttemplates.{templatesVersion}.nupkg", + $"microsoft.dotnet.test.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.test.projecttemplates.{templatesVersion}.nupkg", + $"microsoft.dotnet.web.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.web.projecttemplates.{templatesVersion}.nupkg", + $"microsoft.dotnet.web.spa.projecttemplates.{templatesVersion}.aaa", + $"microsoft.dotnet.web.spa.projecttemplates.{templatesVersion}.nupkg"); + } + } + string GetTagValue (string tag) { return provider.GetTagValue (null, tag) as string; @@ -102,6 +137,35 @@ namespace MonoDevelop.DotNetCore.Tests return GetTagValue ($"DotNetCoreSdk.{version}.Templates.Web.Spa.ProjectTemplates.nupkg"); } + string GetExpectedTemplateTagValue (string sdkVersion, string templatesVersion, string filePrefix, bool globalTemplates) + { + if (globalTemplates) { + return Path.Combine ( + Directory.GetParent (sdkRootPath).FullName, + "templates", + templatesVersion, + $"{filePrefix}.{templatesVersion}.nupkg"); + } else { + return Path.Combine ( + sdkRootPath, + sdkVersion, + "Templates", + $"{filePrefix}.{templatesVersion}.nupkg"); + } + } + + void AddGlobalProjectTemplateFile (string sdkVersion, params string [] fileNames) + { + string directory = Path.Combine (Directory.GetParent (sdkRootPath).FullName, "templates"); + directories.Add (directory); + directory = Path.Combine (directory, sdkVersion); + directories.Add (directory); + foreach (string fileName in fileNames) { + string fullPath = Path.Combine (directory, fileName); + templateFiles.Add (fullPath); + } + } + void AddProjectTemplateFile (string sdkVersion, params string[] fileNames) { foreach (string fileName in fileNames) { @@ -129,6 +193,7 @@ namespace MonoDevelop.DotNetCore.Tests Assert.AreEqual (string.Empty, GetDotNetCoreSdkCommonProjectTemplatesTagValue ("2.1")); Assert.AreEqual (string.Empty, GetDotNetCoreSdkCommonProjectTemplatesTagValue ("2.2")); + Assert.AreEqual (string.Empty, GetDotNetCoreSdkCommonProjectTemplatesTagValue ("3.0")); } [TestCase ("2.1", "2.1.300-preview1-008174")] @@ -142,182 +207,56 @@ namespace MonoDevelop.DotNetCore.Tests Assert.AreEqual (string.Empty, result); } - [Test] - public void NetCore22Installed_CommonProjectTemplates () + [TestCase ("3.0.100-preview8-013592", "3.0.0-preview8-013592", true)] + [TestCase ("3.0.100-preview7-012821", "3.0.2.0.0-preview7.19365.3", false)] + [TestCase ("2.2.100-preview2-009404", "2.2.1.0.2-beta4-20180904-2003790", false)] + [TestCase ("2.1.701", "2.1.1.0.2-beta3", false)] + [TestCase ("2.1.300-preview1-008174", "2.1.1.0.1-beta3-20180215-1392068", false)] + public void NetCoreInstalled_CommonProjectTemplates (string sdkVersion, string templatesVersion, bool globalTemplates) { - DotNetCoreSdksInstalled ("2.2.100-preview2-009404"); - AddProjectTemplateFile ( - "2.2.100-preview2-009404", - "microsoft.dotnet.common.projecttemplates.2.2.1.0.2-beta4-20180904-2003790.nupkg", - "microsoft.dotnet.test.projecttemplates.2.2.1.0.2-beta4-20180821-1966911.nupkg", - "microsoft.dotnet.web.projecttemplates.2.2.2.2.0-preview2-35157.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.2.100-preview2-009404", - "Templates", - "microsoft.dotnet.common.projecttemplates.2.2.1.0.2-beta4-20180904-2003790.nupkg"); - - string result = GetDotNetCoreSdkCommonProjectTemplatesTagValue ("2.2"); - - Assert.AreEqual (expectedResult, result); + DotNetCoreSdksInstalled (sdkVersion, templatesVersion, globalTemplates); + Assert.AreEqual ( + GetExpectedTemplateTagValue (sdkVersion, templatesVersion, "microsoft.dotnet.common.projecttemplates", globalTemplates), + GetDotNetCoreSdkCommonProjectTemplatesTagValue (sdkVersion.Substring (0, 3))); } - [Test] - public void NetCore22Installed_TestProjectTemplates () + [TestCase ("3.0.100-preview8-013592", "3.0.0-preview8-013592", true)] + [TestCase ("3.0.100-preview7-012821", "3.0.1.0.2-beta4.19155.2", false)] + [TestCase ("2.2.100-preview2-009404", "2.2.1.0.2-beta4-20180904-2003790", false)] + [TestCase ("2.1.701", "2.1.1.0.2-beta4-20181009-2100240", false)] + [TestCase ("2.1.300-preview1-008174", "2.1.1.0.1-beta3-20180215-1392068", false)] + public void NetCoreInstalled_TestProjectTemplates (string sdkVersion, string templatesVersion, bool globalTemplates) { - DotNetCoreSdksInstalled ("2.2.100-preview2-009404"); - AddProjectTemplateFile ( - "2.2.100-preview2-009404", - "microsoft.dotnet.common.projecttemplates.2.2.1.0.2-beta4-20180904-2003790.nupkg", - "microsoft.dotnet.test.projecttemplates.2.2.1.0.2-beta4-20180821-1966911.nupkg", - "microsoft.dotnet.web.projecttemplates.2.2.2.2.0-preview2-35157.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.2.100-preview2-009404", - "Templates", - "microsoft.dotnet.test.projecttemplates.2.2.1.0.2-beta4-20180821-1966911.nupkg"); - - string result = GetDotNetCoreSdkTestProjectTemplatesTagValue ("2.2"); - - Assert.AreEqual (expectedResult, result); - } - - [Test] - public void NetCore22Installed_WebProjectTemplates () - { - DotNetCoreSdksInstalled ("2.2.100-preview2-009404"); - AddProjectTemplateFile ( - "2.2.100-preview2-009404", - "microsoft.dotnet.common.projecttemplates.2.2.1.0.2-beta4-20180904-2003790.nupkg", - "microsoft.dotnet.test.projecttemplates.2.2.1.0.2-beta4-20180821-1966911.nupkg", - "microsoft.dotnet.web.projecttemplates.2.2.2.2.0-preview2-35157.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.2.100-preview2-009404", - "Templates", - "microsoft.dotnet.web.projecttemplates.2.2.2.2.0-preview2-35157.nupkg"); - - string result = GetDotNetCoreSdkWebProjectTemplatesTagValue ("2.2"); - - Assert.AreEqual (expectedResult, result); + DotNetCoreSdksInstalled (sdkVersion, templatesVersion, globalTemplates); + Assert.AreEqual ( + GetExpectedTemplateTagValue (sdkVersion, templatesVersion, "microsoft.dotnet.test.projecttemplates", globalTemplates), + GetDotNetCoreSdkTestProjectTemplatesTagValue (sdkVersion.Substring (0, 3))); } - [Test] - public void NetCore22Installed_MatchingFileWithDifferentExtension_CommonProjectTemplates () + [TestCase ("3.0.100-preview8-013592", "3.0.0-preview8-013592", true)] + [TestCase ("3.0.100-preview7-012821", "3.0.0-preview7.19365.7", false)] + [TestCase ("2.2.100-preview2-009404", "2.2.2.2.0-preview2-35157", false)] + [TestCase ("2.1.701", "2.1.2.1.12", false)] + [TestCase ("2.1.300-preview1-008174", "2.1.2.1.0-preview1-final", false)] + public void NetCoreInstalled_WebProjectTemplates (string sdkVersion, string templatesVersion, bool globalTemplates) { - DotNetCoreSdksInstalled ("2.2.100-preview2-009404"); - AddProjectTemplateFile ( - "2.2.100-preview2-009404", - "microsoft.dotnet.common.projecttemplates.2.0.1.0.0-beta3-20171110-312.aaa", - "microsoft.dotnet.common.projecttemplates.2.2.1.0.2-beta4-20180904-2003790.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.2.100-preview2-009404", - "Templates", - "microsoft.dotnet.common.projecttemplates.2.2.1.0.2-beta4-20180904-2003790.nupkg"); - - string result = GetDotNetCoreSdkCommonProjectTemplatesTagValue ("2.2"); - - Assert.AreEqual (expectedResult, result); + DotNetCoreSdksInstalled (sdkVersion, templatesVersion, globalTemplates); + Assert.AreEqual ( + GetExpectedTemplateTagValue (sdkVersion, templatesVersion, "microsoft.dotnet.web.projecttemplates", globalTemplates), + GetDotNetCoreSdkWebProjectTemplatesTagValue (sdkVersion.Substring (0, 3))); } - [Test] - public void NetCore21Installed_CommonProjectTemplates () + [TestCase ("3.0.100-preview8-013592", "3.0.0-preview8-013592", true)] + [TestCase ("3.0.100-preview7-012821", "3.0.0-preview7.19365.7", false)] + [TestCase ("2.2.100-preview2-009404", "2.2.2.2.0-preview2-35157", false)] + [TestCase ("2.1.701", "2.1.12", false)] + [TestCase ("2.1.300-preview1-008174", "2.1.2.1.0-preview1-final", false)] + public void NetCoreInstalled_SpaWebProjectTemplates (string sdkVersion, string templatesVersion, bool globalTemplates) { - DotNetCoreSdksInstalled ("2.1.300-preview1-008174"); - AddProjectTemplateFile ( - "2.1.300-preview1-008174", - "microsoft.dotnet.common.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.test.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.web.projecttemplates.2.1.2.1.0-preview1-final.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.1.300-preview1-008174", - "Templates", - "microsoft.dotnet.common.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg"); - - string result = GetDotNetCoreSdkCommonProjectTemplatesTagValue ("2.1"); - - Assert.AreEqual (expectedResult, result); - } - - [Test] - public void NetCore21Installed_TestProjectTemplates () - { - DotNetCoreSdksInstalled ("2.1.300-preview1-008174"); - AddProjectTemplateFile ( - "2.1.300-preview1-008174", - "microsoft.dotnet.common.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.test.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.web.projecttemplates.2.1.2.1.0-preview1-final.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.1.300-preview1-008174", - "Templates", - "microsoft.dotnet.test.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg"); - - string result = GetDotNetCoreSdkTestProjectTemplatesTagValue ("2.1"); - - Assert.AreEqual (expectedResult, result); - } - - [Test] - public void NetCore21Installed_WebProjectTemplates () - { - DotNetCoreSdksInstalled ("2.1.300-preview1-008174"); - AddProjectTemplateFile ( - "2.1.300-preview1-008174", - "microsoft.dotnet.common.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.test.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.web.projecttemplates.2.1.2.1.0-preview1-final.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.1.300-preview1-008174", - "Templates", - "microsoft.dotnet.web.projecttemplates.2.1.2.1.0-preview1-final.nupkg"); - - string result = GetDotNetCoreSdkWebProjectTemplatesTagValue ("2.1"); - - Assert.AreEqual (expectedResult, result); - } - - [Test] - public void NetCore21Installed_SpaWebProjectTemplates () - { - DotNetCoreSdksInstalled ("2.1.300-preview1-008174"); - AddProjectTemplateFile ( - "2.1.300-preview1-008174", - "microsoft.dotnet.common.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.test.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg", - "microsoft.dotnet.web.spa.projecttemplates.2.1.2.1.0-preview1-final.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.1.300-preview1-008174", - "Templates", - "microsoft.dotnet.web.spa.projecttemplates.2.1.2.1.0-preview1-final.nupkg"); - - string result = GetDotNetCoreSdkSpaWebProjectTemplatesTagValue ("2.1"); - - Assert.AreEqual (expectedResult, result); - } - - [Test] - public void NetCore21Installed_MatchingFileWithDifferentExtension_CommonProjectTemplates () - { - DotNetCoreSdksInstalled ("2.1.300-preview1-008174"); - AddProjectTemplateFile ( - "2.1.300-preview1-008174", - "microsoft.dotnet.common.projecttemplates.2.0.1.0.0-beta3-20171110-312.aaa", - "microsoft.dotnet.common.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg"); - string expectedResult = Path.Combine ( - sdkRootPath, - "2.1.300-preview1-008174", - "Templates", - "microsoft.dotnet.common.projecttemplates.2.1.1.0.1-beta3-20180215-1392068.nupkg"); - - string result = GetDotNetCoreSdkCommonProjectTemplatesTagValue ("2.1"); - - Assert.AreEqual (expectedResult, result); + DotNetCoreSdksInstalled (sdkVersion, templatesVersion, globalTemplates); + Assert.AreEqual ( + GetExpectedTemplateTagValue (sdkVersion, templatesVersion, "microsoft.dotnet.web.spa.projecttemplates", globalTemplates), + GetDotNetCoreSdkSpaWebProjectTemplatesTagValue (sdkVersion.Substring (0, 3))); } [TestCase ("DotNetCoreSdk.2.1.Templates.Common.ProjectTemplates.txt")] diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.csproj b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.csproj index bb1c502dc0..a79bd177e0 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.csproj +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore.csproj @@ -91,6 +91,7 @@ <Compile Include="MonoDevelop.DotNetCore\FrameworkReference.cs" /> <Compile Include="MonoDevelop.DotNetCore.NodeBuilders\PackageDependencyNodePropertyProvider.cs" /> <Compile Include="MonoDevelop.DotNetCore.NodeBuilders\PackageDependencyNodeDescriptor.cs" /> + <Compile Include="MonoDevelop.DotNetCore.Commands\DependenciesNodeCommandHandler.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\..\external\mono-addins\Mono.Addins\Mono.Addins.csproj"> diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreVersion.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreVersion.cs index 4b8f3463ce..24a9ff0849 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreVersion.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreVersion.cs @@ -26,6 +26,7 @@ using System; using System.IO; +using System.Collections.Generic; using System.Linq; using MonoDevelop.Core; diff --git a/main/src/addins/MonoDevelop.DotNetCore/Properties/MonoDevelop.DotNetCore.addin.xml b/main/src/addins/MonoDevelop.DotNetCore/Properties/MonoDevelop.DotNetCore.addin.xml index 8e8459640d..32c246959b 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/Properties/MonoDevelop.DotNetCore.addin.xml +++ b/main/src/addins/MonoDevelop.DotNetCore/Properties/MonoDevelop.DotNetCore.addin.xml @@ -1214,7 +1214,7 @@ <SeparatorItem id="DependenciesEditReferenceSeparator" /> <CommandItem - id="MonoDevelop.PackageManagement.Commands.AddPackages" _label = "Add _Packages..." /> + id="MonoDevelop.PackageManagement.Commands.ManagePackagesInProject" /> <CommandItem id="MonoDevelop.PackageManagement.Commands.UpdateAllPackagesInProject" /> <CommandItem @@ -1222,7 +1222,7 @@ </Condition> <Condition id="ItemType" value="MonoDevelop.DotNetCore.NodeBuilders.PackageDependenciesNode"> <CommandItem - id="MonoDevelop.PackageManagement.Commands.AddPackages" _label = "Add _Packages..." /> + id="MonoDevelop.PackageManagement.Commands.ManagePackagesInProject" /> <CommandItem id="MonoDevelop.PackageManagement.Commands.UpdateAllPackagesInProject" /> <CommandItem @@ -1233,7 +1233,7 @@ value="MonoDevelop.DotNetCore.NodeBuilders.AssemblyDependenciesNode|MonoDevelop.DotNetCore.NodeBuilders.ProjectDependenciesNode"> <CommandItem id="MonoDevelop.Ide.Commands.ProjectCommands.AddReference" - insertbefore="MonoDevelop.PackageManagement.Commands.AddPackages" /> + insertbefore="MonoDevelop.PackageManagement.Commands.ManagePackagesInProject" /> </Condition> <Condition id="ItemType" value="UnknownProject|Solution"> <CommandItem diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/AddPackagesHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/AddPackagesHandler.cs index c16dc31352..1a08dcbb5f 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/AddPackagesHandler.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/AddPackagesHandler.cs @@ -24,16 +24,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-using System;
-
namespace MonoDevelop.PackageManagement.Commands
{
internal class AddPackagesHandler : PackagesCommandHandler
{
protected override void Run ()
{
- var runner = new AddPackagesDialogRunner ();
- runner.Run ();
+ var runner = new ManagePackagesDialogRunner ();
+ runner.Run (GetSelectedDotNetProject ());
}
protected override bool IsEnabled ()
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ManagePackagesHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ManagePackagesHandler.cs new file mode 100644 index 0000000000..91c593b4eb --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ManagePackagesHandler.cs @@ -0,0 +1,48 @@ +// +// ManagePackagesHandler.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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.Linq; +using MonoDevelop.PackageManagement.Commands; + +namespace MonoDevelop.PackageManagement +{ + internal class ManagePackagesHandler : PackagesCommandHandler + { + protected override void Run () + { + var runner = new ManagePackagesDialogRunner (); + runner.Run (); + } + + protected override bool IsEnabled () + { + var solution = GetSelectedSolution (); + return (solution != null) && + solution.GetAllDotNetProjects ().Any (); + } + } +} + diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ManagePackagesInProjectHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ManagePackagesInProjectHandler.cs new file mode 100644 index 0000000000..583c401cf9 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ManagePackagesInProjectHandler.cs @@ -0,0 +1,44 @@ +// +// ManagePackagesInProjectHandler.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2016 Xamarin Inc. (http://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 MonoDevelop.PackageManagement.Commands; + +namespace MonoDevelop.PackageManagement +{ + class ManagePackagesInProjectHandler : PackagesCommandHandler + { + protected override bool IsEnabled () + { + return IsDotNetProjectSelected (); + } + + protected override void Run () + { + var runner = new ManagePackagesDialogRunner (); + runner.Run (GetSelectedDotNetProject ()); + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ProjectPackagesFolderNodeCommandHandler.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ProjectPackagesFolderNodeCommandHandler.cs index b0b0a7b585..a07b239fd0 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ProjectPackagesFolderNodeCommandHandler.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Commands/ProjectPackagesFolderNodeCommandHandler.cs @@ -28,9 +28,9 @@ using System; using System.Collections.Generic;
using System.Linq;
using MonoDevelop.Components.Commands;
+using MonoDevelop.Ide; using MonoDevelop.Ide.Gui.Components;
using MonoDevelop.PackageManagement.NodeBuilders;
-using NuGet.ProjectManagement;
namespace MonoDevelop.PackageManagement.Commands
{
@@ -38,8 +38,8 @@ namespace MonoDevelop.PackageManagement.Commands {
public override void ActivateItem ()
{
- var runner = new AddPackagesDialogRunner ();
- runner.Run ();
+ var runner = new ManagePackagesDialogRunner ();
+ runner.Run (IdeApp.ProjectOperations.CurrentSelectedProject);
}
[CommandUpdateHandler (PackagesFolderNodeCommands.ReinstallAllPackagesInProject)]
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/PackageCellView.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesCellView.cs index aa6b3111d4..09611a1daa 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/PackageCellView.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesCellView.cs @@ -1,340 +1,340 @@ -//
-// PackageCellView.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2014 Xamarin Inc. (http://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.Core;
-using Xwt;
-using Xwt.Drawing;
-
-namespace MonoDevelop.PackageManagement
-{
- internal class PackageCellView : CanvasCellView
- {
- int packageIdFontSize;
- int packageDescriptionFontSize;
-
- public PackageCellView ()
- {
- CellWidth = 260;
-
- BackgroundColor = Styles.CellBackgroundColor;
- StrongSelectionColor = Styles.CellStrongSelectionColor;
- SelectionColor = Styles.CellSelectionColor;
-
- UseStrongSelectionColor = true;
-
- if (Platform.IsWindows) {
- packageIdFontSize = 10;
- packageDescriptionFontSize = 9;
- } else {
- packageIdFontSize = 12;
- packageDescriptionFontSize = 11;
- }
- }
-
- public IDataField<PackageSearchResultViewModel> PackageField { get; set; }
- public IDataField<Image> ImageField { get; set; }
- public IDataField<bool> HasBackgroundColorField { get; set; }
- public IDataField<double> CheckBoxAlphaField { get; set; }
-
- public double CellWidth { get; set; }
-
- public Color BackgroundColor { get; set; }
- public Color StrongSelectionColor { get; set; }
- public Color SelectionColor { get; set; }
-
- public bool UseStrongSelectionColor { get; set; }
-
- public event EventHandler<PackageCellViewEventArgs> PackageChecked;
-
- protected override void OnDraw (Context ctx, Rectangle cellArea)
- {
- PackageSearchResultViewModel packageViewModel = GetValue (PackageField);
- if (packageViewModel == null) {
- return;
- }
-
- FillCellBackground (ctx);
- UpdateTextColor (ctx);
-
- DrawCheckBox (ctx, packageViewModel, cellArea);
- DrawPackageImage (ctx, cellArea);
-
- double packageIdWidth = cellArea.Width - packageDescriptionPadding.HorizontalSpacing - packageDescriptionLeftOffset;
-
- // Package download count.
- if (packageViewModel.HasDownloadCount) {
- var downloadCountTextLayout = new TextLayout ();
- downloadCountTextLayout.Text = packageViewModel.GetDownloadCountOrVersionDisplayText ();
- Size size = downloadCountTextLayout.GetSize ();
- Point location = new Point (cellArea.Right - packageDescriptionPadding.Right, cellArea.Top + packageDescriptionPadding.Top);
- Point downloadLocation = location.Offset (-size.Width, 0);
- ctx.DrawTextLayout (downloadCountTextLayout, downloadLocation);
-
- packageIdWidth = downloadLocation.X - cellArea.Left - packageIdRightHandPaddingWidth - packageDescriptionPadding.HorizontalSpacing - packageDescriptionLeftOffset;
- }
-
- // Package Id.
- // Use the package id and not the package title to prevent a pango crash if the title
- // contains Chinese characters.
- var packageIdTextLayout = new TextLayout ();
- packageIdTextLayout.Font = packageIdTextLayout.Font.WithSize (packageIdFontSize);
- packageIdTextLayout.Markup = packageViewModel.GetIdMarkup ();
- packageIdTextLayout.Trimming = TextTrimming.WordElipsis;
- Size packageIdTextSize = packageIdTextLayout.GetSize ();
- packageIdTextLayout.Width = packageIdWidth;
- ctx.DrawTextLayout (
- packageIdTextLayout,
- cellArea.Left + packageDescriptionPadding.Left + packageDescriptionLeftOffset,
- cellArea.Top + packageDescriptionPadding.Top);
-
- // Package description.
- var descriptionTextLayout = new TextLayout ();
- descriptionTextLayout.Font = descriptionTextLayout.Font.WithSize (packageDescriptionFontSize);
- descriptionTextLayout.Width = cellArea.Width - packageDescriptionPadding.HorizontalSpacing - packageDescriptionLeftOffset;
- descriptionTextLayout.Height = cellArea.Height - packageIdTextSize.Height - packageDescriptionPadding.VerticalSpacing;
- descriptionTextLayout.Text = packageViewModel.Summary;
- descriptionTextLayout.Trimming = TextTrimming.Word;
-
- ctx.DrawTextLayout (
- descriptionTextLayout,
- cellArea.Left + packageDescriptionPadding.Left + packageDescriptionLeftOffset,
- cellArea.Top + packageIdTextSize.Height + packageDescriptionPaddingHeight + packageDescriptionPadding.Top);
- }
-
- void UpdateTextColor (Context ctx)
- {
- if (UseStrongSelectionColor && Selected) {
- ctx.SetColor (Styles.CellTextSelectionColor);
- } else {
- ctx.SetColor (Styles.CellTextColor);
- }
- }
-
- void FillCellBackground (Context ctx)
- {
- if (Selected) {
- FillCellBackground (ctx, GetSelectedColor ());
- } else if (IsBackgroundColorFieldSet ()) {
- FillCellBackground (ctx, BackgroundColor);
- }
- }
-
- Color GetSelectedColor ()
- {
- if (UseStrongSelectionColor) {
- return StrongSelectionColor;
- }
- return SelectionColor;
- }
-
- bool IsBackgroundColorFieldSet ()
- {
- return GetValue (HasBackgroundColorField, false);
- }
-
- void FillCellBackground (Context ctx, Color color)
- {
- ctx.Rectangle (BackgroundBounds);
- ctx.SetColor (color);
- ctx.Fill ();
- }
-
- void DrawCheckBox (Context ctx, PackageSearchResultViewModel packageViewModel, Rectangle cellArea)
- {
- CreateCheckboxImages ();
-
- Image image = GetCheckBoxImage (packageViewModel.IsChecked);
- double alpha = GetCheckBoxImageAlpha ();
- ctx.DrawImage (
- image,
- cellArea.Left + checkBoxPadding.Left,
- cellArea.Top + ((cellArea.Height - checkBoxImageSize.Height - 2) / 2),
- alpha);
- }
-
- void CreateCheckboxImages ()
- {
- if (whiteCheckedCheckBoxImage != null)
- return;
-
- var widget = Toolkit.CurrentEngine.GetNativeWidget (ParentWidget);
- var checkbox = new PackageCellViewCheckBox (ParentWidget.ParentWindow.Screen.ScaleFactor);
- checkbox.Container = (Gtk.Widget)widget;
- checkbox.Size = (int)checkBoxImageSize.Width + 1;
-
- // White checkbox.
- whiteUncheckedCheckBoxImage = checkbox.CreateImage ();
- checkbox.Active = true;
- whiteCheckedCheckBoxImage = checkbox.CreateImage ();
-
- // Odd numbered checkbox.
- checkbox.BackgroundColor = BackgroundColor;
- checkedCheckBoxWithBackgroundColorImage = checkbox.CreateImage ();
- checkbox.Active = false;
- uncheckedCheckBoxWithBackgroundColorImage = checkbox.CreateImage ();
-
- // Grey check box.
- checkbox.BackgroundColor = SelectionColor;
- greyUncheckedCheckBoxImage = checkbox.CreateImage ();
- checkbox.Active = true;
- greyCheckedCheckBoxImage = checkbox.CreateImage ();
-
- // Blue check box.
- checkbox.BackgroundColor = StrongSelectionColor;
- blueCheckedCheckBoxImage = checkbox.CreateImage ();
- checkbox.Active = false;
- blueUncheckedCheckBoxImage = checkbox.CreateImage ();
- }
-
- double GetCheckBoxImageAlpha ()
- {
- return GetValue (CheckBoxAlphaField, 1);
- }
-
- Image GetCheckBoxImage (bool checkBoxActive)
- {
- if (Selected && UseStrongSelectionColor && checkBoxActive) {
- return blueCheckedCheckBoxImage;
- } else if (Selected && checkBoxActive) {
- return greyCheckedCheckBoxImage;
- } else if (Selected && UseStrongSelectionColor) {
- return blueUncheckedCheckBoxImage;
- } else if (Selected) {
- return greyUncheckedCheckBoxImage;
- } else if (checkBoxActive && IsBackgroundColorFieldSet ()) {
- return checkedCheckBoxWithBackgroundColorImage;
- } else if (checkBoxActive) {
- return whiteCheckedCheckBoxImage;
- } else if (IsBackgroundColorFieldSet ()) {
- return uncheckedCheckBoxWithBackgroundColorImage;
- } else {
- return whiteUncheckedCheckBoxImage;
- }
- }
-
- void DrawPackageImage (Context ctx, Rectangle cellArea)
- {
- Image image = GetValue (ImageField);
-
- if (image == null) {
- image = defaultPackageImage;
- }
-
- if (Selected)
- image = image.WithStyles ("sel");
-
- if (PackageImageNeedsResizing (image)) {
- Point imageLocation = GetPackageImageLocation (maxPackageImageSize, cellArea);
- ctx.DrawImage (
- image,
- cellArea.Left + packageImagePadding.Left + checkBoxAreaWidth + imageLocation.X,
- Math.Round( cellArea.Top + packageImagePadding.Top + imageLocation.Y),
- maxPackageImageSize.Width,
- maxPackageImageSize.Height);
- } else {
- Point imageLocation = GetPackageImageLocation (image.Size, cellArea);
- ctx.DrawImage (
- image,
- cellArea.Left + packageImagePadding.Left + checkBoxAreaWidth + imageLocation.X,
- Math.Round (cellArea.Top + packageImagePadding.Top + imageLocation.Y));
- }
- }
-
- bool PackageImageNeedsResizing (Image image)
- {
- return (image.Width > maxPackageImageSize.Width) || (image.Height > maxPackageImageSize.Height);
- }
-
- Point GetPackageImageLocation (Size imageSize, Rectangle cellArea)
- {
- double width = (packageImageAreaWidth - imageSize.Width) / 2;
- double height = (cellArea.Height - imageSize.Height - packageImagePadding.Bottom) / 2;
- return new Point (width, height);
- }
-
+// +// PackageCellView.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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.Core; +using Xwt; +using Xwt.Drawing; + +namespace MonoDevelop.PackageManagement +{ + internal class ManagePackagesCellView : CanvasCellView + { + int packageIdFontSize; + int packageDescriptionFontSize; + + public ManagePackagesCellView () + { + CellWidth = 260; + + BackgroundColor = Styles.CellBackgroundColor; + StrongSelectionColor = Styles.CellStrongSelectionColor; + SelectionColor = Styles.CellSelectionColor; + + UseStrongSelectionColor = true; + + if (Platform.IsWindows) { + packageIdFontSize = 10; + packageDescriptionFontSize = 9; + } else { + packageIdFontSize = 12; + packageDescriptionFontSize = 11; + } + } + + public IDataField<ManagePackagesSearchResultViewModel> PackageField { get; set; } + public IDataField<Image> ImageField { get; set; } + public IDataField<bool> HasBackgroundColorField { get; set; } + public IDataField<double> CheckBoxAlphaField { get; set; } + + public double CellWidth { get; set; } + + public Color BackgroundColor { get; set; } + public Color StrongSelectionColor { get; set; } + public Color SelectionColor { get; set; } + + public bool UseStrongSelectionColor { get; set; } + + public event EventHandler<ManagePackagesCellViewEventArgs> PackageChecked; + + protected override void OnDraw (Context ctx, Rectangle cellArea) + { + ManagePackagesSearchResultViewModel packageViewModel = GetValue (PackageField); + if (packageViewModel == null) { + return; + } + + FillCellBackground (ctx); + UpdateTextColor (ctx); + + DrawCheckBox (ctx, packageViewModel, cellArea); + DrawPackageImage (ctx, cellArea); + + double packageIdWidth = cellArea.Width - packageDescriptionPadding.HorizontalSpacing - packageDescriptionLeftOffset; + + // Package download count. + if (packageViewModel.HasDownloadCount) { + var downloadCountTextLayout = new TextLayout (); + downloadCountTextLayout.Text = packageViewModel.GetDownloadCountOrVersionDisplayText (); + Size size = downloadCountTextLayout.GetSize (); + Point location = new Point (cellArea.Right - packageDescriptionPadding.Right, cellArea.Top + packageDescriptionPadding.Top); + Point downloadLocation = location.Offset (-size.Width, 0); + ctx.DrawTextLayout (downloadCountTextLayout, downloadLocation); + + packageIdWidth = downloadLocation.X - cellArea.Left - packageIdRightHandPaddingWidth - packageDescriptionPadding.HorizontalSpacing - packageDescriptionLeftOffset; + } + + // Package Id. + // Use the package id and not the package title to prevent a pango crash if the title + // contains Chinese characters. + var packageIdTextLayout = new TextLayout (); + packageIdTextLayout.Font = packageIdTextLayout.Font.WithSize (packageIdFontSize); + packageIdTextLayout.Markup = packageViewModel.GetIdMarkup (); + packageIdTextLayout.Trimming = TextTrimming.WordElipsis; + Size packageIdTextSize = packageIdTextLayout.GetSize (); + packageIdTextLayout.Width = packageIdWidth; + ctx.DrawTextLayout ( + packageIdTextLayout, + cellArea.Left + packageDescriptionPadding.Left + packageDescriptionLeftOffset, + cellArea.Top + packageDescriptionPadding.Top); + + // Package description. + var descriptionTextLayout = new TextLayout (); + descriptionTextLayout.Font = descriptionTextLayout.Font.WithSize (packageDescriptionFontSize); + descriptionTextLayout.Width = cellArea.Width - packageDescriptionPadding.HorizontalSpacing - packageDescriptionLeftOffset; + descriptionTextLayout.Height = cellArea.Height - packageIdTextSize.Height - packageDescriptionPadding.VerticalSpacing; + descriptionTextLayout.Text = packageViewModel.Summary; + descriptionTextLayout.Trimming = TextTrimming.Word; + + ctx.DrawTextLayout ( + descriptionTextLayout, + cellArea.Left + packageDescriptionPadding.Left + packageDescriptionLeftOffset, + cellArea.Top + packageIdTextSize.Height + packageDescriptionPaddingHeight + packageDescriptionPadding.Top); + } + + void UpdateTextColor (Context ctx) + { + if (Selected) { + ctx.SetColor (Styles.CellTextSelectionColor); + } else { + ctx.SetColor (Styles.CellTextColor); + } + } + + void FillCellBackground (Context ctx) + { + if (Selected) { + FillCellBackground (ctx, GetSelectedColor ()); + } else if (IsBackgroundColorFieldSet ()) { + FillCellBackground (ctx, BackgroundColor); + } + } + + Color GetSelectedColor () + { + if (UseStrongSelectionColor) { + return StrongSelectionColor; + } + return SelectionColor; + } + + bool IsBackgroundColorFieldSet () + { + return GetValue (HasBackgroundColorField, false); + } + + void FillCellBackground (Context ctx, Color color) + { + ctx.Rectangle (BackgroundBounds); + ctx.SetColor (color); + ctx.Fill (); + } + + void DrawCheckBox (Context ctx, ManagePackagesSearchResultViewModel packageViewModel, Rectangle cellArea) + { + CreateCheckboxImages (); + + Image image = GetCheckBoxImage (packageViewModel.IsChecked); + double alpha = GetCheckBoxImageAlpha (); + ctx.DrawImage ( + image, + cellArea.Left + checkBoxPadding.Left, + cellArea.Top + ((cellArea.Height - checkBoxImageSize.Height - 2) / 2), + alpha); + } + + void CreateCheckboxImages () + { + if (whiteCheckedCheckBoxImage != null) + return; + + var widget = Toolkit.CurrentEngine.GetNativeWidget (ParentWidget); + var checkbox = new ManagePackagesCellViewCheckBox (ParentWidget.ParentWindow.Screen.ScaleFactor); + checkbox.Container = (Gtk.Widget)widget; + checkbox.Size = (int)checkBoxImageSize.Width + 1; + + // White checkbox. + whiteUncheckedCheckBoxImage = checkbox.CreateImage (); + checkbox.Active = true; + whiteCheckedCheckBoxImage = checkbox.CreateImage (); + + // Odd numbered checkbox. + checkbox.BackgroundColor = BackgroundColor; + checkedCheckBoxWithBackgroundColorImage = checkbox.CreateImage (); + checkbox.Active = false; + uncheckedCheckBoxWithBackgroundColorImage = checkbox.CreateImage (); + + // Grey check box. + checkbox.BackgroundColor = SelectionColor; + greyUncheckedCheckBoxImage = checkbox.CreateImage (); + checkbox.Active = true; + greyCheckedCheckBoxImage = checkbox.CreateImage (); + + // Blue check box. + checkbox.BackgroundColor = StrongSelectionColor; + blueCheckedCheckBoxImage = checkbox.CreateImage (); + checkbox.Active = false; + blueUncheckedCheckBoxImage = checkbox.CreateImage (); + } + + double GetCheckBoxImageAlpha () + { + return GetValue (CheckBoxAlphaField, 1); + } + + Image GetCheckBoxImage (bool checkBoxActive) + { + if (Selected && UseStrongSelectionColor && checkBoxActive) { + return blueCheckedCheckBoxImage; + } else if (Selected && checkBoxActive) { + return greyCheckedCheckBoxImage; + } else if (Selected && UseStrongSelectionColor) { + return blueUncheckedCheckBoxImage; + } else if (Selected) { + return greyUncheckedCheckBoxImage; + } else if (checkBoxActive && IsBackgroundColorFieldSet ()) { + return checkedCheckBoxWithBackgroundColorImage; + } else if (checkBoxActive) { + return whiteCheckedCheckBoxImage; + } else if (IsBackgroundColorFieldSet ()) { + return uncheckedCheckBoxWithBackgroundColorImage; + } else { + return whiteUncheckedCheckBoxImage; + } + } + + void DrawPackageImage (Context ctx, Rectangle cellArea) + { + Image image = GetValue (ImageField); + + if (image == null) { + image = defaultPackageImage; + } + + if (Selected) + image = image.WithStyles ("sel"); + + if (PackageImageNeedsResizing (image)) { + Point imageLocation = GetPackageImageLocation (maxPackageImageSize, cellArea); + ctx.DrawImage ( + image, + cellArea.Left + packageImagePadding.Left + checkBoxAreaWidth + imageLocation.X, + Math.Round( cellArea.Top + packageImagePadding.Top + imageLocation.Y), + maxPackageImageSize.Width, + maxPackageImageSize.Height); + } else { + Point imageLocation = GetPackageImageLocation (image.Size, cellArea); + ctx.DrawImage ( + image, + cellArea.Left + packageImagePadding.Left + checkBoxAreaWidth + imageLocation.X, + Math.Round (cellArea.Top + packageImagePadding.Top + imageLocation.Y)); + } + } + + bool PackageImageNeedsResizing (Image image) + { + return (image.Width > maxPackageImageSize.Width) || (image.Height > maxPackageImageSize.Height); + } + + Point GetPackageImageLocation (Size imageSize, Rectangle cellArea) + { + double width = (packageImageAreaWidth - imageSize.Width) / 2; + double height = (cellArea.Height - imageSize.Height - packageImagePadding.Bottom) / 2; + return new Point (width, height); + } + protected override Size OnGetRequiredSize (SizeConstraint widthConstraint)
- {
- var layout = new TextLayout ();
- layout.Text = "W";
- layout.Font = layout.Font.WithSize (packageDescriptionFontSize);
- Size size = layout.GetSize ();
- return new Size (CellWidth, size.Height * linesDisplayedCount + packageDescriptionPaddingHeight + packageDescriptionPadding.VerticalSpacing);
- }
-
- protected override void OnButtonPressed (ButtonEventArgs args)
- {
- PackageSearchResultViewModel packageViewModel = GetValue (PackageField);
- if (packageViewModel == null) {
- base.OnButtonPressed (args);
- return;
- }
-
- double x = args.X - Bounds.X;
- double y = args.Y - Bounds.Y;
-
- if (checkBoxImageClickableRectangle.Contains (x, y)) {
- packageViewModel.IsChecked = !packageViewModel.IsChecked;
- OnPackageChecked (packageViewModel);
- }
- }
-
- void OnPackageChecked (PackageSearchResultViewModel packageViewModel)
- {
- if (PackageChecked != null) {
- PackageChecked (this, new PackageCellViewEventArgs (packageViewModel));
- }
- }
-
- const int packageDescriptionPaddingHeight = 5;
- const int packageIdRightHandPaddingWidth = 5;
- const int linesDisplayedCount = 4;
-
- const int checkBoxAreaWidth = 36;
- const int packageImageAreaWidth = 54;
- const int packageDescriptionLeftOffset = checkBoxAreaWidth + packageImageAreaWidth + 8;
-
- WidgetSpacing packageDescriptionPadding = new WidgetSpacing (5, 5, 5, 10);
- WidgetSpacing packageImagePadding = new WidgetSpacing (0, 0, 0, 5);
- WidgetSpacing checkBoxPadding = new WidgetSpacing (10, 0, 0, 10);
-
- Size maxPackageImageSize = new Size (48, 48);
- Size checkBoxImageSize = new Size (16, 16);
- Rectangle checkBoxImageClickableRectangle = new Rectangle (0, 10, 40, 50);
-
- Image whiteCheckedCheckBoxImage;
- Image whiteUncheckedCheckBoxImage;
- Image greyCheckedCheckBoxImage;
- Image greyUncheckedCheckBoxImage;
- Image blueCheckedCheckBoxImage;
- Image blueUncheckedCheckBoxImage;
- Image checkedCheckBoxWithBackgroundColorImage;
- Image uncheckedCheckBoxWithBackgroundColorImage;
-
- static readonly Image defaultPackageImage = Image.FromResource (typeof(PackageCellView), "package-48.png");
- }
-}
-
+ { + var layout = new TextLayout (); + layout.Text = "W"; + layout.Font = layout.Font.WithSize (packageDescriptionFontSize); + Size size = layout.GetSize (); + return new Size (CellWidth, size.Height * linesDisplayedCount + packageDescriptionPaddingHeight + packageDescriptionPadding.VerticalSpacing); + } + + protected override void OnButtonPressed (ButtonEventArgs args) + { + ManagePackagesSearchResultViewModel packageViewModel = GetValue (PackageField); + if (packageViewModel == null) { + base.OnButtonPressed (args); + return; + } + + double x = args.X - Bounds.X; + double y = args.Y - Bounds.Y; + + if (checkBoxImageClickableRectangle.Contains (x, y)) { + packageViewModel.IsChecked = !packageViewModel.IsChecked; + OnPackageChecked (packageViewModel); + } + } + + void OnPackageChecked (ManagePackagesSearchResultViewModel packageViewModel) + { + if (PackageChecked != null) { + PackageChecked (this, new ManagePackagesCellViewEventArgs (packageViewModel)); + } + } + + const int packageDescriptionPaddingHeight = 5; + const int packageIdRightHandPaddingWidth = 5; + const int linesDisplayedCount = 4; + + const int checkBoxAreaWidth = 36; + const int packageImageAreaWidth = 54; + const int packageDescriptionLeftOffset = checkBoxAreaWidth + packageImageAreaWidth + 8; + + WidgetSpacing packageDescriptionPadding = new WidgetSpacing (5, 5, 5, 10); + WidgetSpacing packageImagePadding = new WidgetSpacing (0, 0, 0, 5); + WidgetSpacing checkBoxPadding = new WidgetSpacing (10, 0, 0, 10); + + Size maxPackageImageSize = new Size (48, 48); + Size checkBoxImageSize = new Size (16, 16); + Rectangle checkBoxImageClickableRectangle = new Rectangle (0, 10, 40, 50); + + Image whiteCheckedCheckBoxImage; + Image whiteUncheckedCheckBoxImage; + Image greyCheckedCheckBoxImage; + Image greyUncheckedCheckBoxImage; + Image blueCheckedCheckBoxImage; + Image blueUncheckedCheckBoxImage; + Image checkedCheckBoxWithBackgroundColorImage; + Image uncheckedCheckBoxWithBackgroundColorImage; + + static readonly Image defaultPackageImage = Image.FromResource (typeof(ManagePackagesCellView), "package-48.png"); + } +} + diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/PackageCellViewCheckBox.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesCellViewCheckBox.cs index 5e0277c98d..c137669b79 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/PackageCellViewCheckBox.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesCellViewCheckBox.cs @@ -1,49 +1,49 @@ -//
-// PackageCellViewCheckBox.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2014 Xamarin Inc. (http://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.
-
+// +// ManagePackagesCellViewCheckBox.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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 Gtk;
namespace MonoDevelop.PackageManagement
{
- internal class PackageCellViewCheckBox
+ internal class ManagePackagesCellViewCheckBox
{
static int indicatorSize;
static int indicatorSpacing;
double scaleFactor;
- static PackageCellViewCheckBox ()
+ static ManagePackagesCellViewCheckBox ()
{
var cb = new Gtk.CheckButton ();
indicatorSize = (int) cb.StyleGetProperty ("indicator-size");
indicatorSpacing = (int) cb.StyleGetProperty ("indicator-spacing");
}
- public PackageCellViewCheckBox (double scaleFactor)
+ public ManagePackagesCellViewCheckBox (double scaleFactor)
{
this.scaleFactor = scaleFactor;
Size = indicatorSize;
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/PackageCellViewEventArgs.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesCellViewEventArgs.cs index 20e7920ec9..ae50585266 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/PackageCellViewEventArgs.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesCellViewEventArgs.cs @@ -1,41 +1,40 @@ -//
-// PackageCellViewEventArgs.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2014 Xamarin Inc. (http://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;
-
-namespace MonoDevelop.PackageManagement
-{
- internal class PackageCellViewEventArgs : EventArgs
- {
- public PackageCellViewEventArgs (PackageSearchResultViewModel packageViewModel)
- {
- PackageViewModel = packageViewModel;
- }
-
- public PackageSearchResultViewModel PackageViewModel { get; private set; }
- }
-}
-
+// +// PackageCellViewEventArgs.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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; + +namespace MonoDevelop.PackageManagement +{ + internal class ManagePackagesCellViewEventArgs : EventArgs + { + public ManagePackagesCellViewEventArgs (ManagePackagesSearchResultViewModel packageViewModel) + { + PackageViewModel = packageViewModel; + } + + public ManagePackagesSearchResultViewModel PackageViewModel { get; } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/AddPackagesDialog.UI.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesDialog.UI.cs index eda980a15c..c47a39aaf4 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/AddPackagesDialog.UI.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesDialog.UI.cs @@ -1,377 +1,476 @@ -//
-// AddPackagesDialog.UI.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2014 Xamarin Inc. (http://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 ExtendedTitleBarDialog = MonoDevelop.Components.ExtendedTitleBarDialog;
-using Mono.Unix;
-using MonoDevelop.Core;
-using MonoDevelop.Ide;
-using Xwt;
-using Xwt.Drawing;
-
-namespace MonoDevelop.PackageManagement
-{
- internal partial class AddPackagesDialog : ExtendedTitleBarDialog
- {
- ComboBox packageSourceComboBox;
- SearchTextEntry packageSearchEntry;
- ListView packagesListView;
- VBox packageInfoVBox;
- Label packageNameLabel;
- LinkLabel packageIdLink;
- Label packageDescription;
- Label packageAuthor;
- Label packagePublishedDate;
- Label packageDownloads;
- LinkLabel packageLicenseLink;
- LinkLabel packageProjectPageLink;
- Label packageDependenciesList;
- HBox packageDependenciesHBox;
- HBox packageDependenciesListHBox;
- Label packageDependenciesNoneLabel;
- CheckBox showPrereleaseCheckBox;
- Label packageId;
- Button addPackagesButton;
- FrameBox loadingSpinnerFrame;
- HBox errorMessageHBox;
- Label errorMessageLabel;
- Label loadingSpinnerLabel;
- FrameBox noPackagesFoundFrame;
- ComboBox packageVersionComboBox;
- HBox packageVersionsHBox;
- int packageInfoFontSize = 11;
-
- void Build ()
- {
- Title = Catalog.GetString ("Add Packages");
- Width = 820;
- Height = 520;
- Padding = new WidgetSpacing ();
-
- if (Platform.IsWindows) {
- packageInfoFontSize = 9;
- }
-
- // Top part of dialog:
- // Package sources and search.
- var topHBox = new HBox ();
- topHBox.Margin = new WidgetSpacing (8, 5, 6, 5);
-
- packageSourceComboBox = new ComboBox ();
- packageSourceComboBox.Name = "packageSourceComboBox";
- packageSourceComboBox.MinWidth = 200;
- topHBox.PackStart (packageSourceComboBox);
-
- packageSearchEntry = new SearchTextEntry ();
- packageSearchEntry.Name = "addPackagesDialogSearchEntry";
- packageSearchEntry.WidthRequest = 187;
- topHBox.PackEnd (packageSearchEntry);
-
- this.HeaderContent = topHBox;
-
- // Middle of dialog:
- // Packages and package information.
- var mainVBox = new VBox ();
- Content = mainVBox;
-
- var middleHBox = new HBox ();
- middleHBox.Spacing = 0;
- var middleFrame = new FrameBox ();
- middleFrame.Content = middleHBox;
- middleFrame.BorderWidth = new WidgetSpacing (0, 0, 0, 1);
- middleFrame.BorderColor = Styles.LineBorderColor;
- mainVBox.PackStart (middleFrame, true, true);
-
- // Error information.
- var packagesListVBox = new VBox ();
- packagesListVBox.Spacing = 0;
- errorMessageHBox = new HBox ();
- errorMessageHBox.Margin = new WidgetSpacing ();
- errorMessageHBox.BackgroundColor = Styles.ErrorBackgroundColor;
- errorMessageHBox.Visible = false;
- var errorImage = new ImageView ();
- errorImage.Margin = new WidgetSpacing (10, 0, 0, 0);
- errorImage.Image = ImageService.GetIcon (MonoDevelop.Ide.Gui.Stock.Warning, Gtk.IconSize.Menu);
- errorImage.HorizontalPlacement = WidgetPlacement.End;
- errorMessageHBox.PackStart (errorImage);
- errorMessageLabel = new Label ();
- errorMessageLabel.TextColor = Styles.ErrorForegroundColor;
- errorMessageLabel.Margin = new WidgetSpacing (5, 5, 5, 5);
- errorMessageLabel.Wrap = WrapMode.Word;
- errorMessageHBox.PackStart (errorMessageLabel, true);
- packagesListVBox.PackStart (errorMessageHBox);
-
- // Packages list.
- middleHBox.PackStart (packagesListVBox, true, true);
- packagesListView = new ListView ();
- packagesListView.BorderVisible = false;
- packagesListView.HeadersVisible = false;
- packagesListVBox.PackStart (packagesListView, true, true);
-
- // Loading spinner.
- var loadingSpinnerHBox = new HBox ();
- loadingSpinnerHBox.HorizontalPlacement = WidgetPlacement.Center;
- var loadingSpinner = new Spinner ();
- loadingSpinner.Animate = true;
- loadingSpinner.MinWidth = 20;
- loadingSpinnerHBox.PackStart (loadingSpinner);
-
- loadingSpinnerLabel = new Label ();
- loadingSpinnerLabel.Text = Catalog.GetString ("Loading package list...");
- loadingSpinnerHBox.PackEnd (loadingSpinnerLabel);
-
- loadingSpinnerFrame = new FrameBox ();
- loadingSpinnerFrame.Visible = false;
- loadingSpinnerFrame.BackgroundColor = Styles.BackgroundColor;
- loadingSpinnerFrame.Content = loadingSpinnerHBox;
- loadingSpinnerFrame.BorderWidth = new WidgetSpacing ();
- packagesListVBox.PackStart (loadingSpinnerFrame, true, true);
-
- // No packages found label.
- var noPackagesFoundHBox = new HBox ();
- noPackagesFoundHBox.HorizontalPlacement = WidgetPlacement.Center;
-
- var noPackagesFoundLabel = new Label ();
- noPackagesFoundLabel.Text = Catalog.GetString ("No matching packages found.");
- noPackagesFoundHBox.PackEnd (noPackagesFoundLabel);
-
- noPackagesFoundFrame = new FrameBox ();
- noPackagesFoundFrame.Visible = false;
- noPackagesFoundFrame.BackgroundColor = Styles.BackgroundColor;
- noPackagesFoundFrame.Content = noPackagesFoundHBox;
- noPackagesFoundFrame.BorderWidth = new WidgetSpacing ();
- packagesListVBox.PackStart (noPackagesFoundFrame, true, true);
-
- // Package information
- packageInfoVBox = new VBox ();
- var packageInfoFrame = new FrameBox ();
- packageInfoFrame.BackgroundColor = Styles.PackageInfoBackgroundColor;
- packageInfoFrame.BorderWidth = new WidgetSpacing ();
- packageInfoFrame.Content = packageInfoVBox;
- packageInfoVBox.Margin = new WidgetSpacing (15, 12, 15, 12);
- var packageInfoContainerVBox = new VBox ();
- packageInfoContainerVBox.WidthRequest = 240;
- packageInfoContainerVBox.PackStart (packageInfoFrame, true, true);
-
- var packageInfoScrollView = new ScrollView ();
- packageInfoScrollView.BorderVisible = false;
- packageInfoScrollView.HorizontalScrollPolicy = ScrollPolicy.Never;
- packageInfoScrollView.Content = packageInfoContainerVBox;
- packageInfoScrollView.BackgroundColor = Styles.PackageInfoBackgroundColor;
- var packageInfoScrollViewFrame = new FrameBox ();
- packageInfoScrollViewFrame.BackgroundColor = Styles.PackageInfoBackgroundColor;
- packageInfoScrollViewFrame.BorderWidth = new WidgetSpacing (1, 0, 0, 0);
- packageInfoScrollViewFrame.BorderColor = Styles.LineBorderColor;
- packageInfoScrollViewFrame.Content = packageInfoScrollView;
-
- // Package name and version.
- var packageNameHBox = new HBox ();
- packageInfoVBox.PackStart (packageNameHBox);
-
- packageNameLabel = new Label ();
- packageNameLabel.Ellipsize = EllipsizeMode.End;
- Font packageInfoSmallFont = packageNameLabel.Font.WithSize (packageInfoFontSize);
- Font packageInfoBoldFont = packageInfoSmallFont.WithWeight (FontWeight.Bold);
- packageNameLabel.Font = packageInfoSmallFont;
- packageNameHBox.PackStart (packageNameLabel, true);
-
- // Package description.
- packageDescription = new Label ();
- packageDescription.Wrap = WrapMode.Word;
- packageDescription.Font = packageNameLabel.Font.WithSize (packageInfoFontSize);
- packageDescription.BackgroundColor = Styles.PackageInfoBackgroundColor;
- packageInfoVBox.PackStart (packageDescription);
-
- // Package id.
- var packageIdHBox = new HBox ();
- packageIdHBox.MarginTop = 7;
- packageInfoVBox.PackStart (packageIdHBox);
-
- var packageIdLabel = new Label ();
+// +// ManagePackagesDialog.UI.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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 ExtendedTitleBarDialog = MonoDevelop.Components.ExtendedTitleBarDialog; +using InformationPopoverWidget = MonoDevelop.Components.InformationPopoverWidget; +using System; +using MonoDevelop.Core; +using MonoDevelop.Ide; +using Xwt; +using Xwt.Drawing; + +namespace MonoDevelop.PackageManagement +{ + internal partial class ManagePackagesDialog : ExtendedTitleBarDialog + { + ComboBox packageSourceComboBox; + SearchTextEntry packageSearchEntry; + ListView packagesListView; + VBox packageInfoVBox; + HBox packageNameHBox; + Label packageNameLabel; + LinkLabel packageIdLink; + Label packageDescription; + Label packageAuthor; + Label packagePublishedDate; + Label packageDownloads; + LinkLabel packageLicenseLink; + LinkLabel packageProjectPageLink; + Label packageDependenciesList; + HBox packageDependenciesHBox; + HBox packageDependenciesListHBox; + Label packageDependenciesNoneLabel; + CheckBox showPrereleaseCheckBox; + Label packageId; + Button addPackagesButton; + FrameBox loadingSpinnerFrame; + HBox errorMessageHBox; + Label errorMessageLabel; + Label loadingSpinnerLabel; + FrameBox noPackagesFoundFrame; + ComboBox packageVersionComboBox; + HBox packageVersionsHBox; + Label packageVersionsLabel; + Label browseLabel; + Label installedLabel; + Label updatesLabel; + Label consolidateLabel; + VBox projectsListViewVBox; + Label projectsListViewLabel; + ListView projectsListView; + HBox currentPackageVersionHBox; + Label currentPackageVersionLabel; + Label currentPackageVersion; + InformationPopoverWidget currentPackageVersionInfoPopoverWidget; + Button closeButton; + int packageInfoFontSize = 11; + + void Build () + { + Title = GettextCatalog.GetString ("Manage NuGet Packages – Solution"); + Width = 840; + Height = 528; + Padding = new WidgetSpacing (); + + if (Platform.IsWindows) { + packageInfoFontSize = 9; + } + + // Top part of dialog: + // Package sources and search. + var topHBox = new HBox (); + topHBox.Margin = new WidgetSpacing (8, 5, 6, 5); + + packageSourceComboBox = new ComboBox (); + packageSourceComboBox.Name = "packageSourceComboBox"; + packageSourceComboBox.MinWidth = 200; + topHBox.PackStart (packageSourceComboBox); + + int tabLabelMinWidth = 60; + browseLabel = new Label (); + browseLabel.Text = GettextCatalog.GetString ("Browse"); + browseLabel.Tag = browseLabel.Text; + browseLabel.MinWidth = tabLabelMinWidth; + browseLabel.MarginLeft = 10; + topHBox.PackStart (browseLabel); + + installedLabel = new Label (); + installedLabel.Text = GettextCatalog.GetString ("Installed"); + installedLabel.Tag = installedLabel.Text; + installedLabel.MinWidth = tabLabelMinWidth; + topHBox.PackStart (installedLabel); + + updatesLabel = new Label (); + updatesLabel.Text = GettextCatalog.GetString ("Updates"); + updatesLabel.Tag = updatesLabel.Text; + updatesLabel.MinWidth = tabLabelMinWidth; + topHBox.PackStart (updatesLabel); + + consolidateLabel = new Label (); + consolidateLabel.Text = GettextCatalog.GetString ("Consolidate"); + consolidateLabel.Tag = consolidateLabel.Text; + consolidateLabel.MinWidth = tabLabelMinWidth; + topHBox.PackStart (consolidateLabel); + + packageSearchEntry = new SearchTextEntry (); + packageSearchEntry.Name = "managePackagesDialogSearchEntry"; + packageSearchEntry.WidthRequest = 187; + topHBox.PackEnd (packageSearchEntry); + + this.HeaderContent = topHBox; + + // Middle of dialog: + // Packages and package information. + var mainVBox = new VBox (); + Content = mainVBox; + + var middleHBox = new HBox (); + middleHBox.Spacing = 0; + var middleFrame = new FrameBox (); + middleFrame.Content = middleHBox; + middleFrame.BorderWidth = new WidgetSpacing (0, 0, 0, 1); + middleFrame.BorderColor = Styles.LineBorderColor; + mainVBox.PackStart (middleFrame, true, true); + + // Error information. + var packagesListVBox = new VBox (); + packagesListVBox.Spacing = 0; + errorMessageHBox = new HBox (); + errorMessageHBox.Margin = new WidgetSpacing (); + errorMessageHBox.BackgroundColor = Styles.ErrorBackgroundColor; + errorMessageHBox.Visible = false; + var errorImage = new ImageView (); + errorImage.Margin = new WidgetSpacing (10, 0, 0, 0); + errorImage.Image = ImageService.GetIcon (MonoDevelop.Ide.Gui.Stock.Warning, Gtk.IconSize.Menu); + errorImage.HorizontalPlacement = WidgetPlacement.End; + errorMessageHBox.PackStart (errorImage); + errorMessageLabel = new Label (); + errorMessageLabel.TextColor = Styles.ErrorForegroundColor; + errorMessageLabel.Margin = new WidgetSpacing (5, 5, 5, 5); + errorMessageLabel.Wrap = WrapMode.Word; + errorMessageHBox.PackStart (errorMessageLabel, true); + packagesListVBox.PackStart (errorMessageHBox); + + // Packages list. + middleHBox.PackStart (packagesListVBox, true, true); + packagesListView = new ListView (); + packagesListView.BorderVisible = false; + packagesListView.HeadersVisible = false; + packagesListVBox.PackStart (packagesListView, true, true); + + // Loading spinner. + var loadingSpinnerHBox = new HBox (); + loadingSpinnerHBox.HorizontalPlacement = WidgetPlacement.Center; + var loadingSpinner = new Spinner (); + loadingSpinner.Animate = true; + loadingSpinner.MinWidth = 20; + loadingSpinnerHBox.PackStart (loadingSpinner); + + loadingSpinnerLabel = new Label (); + loadingSpinnerLabel.Text = GettextCatalog.GetString ("Loading package list..."); + loadingSpinnerHBox.PackEnd (loadingSpinnerLabel); + + loadingSpinnerFrame = new FrameBox (); + loadingSpinnerFrame.Visible = false; + loadingSpinnerFrame.BackgroundColor = Styles.BackgroundColor; + loadingSpinnerFrame.Content = loadingSpinnerHBox; + loadingSpinnerFrame.BorderWidth = new WidgetSpacing (); + packagesListVBox.PackStart (loadingSpinnerFrame, true, true); + + // No packages found label. + var noPackagesFoundHBox = new HBox (); + noPackagesFoundHBox.HorizontalPlacement = WidgetPlacement.Center; + + var noPackagesFoundLabel = new Label (); + noPackagesFoundLabel.Text = GettextCatalog.GetString ("No matching packages found."); + noPackagesFoundHBox.PackEnd (noPackagesFoundLabel); + + noPackagesFoundFrame = new FrameBox (); + noPackagesFoundFrame.Visible = false; + noPackagesFoundFrame.BackgroundColor = Styles.BackgroundColor; + noPackagesFoundFrame.Content = noPackagesFoundHBox; + noPackagesFoundFrame.BorderWidth = new WidgetSpacing (); + packagesListVBox.PackStart (noPackagesFoundFrame, true, true); + + // Package information + packageInfoVBox = new VBox (); + var packageInfoFrame = new FrameBox (); + packageInfoFrame.BackgroundColor = Styles.PackageInfoBackgroundColor; + packageInfoFrame.BorderWidth = new WidgetSpacing (); + packageInfoFrame.Content = packageInfoVBox; + packageInfoVBox.Margin = new WidgetSpacing (15, 12, 15, 12); + var packageInfoContainerVBox = new VBox (); + packageInfoContainerVBox.WidthRequest = 328; + packageInfoContainerVBox.PackStart (packageInfoFrame, true, true); + + var packageInfoScrollView = new ScrollView (); + packageInfoScrollView.BorderVisible = false; + packageInfoScrollView.HorizontalScrollPolicy = ScrollPolicy.Never; + packageInfoScrollView.Content = packageInfoContainerVBox; + packageInfoScrollView.BackgroundColor = Styles.PackageInfoBackgroundColor; + var packageInfoScrollViewFrame = new FrameBox (); + packageInfoScrollViewFrame.BackgroundColor = Styles.PackageInfoBackgroundColor; + packageInfoScrollViewFrame.BorderWidth = new WidgetSpacing (1, 0, 0, 0); + packageInfoScrollViewFrame.BorderColor = Styles.LineBorderColor; + packageInfoScrollViewFrame.Content = packageInfoScrollView; + + // Package name and version. + packageNameHBox = new HBox (); + packageInfoVBox.PackStart (packageNameHBox); + + packageNameLabel = new Label (); + packageNameLabel.Ellipsize = EllipsizeMode.End; + Font packageInfoSmallFont = packageNameLabel.Font.WithSize (packageInfoFontSize); + Font packageInfoBoldFont = packageInfoSmallFont.WithWeight (FontWeight.Bold); + packageNameLabel.Font = packageInfoSmallFont; + packageNameHBox.PackStart (packageNameLabel, true); + + // Projects list view label. + projectsListViewLabel = new Label (); + projectsListViewLabel.Wrap = WrapMode.Word; + projectsListViewLabel.BackgroundColor = Styles.PackageInfoBackgroundColor; + packageInfoVBox.PackStart (projectsListViewLabel); + + // Projects list view. + projectsListViewVBox = new VBox (); + projectsListViewVBox.Margin = new WidgetSpacing (); + packageInfoVBox.PackStart (projectsListViewVBox, true, true); + + // Package description. + packageDescription = new Label (); + packageDescription.Wrap = WrapMode.Word; + packageDescription.Font = packageNameLabel.Font.WithSize (packageInfoFontSize); + packageDescription.BackgroundColor = Styles.PackageInfoBackgroundColor; + packageInfoVBox.PackStart (packageDescription); + + // Package id. + var packageIdHBox = new HBox (); + packageIdHBox.MarginTop = 7; + packageInfoVBox.PackStart (packageIdHBox); + + var packageIdLabel = new Label (); packageIdLabel.Font = packageInfoBoldFont;
- packageIdLabel.Text = Catalog.GetString ("Id");
- packageIdHBox.PackStart (packageIdLabel);
-
- packageId = new Label ();
- packageId.Ellipsize = EllipsizeMode.End;
- packageId.TextAlignment = Alignment.End;
- packageId.Font = packageInfoSmallFont;
- packageIdLink = new LinkLabel ();
- packageIdLink.Ellipsize = EllipsizeMode.End;
- packageIdLink.TextAlignment = Alignment.End;
- packageIdLink.Font = packageInfoSmallFont;
- packageIdHBox.PackEnd (packageIdLink, true);
- packageIdHBox.PackEnd (packageId, true);
-
- // Package author
- var packageAuthorHBox = new HBox ();
- packageInfoVBox.PackStart (packageAuthorHBox);
-
- var packageAuthorLabel = new Label ();
- packageAuthorLabel.Text = Catalog.GetString ("Author");
- packageAuthorLabel.Font = packageInfoBoldFont;
- packageAuthorHBox.PackStart (packageAuthorLabel);
-
- packageAuthor = new Label ();
- packageAuthor.TextAlignment = Alignment.End;
- packageAuthor.Ellipsize = EllipsizeMode.End;
- packageAuthor.Font = packageInfoSmallFont;
- packageAuthorHBox.PackEnd (packageAuthor, true);
-
- // Package published
- var packagePublishedHBox = new HBox ();
- packageInfoVBox.PackStart (packagePublishedHBox);
-
- var packagePublishedLabel = new Label ();
- packagePublishedLabel.Text = Catalog.GetString ("Published");
- packagePublishedLabel.Font = packageInfoBoldFont;
- packagePublishedHBox.PackStart (packagePublishedLabel);
-
- packagePublishedDate = new Label ();
- packagePublishedDate.Font = packageInfoSmallFont;
- packagePublishedHBox.PackEnd (packagePublishedDate);
-
- // Package downloads
- var packageDownloadsHBox = new HBox ();
- packageInfoVBox.PackStart (packageDownloadsHBox);
-
- var packageDownloadsLabel = new Label ();
- packageDownloadsLabel.Text = Catalog.GetString ("Downloads");
- packageDownloadsLabel.Font = packageInfoBoldFont;
- packageDownloadsHBox.PackStart (packageDownloadsLabel);
-
- packageDownloads = new Label ();
- packageDownloads.Font = packageInfoSmallFont;
- packageDownloadsHBox.PackEnd (packageDownloads);
-
- // Package license.
- var packageLicenseHBox = new HBox ();
- packageInfoVBox.PackStart (packageLicenseHBox);
-
- var packageLicenseLabel = new Label ();
- packageLicenseLabel.Text = Catalog.GetString ("License");
+ packageIdLabel.Text = GettextCatalog.GetString ("Id"); + packageIdHBox.PackStart (packageIdLabel); + + packageId = new Label (); + packageId.Ellipsize = EllipsizeMode.End; + packageId.TextAlignment = Alignment.End; + packageId.Font = packageInfoSmallFont; + packageIdLink = new LinkLabel (); + packageIdLink.Ellipsize = EllipsizeMode.End; + packageIdLink.TextAlignment = Alignment.End; + packageIdLink.Font = packageInfoSmallFont; + packageIdHBox.PackEnd (packageIdLink, true); + packageIdHBox.PackEnd (packageId, true); + + // Package author + var packageAuthorHBox = new HBox (); + packageInfoVBox.PackStart (packageAuthorHBox); + + var packageAuthorLabel = new Label (); + packageAuthorLabel.Text = GettextCatalog.GetString ("Author"); + packageAuthorLabel.Font = packageInfoBoldFont; + packageAuthorHBox.PackStart (packageAuthorLabel); + + packageAuthor = new Label (); + packageAuthor.TextAlignment = Alignment.End; + packageAuthor.Ellipsize = EllipsizeMode.End; + packageAuthor.Font = packageInfoSmallFont; + packageAuthorHBox.PackEnd (packageAuthor, true); + + // Package published + var packagePublishedHBox = new HBox (); + packageInfoVBox.PackStart (packagePublishedHBox); + + var packagePublishedLabel = new Label (); + packagePublishedLabel.Text = GettextCatalog.GetString ("Published"); + packagePublishedLabel.Font = packageInfoBoldFont; + packagePublishedHBox.PackStart (packagePublishedLabel); + + packagePublishedDate = new Label (); + packagePublishedDate.Font = packageInfoSmallFont; + packagePublishedHBox.PackEnd (packagePublishedDate); + + // Package downloads + var packageDownloadsHBox = new HBox (); + packageInfoVBox.PackStart (packageDownloadsHBox); + + var packageDownloadsLabel = new Label (); + packageDownloadsLabel.Text = GettextCatalog.GetString ("Downloads"); + packageDownloadsLabel.Font = packageInfoBoldFont; + packageDownloadsHBox.PackStart (packageDownloadsLabel); + + packageDownloads = new Label (); + packageDownloads.Font = packageInfoSmallFont; + packageDownloadsHBox.PackEnd (packageDownloads); + + // Package license. + var packageLicenseHBox = new HBox (); + packageInfoVBox.PackStart (packageLicenseHBox); + + var packageLicenseLabel = new Label (); + packageLicenseLabel.Text = GettextCatalog.GetString ("License");
packageLicenseLabel.Font = packageInfoBoldFont;
- packageLicenseHBox.PackStart (packageLicenseLabel);
-
- packageLicenseLink = new LinkLabel ();
- packageLicenseLink.Text = Catalog.GetString ("View License");
- packageLicenseLink.Font = packageInfoSmallFont;
- packageLicenseHBox.PackEnd (packageLicenseLink);
-
- // Package project page.
- var packageProjectPageHBox = new HBox ();
- packageInfoVBox.PackStart (packageProjectPageHBox);
-
- var packageProjectPageLabel = new Label ();
- packageProjectPageLabel.Text = Catalog.GetString ("Project Page");
- packageProjectPageLabel.Font = packageInfoBoldFont;
- packageProjectPageHBox.PackStart (packageProjectPageLabel);
-
- packageProjectPageLink = new LinkLabel ();
- packageProjectPageLink.Text = Catalog.GetString ("Visit Page");
- packageProjectPageLink.Font = packageInfoSmallFont;
- packageProjectPageHBox.PackEnd (packageProjectPageLink);
-
- // Package dependencies
- packageDependenciesHBox = new HBox ();
- packageInfoVBox.PackStart (packageDependenciesHBox);
-
- var packageDependenciesLabel = new Label ();
- packageDependenciesLabel.Text = Catalog.GetString ("Dependencies");
- packageDependenciesLabel.Font = packageInfoBoldFont;
- packageDependenciesHBox.PackStart (packageDependenciesLabel);
-
- packageDependenciesNoneLabel = new Label ();
- packageDependenciesNoneLabel.Text = Catalog.GetString ("None");
- packageDependenciesNoneLabel.Font = packageInfoSmallFont;
- packageDependenciesHBox.PackEnd (packageDependenciesNoneLabel);
-
- // Package dependencies list.
- packageDependenciesListHBox = new HBox ();
- packageDependenciesListHBox.Visible = false;
- packageInfoVBox.PackStart (packageDependenciesListHBox);
-
- packageDependenciesList = new Label ();
- packageDependenciesList.Wrap = WrapMode.WordAndCharacter;
- packageDependenciesList.Margin = new WidgetSpacing (5);
- packageDependenciesList.Font = packageInfoSmallFont;
- packageDependenciesListHBox.PackStart (packageDependenciesList, true);
-
- // Package versions.
- packageVersionsHBox = new HBox ();
- packageVersionsHBox.Visible = false;
- packageVersionsHBox.BackgroundColor = Styles.PackageInfoBackgroundColor;
- packageVersionsHBox.Margin = new WidgetSpacing (15, 0, 15, 12);
- var packageVersionsLabel = new Label ();
- packageVersionsLabel.Font = packageInfoBoldFont;
- packageVersionsLabel.Text = Catalog.GetString ("Version");
- packageVersionsHBox.PackStart (packageVersionsLabel);
-
- packageVersionComboBox = new ComboBox ();
- packageVersionComboBox.Name = "packageVersionComboBox";
- packageVersionsHBox.Spacing = 15;
- packageVersionsHBox.PackStart (packageVersionComboBox, true, true);
-
- var packageInfoAndVersionsVBox = new VBox ();
- packageInfoAndVersionsVBox.Margin = new WidgetSpacing ();
- packageInfoAndVersionsVBox.BackgroundColor = Styles.PackageInfoBackgroundColor;
- packageInfoAndVersionsVBox.PackStart (packageInfoScrollViewFrame, true, true);
- packageInfoAndVersionsVBox.PackStart (packageVersionsHBox, false, false);
- middleHBox.PackEnd (packageInfoAndVersionsVBox);
-
- // Bottom part of dialog:
- // Show pre-release packages and Close/Add to Project buttons.
- var bottomHBox = new HBox ();
- bottomHBox.Margin = new WidgetSpacing (8, 5, 14, 10);
- bottomHBox.Spacing = 5;
- mainVBox.PackStart (bottomHBox);
-
- showPrereleaseCheckBox = new CheckBox ();
- showPrereleaseCheckBox.Name = "addPackagesDialogShowPreReleaseCheckBox";
- showPrereleaseCheckBox.Label = Catalog.GetString ("Show pre-release packages");
- bottomHBox.PackStart (showPrereleaseCheckBox);
-
- addPackagesButton = new Button ();
- addPackagesButton.Name = "addPackagesDialogAddPackageButton";
- addPackagesButton.MinWidth = 120;
- addPackagesButton.MinHeight = 25;
- addPackagesButton.Label = Catalog.GetString ("Add Package");
- bottomHBox.PackEnd (addPackagesButton);
-
- var closeButton = new Button ();
- closeButton.Name = "addPackagesDialogCloseButton";
- closeButton.MinWidth = 120;
- closeButton.MinHeight = 25;
- closeButton.Label = Catalog.GetString ("Close");
- closeButton.Clicked += (sender, e) => Close ();
- bottomHBox.PackEnd (closeButton);
-
- packageSearchEntry.SetFocus ();
- packageInfoVBox.Visible = false;
- }
- }
-}
-
+ packageLicenseHBox.PackStart (packageLicenseLabel); + + packageLicenseLink = new LinkLabel (); + packageLicenseLink.Text = GettextCatalog.GetString ("View License"); + packageLicenseLink.Font = packageInfoSmallFont; + packageLicenseHBox.PackEnd (packageLicenseLink); + + // Package project page. + var packageProjectPageHBox = new HBox (); + packageInfoVBox.PackStart (packageProjectPageHBox); + + var packageProjectPageLabel = new Label (); + packageProjectPageLabel.Text = GettextCatalog.GetString ("Project Page"); + packageProjectPageLabel.Font = packageInfoBoldFont; + packageProjectPageHBox.PackStart (packageProjectPageLabel); + + packageProjectPageLink = new LinkLabel (); + packageProjectPageLink.Text = GettextCatalog.GetString ("Visit Page"); + packageProjectPageLink.Font = packageInfoSmallFont; + packageProjectPageHBox.PackEnd (packageProjectPageLink); + + // Package dependencies + packageDependenciesHBox = new HBox (); + packageInfoVBox.PackStart (packageDependenciesHBox); + + var packageDependenciesLabel = new Label (); + packageDependenciesLabel.Text = GettextCatalog.GetString ("Dependencies"); + packageDependenciesLabel.Font = packageInfoBoldFont; + packageDependenciesHBox.PackStart (packageDependenciesLabel); + + packageDependenciesNoneLabel = new Label (); + packageDependenciesNoneLabel.Text = GettextCatalog.GetString ("None"); + packageDependenciesNoneLabel.Font = packageInfoSmallFont; + packageDependenciesHBox.PackEnd (packageDependenciesNoneLabel); + + // Package dependencies list. + packageDependenciesListHBox = new HBox (); + packageDependenciesListHBox.Visible = false; + packageInfoVBox.PackStart (packageDependenciesListHBox); + + packageDependenciesList = new Label (); + packageDependenciesList.Wrap = WrapMode.WordAndCharacter; + packageDependenciesList.Margin = new WidgetSpacing (5); + packageDependenciesList.Font = packageInfoSmallFont; + packageDependenciesListHBox.PackStart (packageDependenciesList, true); + + // Current package version. + currentPackageVersionHBox = new HBox (); + currentPackageVersionHBox.Spacing = 15; + currentPackageVersionHBox.Visible = false; + currentPackageVersionHBox.BackgroundColor = Styles.PackageInfoBackgroundColor; + currentPackageVersionHBox.Margin = new WidgetSpacing (15, 0, 15, 0); + currentPackageVersionLabel = new Label (); + currentPackageVersionLabel.BoundsChanged += PackageVersionLabelBoundsChanged; + currentPackageVersionLabel.Font = packageInfoSmallFont; + currentPackageVersionLabel.Text = GettextCatalog.GetString ("Current Version:"); + currentPackageVersionLabel.TextAlignment = Alignment.End; + currentPackageVersionHBox.PackStart (currentPackageVersionLabel); + + var currentPackageVersionWithInfoPopoverHBox = new HBox (); + currentPackageVersionWithInfoPopoverHBox.Margin = new WidgetSpacing (); + currentPackageVersionWithInfoPopoverHBox.Spacing = 0; + + currentPackageVersion = new Label (); + currentPackageVersion.Font = packageInfoSmallFont; + currentPackageVersionWithInfoPopoverHBox.PackStart (currentPackageVersion); + + currentPackageVersionInfoPopoverWidget = new InformationPopoverWidget (); + currentPackageVersionInfoPopoverWidget.Severity = Ide.Tasks.TaskSeverity.Information; + currentPackageVersionInfoPopoverWidget.Margin = new WidgetSpacing (5, 0, 0, 2); + currentPackageVersionWithInfoPopoverHBox.PackStart (currentPackageVersionInfoPopoverWidget); + + currentPackageVersionHBox.PackStart (currentPackageVersionWithInfoPopoverHBox); + + // Package versions. + packageVersionsHBox = new HBox (); + packageVersionsHBox.Visible = false; + packageVersionsHBox.BackgroundColor = Styles.PackageInfoBackgroundColor; + packageVersionsHBox.Margin = new WidgetSpacing (15, 0, 15, 12); + packageVersionsLabel = new Label (); + packageVersionsLabel.Font = packageInfoSmallFont; + packageVersionsLabel.Text = GettextCatalog.GetString ("New Version:"); + packageVersionsLabel.TextAlignment = Alignment.End; + packageVersionsHBox.PackStart (packageVersionsLabel); + + packageVersionComboBox = new ComboBox (); + packageVersionComboBox.Name = "packageVersionComboBox"; + packageVersionsHBox.Spacing = 15; + packageVersionsHBox.PackStart (packageVersionComboBox, true, true); + + var packageInfoAndVersionsVBox = new VBox (); + packageInfoAndVersionsVBox.Margin = new WidgetSpacing (); + packageInfoAndVersionsVBox.BackgroundColor = Styles.PackageInfoBackgroundColor; + packageInfoAndVersionsVBox.PackStart (packageInfoScrollViewFrame, true, true); + packageInfoAndVersionsVBox.PackStart (currentPackageVersionHBox, false, false); + packageInfoAndVersionsVBox.PackStart (packageVersionsHBox, false, false); + middleHBox.PackEnd (packageInfoAndVersionsVBox); + + // Bottom part of dialog: + // Show pre-release packages and Close/Add to Project buttons. + var bottomHBox = new HBox (); + bottomHBox.Margin = new WidgetSpacing (8, 5, 14, 10); + bottomHBox.Spacing = 5; + mainVBox.PackStart (bottomHBox); + + showPrereleaseCheckBox = new CheckBox (); + showPrereleaseCheckBox.Name = "managePackagesDialogShowPreReleaseCheckBox"; + showPrereleaseCheckBox.Label = GettextCatalog.GetString ("Show pre-release packages"); + bottomHBox.PackStart (showPrereleaseCheckBox); + + addPackagesButton = new Button (); + addPackagesButton.Name = "managePackagesDialogAddPackageButton"; + addPackagesButton.MinWidth = 120; + addPackagesButton.MinHeight = 25; + addPackagesButton.Label = GettextCatalog.GetString ("Add Package"); + bottomHBox.PackEnd (addPackagesButton); + + closeButton = new Button (); + closeButton.Name = "managePackagesDialogCloseButton"; + closeButton.MinWidth = 120; + closeButton.MinHeight = 25; + closeButton.Label = GettextCatalog.GetString ("Close"); + bottomHBox.PackEnd (closeButton); + + packageSearchEntry.SetFocus (); + packageInfoVBox.Visible = false; + } + + double? maxPackageVersionLabelWidth; + + void PackageVersionLabelBoundsChanged (object sender, EventArgs e) + { + if (!viewModel.IsUpdatesPageSelected) + return; + + double currentPackageVersionLabelWidth = currentPackageVersionLabel.Size.Width; + double packageVersionsLabelWidth = packageVersionsLabel.Size.Width; + + if (currentPackageVersionLabelWidth > packageVersionsLabelWidth) { + packageVersionsLabel.WidthRequest = currentPackageVersionLabelWidth; + maxPackageVersionLabelWidth = currentPackageVersionLabelWidth; + } else if (packageVersionsLabelWidth > currentPackageVersionLabelWidth) { + currentPackageVersionLabel.WidthRequest = packageVersionsLabelWidth; + maxPackageVersionLabelWidth = packageVersionsLabelWidth; + } + } + } +} + diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/AddPackagesDialog.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesDialog.cs index d6e867e140..2cf875404d 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/AddPackagesDialog.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesDialog.cs @@ -1,822 +1,1200 @@ -//
-// AddPackagesDialog.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2014 Xamarin Inc. (http://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 System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using Mono.Unix;
-using MonoDevelop.Core;
-using MonoDevelop.Ide;
-using MonoDevelop.Projects;
-using NuGet.Versioning;
-using Xwt;
-using Xwt.Drawing;
-using PropertyChangedEventArgs = System.ComponentModel.PropertyChangedEventArgs;
-
-namespace MonoDevelop.PackageManagement
-{
- internal partial class AddPackagesDialog
- {
- IBackgroundPackageActionRunner backgroundActionRunner;
- AllPackagesViewModel viewModel;
- List<SourceRepositoryViewModel> packageSources;
- DataField<bool> packageHasBackgroundColorField = new DataField<bool> ();
- DataField<PackageSearchResultViewModel> packageViewModelField = new DataField<PackageSearchResultViewModel> ();
- DataField<Image> packageImageField = new DataField<Image> ();
- DataField<double> packageCheckBoxAlphaField = new DataField<double> ();
- const double packageCheckBoxSemiTransarentAlpha = 0.6;
- ListStore packageStore;
- PackageCellView packageCellView;
- TimeSpan searchDelayTimeSpan = TimeSpan.FromMilliseconds (500);
- IDisposable searchTimer;
- SourceRepositoryViewModel dummyPackageSourceRepresentingConfigureSettingsItem =
- new SourceRepositoryViewModel (Catalog.GetString ("Configure Sources..."));
- ImageLoader imageLoader = new ImageLoader ();
- bool loadingMessageVisible;
- bool ignorePackageVersionChanges;
- const string IncludePrereleaseUserPreferenceName = "NuGet.AddPackagesDialog.IncludePrerelease";
- TimeSpan populatePackageVersionsDelayTimeSpan = TimeSpan.FromMilliseconds (500);
- int packageVersionsAddedCount;
- IDisposable populatePackageVersionsTimer;
- const int MaxVersionsToPopulate = 100;
-
- public AddPackagesDialog (AllPackagesViewModel viewModel, string initialSearch = null)
- : this (
- viewModel,
- initialSearch,
- PackageManagementServices.BackgroundPackageActionRunner)
- {
- }
-
- public AddPackagesDialog (
- AllPackagesViewModel viewModel,
- string initialSearch,
- IBackgroundPackageActionRunner backgroundActionRunner)
- {
- this.viewModel = viewModel;
- this.backgroundActionRunner = backgroundActionRunner;
-
- Build ();
-
- UpdatePackageSearchEntryWithInitialText (initialSearch);
-
- InitializeListView ();
- UpdateAddPackagesButton ();
- ShowLoadingMessage ();
- LoadViewModel (initialSearch);
-
- this.showPrereleaseCheckBox.Clicked += ShowPrereleaseCheckBoxClicked;
- this.packageSourceComboBox.SelectionChanged += PackageSourceChanged;
- this.addPackagesButton.Clicked += AddPackagesButtonClicked;
- this.packageSearchEntry.Changed += PackageSearchEntryChanged;
- this.packageSearchEntry.Activated += PackageSearchEntryActivated;
- this.packageVersionComboBox.SelectionChanged += PackageVersionChanged;
- imageLoader.Loaded += ImageLoaded;
- }
-
- public bool ShowPreferencesForPackageSources { get; private set; }
-
- protected override void Dispose (bool disposing)
- {
- imageLoader.Loaded -= ImageLoaded;
- imageLoader.Dispose ();
-
- RemoveSelectedPackagePropertyChangedEventHandler ();
- viewModel.PropertyChanged -= ViewModelPropertyChanged;
- viewModel.Dispose ();
- DisposeExistingTimer ();
- DisposePopulatePackageVersionsTimer ();
- packageStore.Clear ();
- viewModel = null;
- base.Dispose (disposing);
- }
-
- void UpdatePackageSearchEntryWithInitialText (string initialSearch)
- {
- packageSearchEntry.Text = initialSearch;
- if (!String.IsNullOrEmpty (initialSearch)) {
- packageSearchEntry.CursorPosition = initialSearch.Length;
- }
- }
-
- public string SearchText {
- get { return packageSearchEntry.Text; }
- }
-
- void InitializeListView ()
- {
- packageStore = new ListStore (packageHasBackgroundColorField, packageCheckBoxAlphaField, packageImageField, packageViewModelField);
- packagesListView.DataSource = packageStore;
-
- AddPackageCellViewToListView ();
-
- packagesListView.SelectionChanged += PackagesListViewSelectionChanged;
- packagesListView.RowActivated += PackagesListRowActivated;
- packagesListView.VerticalScrollControl.ValueChanged += PackagesListViewScrollValueChanged;
- }
-
- void AddPackageCellViewToListView ()
- {
- packageCellView = new PackageCellView {
- PackageField = packageViewModelField,
- HasBackgroundColorField = packageHasBackgroundColorField,
- CheckBoxAlphaField = packageCheckBoxAlphaField,
- ImageField = packageImageField,
- CellWidth = 535
- };
- var textColumn = new ListViewColumn ("Package", packageCellView);
- packagesListView.Columns.Add (textColumn);
-
- packageCellView.PackageChecked += PackageCellViewPackageChecked;
- }
-
- void ShowLoadingMessage ()
- {
- UpdateSpinnerLabel ();
- noPackagesFoundFrame.Visible = false;
- packagesListView.Visible = false;
- loadingSpinnerFrame.Visible = true;
- loadingMessageVisible = true;
- }
-
- void HideLoadingMessage ()
- {
- loadingSpinnerFrame.Visible = false;
- packagesListView.Visible = true;
- noPackagesFoundFrame.Visible = false;
- loadingMessageVisible = false;
- }
-
- void UpdateSpinnerLabel ()
- {
- if (String.IsNullOrWhiteSpace (packageSearchEntry.Text)) {
- loadingSpinnerLabel.Text = Catalog.GetString ("Loading package list...");
- } else {
- loadingSpinnerLabel.Text = Catalog.GetString ("Searching packages...");
- }
- }
-
- void ShowNoPackagesFoundMessage ()
- {
- if (!String.IsNullOrWhiteSpace (packageSearchEntry.Text)) {
- packagesListView.Visible = false;
- noPackagesFoundFrame.Visible = true;
- }
- }
-
- void ShowPrereleaseCheckBoxClicked (object sender, EventArgs e)
- {
- viewModel.IncludePrerelease = !viewModel.IncludePrerelease;
-
- SaveIncludePrereleaseUserPreference ();
- }
-
- void SaveIncludePrereleaseUserPreference ()
- {
- Solution solution = IdeApp.ProjectOperations.CurrentSelectedSolution;
- if (solution != null) {
- if (viewModel.IncludePrerelease) {
- solution.UserProperties.SetValue (IncludePrereleaseUserPreferenceName, viewModel.IncludePrerelease);
- } else {
- solution.UserProperties.RemoveValue (IncludePrereleaseUserPreferenceName);
- }
- solution.SaveUserProperties ();
- }
- }
-
- bool GetIncludePrereleaseUserPreference ()
- {
- Solution solution = IdeApp.ProjectOperations.CurrentSelectedSolution;
- if (solution != null) {
- return solution.UserProperties.GetValue (IncludePrereleaseUserPreferenceName, false);
- }
-
- return false;
- }
-
- void LoadViewModel (string initialSearch)
- {
- viewModel.SearchTerms = initialSearch;
-
- viewModel.IncludePrerelease = GetIncludePrereleaseUserPreference ();
- showPrereleaseCheckBox.Active = viewModel.IncludePrerelease;
-
- ClearSelectedPackageInformation ();
- PopulatePackageSources ();
- viewModel.PropertyChanged += ViewModelPropertyChanged;
-
- if (viewModel.SelectedPackageSource != null) {
- viewModel.ReadPackages ();
- } else {
- HideLoadingMessage ();
- }
- }
-
- void ClearSelectedPackageInformation ()
- {
- this.packageInfoVBox.Visible = false;
- this.packageVersionsHBox.Visible = false;
- }
-
- void RemoveSelectedPackagePropertyChangedEventHandler ()
- {
- if (viewModel.SelectedPackage != null) {
- viewModel.SelectedPackage.PropertyChanged -= SelectedPackageViewModelChanged;
- viewModel.SelectedPackage = null;
- }
- }
-
- List<SourceRepositoryViewModel> PackageSources {
- get {
- if (packageSources == null) {
- packageSources = viewModel.PackageSources.ToList ();
- }
- return packageSources;
- }
- }
-
- void PopulatePackageSources ()
- {
- foreach (SourceRepositoryViewModel packageSource in PackageSources) {
- AddPackageSourceToComboBox (packageSource);
- }
-
- AddPackageSourceToComboBox (dummyPackageSourceRepresentingConfigureSettingsItem);
-
- packageSourceComboBox.SelectedItem = viewModel.SelectedPackageSource;
- }
-
- void AddPackageSourceToComboBox (SourceRepositoryViewModel packageSource)
- {
- packageSourceComboBox.Items.Add (packageSource, packageSource.Name);
- }
-
- void PackageSourceChanged (object sender, EventArgs e)
- {
- var selectedPackageSource = (SourceRepositoryViewModel)packageSourceComboBox.SelectedItem;
- if (selectedPackageSource == dummyPackageSourceRepresentingConfigureSettingsItem) {
- ShowPreferencesForPackageSources = true;
- Close ();
- } else {
- viewModel.SelectedPackageSource = selectedPackageSource;
- }
- }
-
- void PackagesListViewSelectionChanged (object sender, EventArgs e)
- {
- try {
- ShowSelectedPackage ();
- } catch (Exception ex) {
- LoggingService.LogError ("Error showing selected package.", ex);
- ShowErrorMessage (ex.Message);
- }
- }
-
- void ShowSelectedPackage ()
- {
- RemoveSelectedPackagePropertyChangedEventHandler ();
-
- PackageSearchResultViewModel packageViewModel = GetSelectedPackageViewModel ();
- if (packageViewModel != null) {
- ShowPackageInformation (packageViewModel);
- } else {
- ClearSelectedPackageInformation ();
- }
- viewModel.SelectedPackage = packageViewModel;
- UpdateAddPackagesButton ();
- }
-
- PackageSearchResultViewModel GetSelectedPackageViewModel ()
- {
- if (packagesListView.SelectedRow != -1) {
- return packageStore.GetValue (packagesListView.SelectedRow, packageViewModelField);
- }
- return null;
- }
-
- void ShowPackageInformation (PackageSearchResultViewModel packageViewModel)
- {
- // Use the package id and not the package title to prevent a pango crash if the title
- // contains Chinese characters.
- this.packageNameLabel.Markup = packageViewModel.GetIdMarkup ();
- this.packageAuthor.Text = packageViewModel.Author;
- this.packagePublishedDate.Text = packageViewModel.GetLastPublishedDisplayText ();
- this.packageDownloads.Text = packageViewModel.GetDownloadCountDisplayText ();
- this.packageDescription.Text = packageViewModel.Description;
- this.packageId.Text = packageViewModel.Id;
- this.packageId.Visible = packageViewModel.HasNoGalleryUrl;
- ShowUri (this.packageIdLink, packageViewModel.GalleryUrl, packageViewModel.Id);
- ShowUri (this.packageProjectPageLink, packageViewModel.ProjectUrl);
- ShowUri (this.packageLicenseLink, packageViewModel.LicenseUrl);
-
- PopulatePackageDependencies (packageViewModel);
-
- PopulatePackageVersions (packageViewModel);
-
- this.packageInfoVBox.Visible = true;
- this.packageVersionsHBox.Visible = true;
-
- packageViewModel.PropertyChanged += SelectedPackageViewModelChanged;
- viewModel.LoadPackageMetadata (packageViewModel);
- }
- - void ShowUri (LinkLabel linkLabel, Uri uri, string label)
- {
- linkLabel.Text = label;
- ShowUri (linkLabel, uri);
- }
-
- void ShowUri (LinkLabel linkLabel, Uri uri)
- {
- if (uri == null) {
- linkLabel.Visible = false;
- } else {
- linkLabel.Visible = true;
- linkLabel.Uri = uri;
- }
- }
-
- void ViewModelPropertyChanged (object sender, PropertyChangedEventArgs e)
- {
- try {
- ShowPackages ();
- } catch (Exception ex) {
- LoggingService.LogError ("Error showing packages.", ex);
- ShowErrorMessage (ex.Message);
- }
- }
-
- void ShowPackages ()
- {
- if (viewModel.HasError) {
- ShowErrorMessage (viewModel.ErrorMessage);
- } else {
- ClearErrorMessage ();
- }
-
- if (viewModel.IsLoadingNextPage) {
- // Show spinner?
- } else if (viewModel.IsReadingPackages) {
- ClearPackages ();
- } else {
- HideLoadingMessage ();
- }
-
- if (!viewModel.IsLoadingNextPage) {
- AppendPackagesToListView ();
- }
-
- UpdateAddPackagesButton ();
- }
-
- void ClearPackages ()
- {
- packageStore.Clear ();
- ResetPackagesListViewScroll ();
- UpdatePackageListViewSelectionColor ();
- ShowLoadingMessage ();
- ShrinkImageCache ();
- DisposePopulatePackageVersionsTimer ();
- }
-
- void ResetPackagesListViewScroll ()
- {
- packagesListView.VerticalScrollControl.Value = 0;
- }
-
- void ShowErrorMessage (string message)
- {
- errorMessageLabel.Text = message;
- errorMessageHBox.Visible = true;
- }
-
- void ClearErrorMessage ()
- {
- errorMessageHBox.Visible = false;
- errorMessageLabel.Text = "";
- }
-
- void ShrinkImageCache ()
- {
- imageLoader.ShrinkImageCache ();
- }
-
- void AppendPackagesToListView ()
- {
- bool packagesListViewWasEmpty = (packageStore.RowCount == 0);
-
- for (int row = packageStore.RowCount; row < viewModel.PackageViewModels.Count; ++row) {
- PackageSearchResultViewModel packageViewModel = viewModel.PackageViewModels [row];
- AppendPackageToListView (packageViewModel);
- LoadPackageImage (row, packageViewModel);
- }
-
- if (packagesListViewWasEmpty && (packageStore.RowCount > 0)) {
- packagesListView.SelectRow (0);
- }
-
- if (!viewModel.IsReadingPackages && (packageStore.RowCount == 0)) {
- ShowNoPackagesFoundMessage ();
- }
- }
-
- void AppendPackageToListView (PackageSearchResultViewModel packageViewModel)
- {
- int row = packageStore.AddRow ();
- packageStore.SetValue (row, packageHasBackgroundColorField, IsOddRow (row));
- packageStore.SetValue (row, packageCheckBoxAlphaField, GetPackageCheckBoxAlpha ());
- packageStore.SetValue (row, packageViewModelField, packageViewModel);
- }
-
- void LoadPackageImage (int row, PackageSearchResultViewModel packageViewModel)
- {
- if (packageViewModel.HasIconUrl) {
- imageLoader.LoadFrom (packageViewModel.IconUrl, row);
- }
- }
-
- bool IsOddRow (int row)
- {
- return (row % 2) == 0;
- }
-
- double GetPackageCheckBoxAlpha ()
- {
- if (PackagesCheckedCount == 0) {
- return packageCheckBoxSemiTransarentAlpha;
- }
- return 1;
- }
-
- void ImageLoaded (object sender, ImageLoadedEventArgs e)
- {
- if (!e.HasError) {
- int row = (int)e.State;
- if (IsValidRowAndUrl (row, e.Uri)) {
- packageStore.SetValue (row, packageImageField, e.Image);
- }
- }
- }
-
- bool IsValidRowAndUrl (int row, Uri uri)
- {
- if (row < packageStore.RowCount) {
- PackageSearchResultViewModel packageViewModel = packageStore.GetValue (row, packageViewModelField);
- if (packageViewModel != null) {
- return uri == packageViewModel.IconUrl;
- }
- }
- return false;
- }
-
- void AddPackagesButtonClicked (object sender, EventArgs e)
- {
- try {
- List<IPackageAction> packageActions = CreateInstallPackageActionsForSelectedPackages ();
- InstallPackages (packageActions);
- } catch (Exception ex) {
- LoggingService.LogError ("Adding packages failed.", ex);
- ShowErrorMessage (ex.Message);
- }
- }
-
- void InstallPackages (List<IPackageAction> packageActions)
- {
- if (packageActions.Count > 0) {
- ProgressMonitorStatusMessage progressMessage = GetProgressMonitorStatusMessages (packageActions);
- backgroundActionRunner.Run (progressMessage, packageActions);
-
- viewModel.OnInstallingSelectedPackages ();
- Close ();
- }
- }
-
- List<IPackageAction> CreateInstallPackageActionsForSelectedPackages ()
- {
- List<PackageSearchResultViewModel> packageViewModels = GetSelectedPackageViewModels ();
- if (packageViewModels.Count > 0) {
- return CreateInstallPackageActions (packageViewModels);
- }
- return new List<IPackageAction> ();
- }
-
- ProgressMonitorStatusMessage GetProgressMonitorStatusMessages (List<IPackageAction> packageActions)
- {
- if (packageActions.Count == 1) {
- string packageId = packageActions.OfType<INuGetPackageAction> ().First ().PackageId;
- if (OlderPackageInstalledThanPackageSelected ()) {
- return ProgressMonitorStatusMessageFactory.CreateUpdatingSinglePackageMessage (packageId);
- }
- return ProgressMonitorStatusMessageFactory.CreateInstallingSinglePackageMessage (packageId);
- }
- return ProgressMonitorStatusMessageFactory.CreateInstallingMultiplePackagesMessage (packageActions.Count);
- }
-
- List<PackageSearchResultViewModel> GetSelectedPackageViewModels ()
- {
- List<PackageSearchResultViewModel> packageViewModels = viewModel.CheckedPackageViewModels.ToList ();
- if (packageViewModels.Count > 0) {
- return packageViewModels;
- }
-
- PackageSearchResultViewModel selectedPackageViewModel = GetSelectedPackageViewModel ();
- if (selectedPackageViewModel != null) {
- packageViewModels.Add (selectedPackageViewModel);
- }
- return packageViewModels;
- }
-
- List<IPackageAction> CreateInstallPackageActions (IEnumerable<PackageSearchResultViewModel> packageViewModels)
- {
- return packageViewModels.Select (packageViewModel => viewModel.CreateInstallPackageAction (packageViewModel)).ToList ();
- }
-
- void PackageSearchEntryChanged (object sender, EventArgs e)
- {
- ClearErrorMessage ();
- ClearPackages ();
- UpdateAddPackagesButton ();
- SearchAfterDelay ();
- }
-
- void SearchAfterDelay ()
- {
- DisposeExistingTimer ();
- searchTimer = Application.TimeoutInvoke (searchDelayTimeSpan, Search);
- }
-
- void DisposeExistingTimer ()
- {
- if (searchTimer != null) {
- searchTimer.Dispose ();
- }
- }
-
- bool Search ()
- {
- viewModel.SearchTerms = this.packageSearchEntry.Text;
- viewModel.Search ();
-
- return false;
- }
-
- void PackagesListRowActivated (object sender, ListViewRowEventArgs e)
- {
- if (PackagesCheckedCount > 0) {
- AddPackagesButtonClicked (sender, e);
- } else {
- PackageSearchResultViewModel packageViewModel = packageStore.GetValue (e.RowIndex, packageViewModelField);
- InstallPackage (packageViewModel);
- }
- }
-
- void InstallPackage (PackageSearchResultViewModel packageViewModel)
- {
- try {
- if (packageViewModel != null) {
- List<IPackageAction> packageActions = CreateInstallPackageActions (
- new PackageSearchResultViewModel [] { packageViewModel });
- InstallPackages (packageActions);
- }
- } catch (Exception ex) {
- LoggingService.LogError ("Installing package failed.", ex);
- ShowErrorMessage (ex.Message);
- }
- }
-
- void PackageSearchEntryActivated (object sender, EventArgs e)
- {
- if (loadingMessageVisible)
- return;
-
- if (PackagesCheckedCount > 0) {
- AddPackagesButtonClicked (sender, e);
- } else {
- PackageSearchResultViewModel selectedPackageViewModel = GetSelectedPackageViewModel ();
- InstallPackage (selectedPackageViewModel);
- }
- }
-
- void PackagesListViewScrollValueChanged (object sender, EventArgs e)
- {
- if (viewModel.IsLoadingNextPage) {
- return;
- }
-
- if (IsScrollBarNearEnd (packagesListView.VerticalScrollControl)) {
- if (viewModel.HasNextPage) {
- viewModel.ShowNextPage ();
- }
- }
- }
-
- bool IsScrollBarNearEnd (ScrollControl scrollControl)
- {
- double currentValue = scrollControl.Value;
- double maxValue = scrollControl.UpperValue;
- double pageSize = scrollControl.PageSize;
-
- return (currentValue / (maxValue - pageSize)) > 0.7;
- }
-
- void PackageCellViewPackageChecked (object sender, PackageCellViewEventArgs e)
- {
- UpdateAddPackagesButton ();
- UpdatePackageListViewSelectionColor ();
- UpdatePackageListViewCheckBoxAlpha ();
- }
-
- void UpdateAddPackagesButton ()
- {
- string label = Catalog.GetPluralString ("Add Package", "Add Packages", GetPackagesCountForAddPackagesButtonLabel ());
- if (PackagesCheckedCount <= 1 && OlderPackageInstalledThanPackageSelected ()) {
- label = Catalog.GetString ("Update Package");
- }
- addPackagesButton.Label = label;
- addPackagesButton.Sensitive = IsAddPackagesButtonEnabled ();
- }
-
- int GetPackagesCountForAddPackagesButtonLabel ()
- {
- if (PackagesCheckedCount > 1)
- return PackagesCheckedCount;
-
- return 1;
- }
-
- void UpdatePackageListViewSelectionColor ()
- {
- packageCellView.UseStrongSelectionColor = (PackagesCheckedCount == 0);
- }
-
- void UpdatePackageListViewCheckBoxAlpha ()
- {
- if (PackagesCheckedCount > 1)
- return;
-
- double alpha = GetPackageCheckBoxAlpha ();
- for (int row = 0; row < packageStore.RowCount; ++row) {
- packageStore.SetValue (row, packageCheckBoxAlphaField, alpha);
- }
- }
-
- bool OlderPackageInstalledThanPackageSelected ()
- {
- if (PackagesCheckedCount != 0) {
- return false;
- }
-
- PackageSearchResultViewModel selectedPackageViewModel = GetSelectedPackageViewModel ();
- if (selectedPackageViewModel != null) {
- return selectedPackageViewModel.IsOlderPackageInstalled ();
- }
- return false;
- }
-
- bool IsAddPackagesButtonEnabled ()
- {
- return !loadingMessageVisible && IsAtLeastOnePackageSelected ();
- }
-
- bool IsAtLeastOnePackageSelected ()
- {
- return (PackagesCheckedCount) >= 1 || (packagesListView.SelectedRow != -1);
- }
-
- int PackagesCheckedCount {
- get { return viewModel.CheckedPackageViewModels.Count; }
- }
-
- void SelectedPackageViewModelChanged (object sender, PropertyChangedEventArgs e)
- {
- try {
- if (e.PropertyName == "Versions") {
- PopulatePackageVersions (viewModel.SelectedPackage);
- } else {
- packagePublishedDate.Text = viewModel.SelectedPackage.GetLastPublishedDisplayText ();
- PopulatePackageDependencies (viewModel.SelectedPackage);
- }
- } catch (Exception ex) {
- LoggingService.LogError ("Error loading package versions.", ex);
- }
- }
-
- void PopulatePackageVersions (PackageSearchResultViewModel packageViewModel)
- {
- DisposePopulatePackageVersionsTimer ();
-
- ignorePackageVersionChanges = true;
- try {
- packageVersionComboBox.Items.Clear ();
- if (packageViewModel.Versions.Any ()) {
- int count = 0;
- foreach (NuGetVersion version in packageViewModel.Versions) {
- count++;
- if (count > MaxVersionsToPopulate) {
- packageVersionsAddedCount = count - 1;
- if (version >= packageViewModel.SelectedVersion) {
- AddPackageVersionToComboBox (packageViewModel.SelectedVersion);
- }
- PopulatePackageVersionsAfterDelay ();
- break;
- }
- AddPackageVersionToComboBox (version);
- }
- } else {
- AddPackageVersionToComboBox (packageViewModel.Version);
- }
- packageVersionComboBox.SelectedItem = packageViewModel.SelectedVersion;
- } finally {
- ignorePackageVersionChanges = false;
- }
- }
-
- void AddPackageVersionToComboBox (NuGetVersion version)
- {
- packageVersionComboBox.Items.Add (version, version.ToString ());
- }
-
- void PackageVersionChanged (object sender, EventArgs e)
- {
- if (ignorePackageVersionChanges || viewModel.SelectedPackage == null)
- return;
-
- viewModel.SelectedPackage.SelectedVersion = (NuGetVersion)packageVersionComboBox.SelectedItem;
- UpdateAddPackagesButton ();
- }
-
- void PopulatePackageDependencies (PackageSearchResultViewModel packageViewModel)
- {
- if (packageViewModel.IsDependencyInformationAvailable) {
- this.packageDependenciesHBox.Visible = true;
- this.packageDependenciesListHBox.Visible = packageViewModel.HasDependencies;
- this.packageDependenciesNoneLabel.Visible = !packageViewModel.HasDependencies;
- this.packageDependenciesList.Text = packageViewModel.GetPackageDependenciesDisplayText ();
- } else {
- this.packageDependenciesHBox.Visible = false;
- this.packageDependenciesListHBox.Visible = false;
- this.packageDependenciesNoneLabel.Visible = false;
- this.packageDependenciesList.Text = String.Empty;
- }
- }
-
- void PopulatePackageVersionsAfterDelay ()
- {
- populatePackageVersionsTimer = Application.TimeoutInvoke (populatePackageVersionsDelayTimeSpan, PopulateMorePackageVersions);
- }
-
- void DisposePopulatePackageVersionsTimer ()
- {
- if (populatePackageVersionsTimer != null) {
- populatePackageVersionsTimer.Dispose ();
- populatePackageVersionsTimer = null;
- }
- }
-
- bool PopulateMorePackageVersions ()
- {
- PackageSearchResultViewModel packageViewModel = viewModel?.SelectedPackage;
- if (populatePackageVersionsTimer == null || packageViewModel == null) {
- return false;
- }
-
- int count = 0;
- foreach (NuGetVersion version in packageViewModel.Versions.Skip (packageVersionsAddedCount)) {
- count++;
-
- if (count > MaxVersionsToPopulate) {
- packageVersionsAddedCount += count - 1;
- return true;
- }
-
- AddPackageVersionToComboBox (version);
- }
-
- return false;
- }
- }
+// +// ManagePackagesDialog.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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 System.Collections.Generic; +using System.Linq; +using MonoDevelop.Core; +using MonoDevelop.Ide; +using MonoDevelop.Projects; +using NuGet.Versioning; +using Xwt; +using Xwt.Drawing; +using PropertyChangedEventArgs = System.ComponentModel.PropertyChangedEventArgs; + +namespace MonoDevelop.PackageManagement +{ + internal partial class ManagePackagesDialog + { + IBackgroundPackageActionRunner backgroundActionRunner; + ManagePackagesViewModel viewModel; + List<SourceRepositoryViewModel> packageSources; + DataField<bool> packageHasBackgroundColorField = new DataField<bool> (); + DataField<ManagePackagesSearchResultViewModel> packageViewModelField = new DataField<ManagePackagesSearchResultViewModel> (); + DataField<Image> packageImageField = new DataField<Image> (); + DataField<double> packageCheckBoxAlphaField = new DataField<double> (); + const double packageCheckBoxSemiTransarentAlpha = 0.6; + ListStore packageStore; + ManagePackagesCellView packageCellView; + TimeSpan searchDelayTimeSpan = TimeSpan.FromMilliseconds (500); + IDisposable searchTimer; + SourceRepositoryViewModel dummyPackageSourceRepresentingConfigureSettingsItem = + new SourceRepositoryViewModel (GettextCatalog.GetString ("Configure Sources...")); + ImageLoader imageLoader = new ImageLoader (); + bool loadingMessageVisible; + bool ignorePackageVersionChanges; + const string IncludePrereleaseUserPreferenceName = "NuGet.AddPackagesDialog.IncludePrerelease"; + TimeSpan populatePackageVersionsDelayTimeSpan = TimeSpan.FromMilliseconds (500); + int packageVersionsAddedCount; + IDisposable populatePackageVersionsTimer; + const int MaxVersionsToPopulate = 100; + DataField<bool> projectCheckedField; + DataField<string> projectNameField; + DataField<string> packageVersionField; + DataField<ManageProjectViewModel> projectField; + CheckBoxCellView projectCheckBoxCellView; + ListStore projectStore; + + public ManagePackagesDialog (ManagePackagesViewModel viewModel, string initialSearch = null) + : this ( + viewModel, + initialSearch, + PackageManagementServices.BackgroundPackageActionRunner) + { + } + + public ManagePackagesDialog ( + ManagePackagesViewModel viewModel, + string initialSearch, + IBackgroundPackageActionRunner backgroundActionRunner) + { + this.viewModel = viewModel; + this.backgroundActionRunner = backgroundActionRunner; + + Build (); + + consolidateLabel.Visible = viewModel.IsManagingSolution; + UpdateDialogTitle (); + UpdatePackageSearchEntryWithInitialText (initialSearch); + UpdatePackageResultsPageLabels (); + + InitializeListView (); + UpdateAddPackagesButton (); + ShowLoadingMessage (); + LoadViewModel (initialSearch); + + closeButton.Clicked += CloseButtonClicked; + this.showPrereleaseCheckBox.Clicked += ShowPrereleaseCheckBoxClicked; + this.packageSourceComboBox.SelectionChanged += PackageSourceChanged; + this.addPackagesButton.Clicked += AddPackagesButtonClicked; + this.packageSearchEntry.Changed += PackageSearchEntryChanged; + this.packageSearchEntry.Activated += PackageSearchEntryActivated; + this.packageVersionComboBox.SelectionChanged += PackageVersionChanged; + imageLoader.Loaded += ImageLoaded; + + browseLabel.ButtonPressed += BrowseLabelButtonPressed; + installedLabel.ButtonPressed += InstalledLabelButtonPressed; + updatesLabel.ButtonPressed += UpdatesLabelButtonPressed; + consolidateLabel.ButtonPressed += ConsolidateLabelButtonPressed; + } + + public bool ShowPreferencesForPackageSources { get; private set; } + + protected override void Dispose (bool disposing) + { + closeButton.Clicked -= CloseButtonClicked; + currentPackageVersionLabel.BoundsChanged -= PackageVersionLabelBoundsChanged; + + imageLoader.Loaded -= ImageLoaded; + imageLoader.Dispose (); + + RemoveSelectedPackagePropertyChangedEventHandler (); + viewModel.PropertyChanged -= ViewModelPropertyChanged; + viewModel.Dispose (); + DisposeExistingTimer (); + DisposePopulatePackageVersionsTimer (); + packageStore.Clear (); + projectStore?.Clear (); + viewModel = null; + + base.Dispose (disposing); + } + + void UpdateDialogTitle () + { + if (viewModel.IsManagingSolution) + return; + + Title = GettextCatalog.GetString ("Manage NuGet Packages – {0}", viewModel.Project.Name); + } + + void UpdatePackageSearchEntryWithInitialText (string initialSearch) + { + packageSearchEntry.Text = initialSearch; + if (!String.IsNullOrEmpty (initialSearch)) { + packageSearchEntry.CursorPosition = initialSearch.Length; + } + } + + public string SearchText { + get { return packageSearchEntry.Text; } + } + + void InitializeListView () + { + packageStore = new ListStore (packageHasBackgroundColorField, packageCheckBoxAlphaField, packageImageField, packageViewModelField); + packagesListView.DataSource = packageStore; + + AddPackageCellViewToListView (); + + packagesListView.SelectionChanged += PackagesListViewSelectionChanged; + packagesListView.RowActivated += PackagesListRowActivated; + packagesListView.VerticalScrollControl.ValueChanged += PackagesListViewScrollValueChanged; + } + + void AddPackageCellViewToListView () + { + packageCellView = new ManagePackagesCellView { + PackageField = packageViewModelField, + HasBackgroundColorField = packageHasBackgroundColorField, + CheckBoxAlphaField = packageCheckBoxAlphaField, + ImageField = packageImageField, + CellWidth = 467 + }; + var textColumn = new ListViewColumn ("Package", packageCellView); + packagesListView.Columns.Add (textColumn); + + packageCellView.PackageChecked += PackageCellViewPackageChecked; + } + + void InitializeProjectsListView () + { + projectStore?.Clear (); + + // Recreate the list view each time. This is a workaround for the + // list view not displaying items on re-populating if it has been sorted. + if (projectsListView != null) { + projectsListViewVBox.Remove (projectsListView); + projectsListView.Dispose (); + } + + projectStore?.Dispose (); + + projectCheckedField = new DataField<bool> (); + projectNameField = new DataField<string> (); + packageVersionField = new DataField<string> (); + projectField = new DataField<ManageProjectViewModel> (); + projectStore = new ListStore (projectCheckedField, projectNameField, packageVersionField, projectField); + + projectsListView = new ListView (); + projectsListView.DataSource = projectStore; + + // Selected project check box column. + if (projectCheckBoxCellView != null) + projectCheckBoxCellView.Toggled -= ProjectCheckBoxCellViewToggled; + projectCheckBoxCellView = new CheckBoxCellView (); + projectCheckBoxCellView.ActiveField = projectCheckedField; + projectCheckBoxCellView.Editable = true; + projectCheckBoxCellView.Toggled += ProjectCheckBoxCellViewToggled; + var column = new ListViewColumn (string.Empty, projectCheckBoxCellView); + projectsListView.Columns.Add (column); + + // Project column. + var textCellView = new TextCellView (); + textCellView.TextField = projectNameField; + column = new ListViewColumn (GettextCatalog.GetString ("Project"), textCellView) { + CanResize = true, + SortDataField = projectNameField + }; + projectsListView.Columns.Add (column); + + // Package version column + textCellView = new TextCellView (); + textCellView.TextField = packageVersionField; + column = new ListViewColumn (GettextCatalog.GetString ("Version"), textCellView) { + CanResize = true, + SortDataField = packageVersionField + }; + projectsListView.Columns.Add (column); + + // Add list view to dialog. + projectsListViewVBox.PackStart (projectsListView, true, true); + } + + void ShowLoadingMessage () + { + UpdateSpinnerLabel (); + noPackagesFoundFrame.Visible = false; + packagesListView.Visible = false; + loadingSpinnerFrame.Visible = true; + loadingMessageVisible = true; + } + + void HideLoadingMessage () + { + loadingSpinnerFrame.Visible = false; + packagesListView.Visible = true; + noPackagesFoundFrame.Visible = false; + loadingMessageVisible = false; + } + + void UpdateSpinnerLabel () + { + if (String.IsNullOrWhiteSpace (packageSearchEntry.Text)) { + loadingSpinnerLabel.Text = GettextCatalog.GetString ("Loading package list..."); + } else { + loadingSpinnerLabel.Text = GettextCatalog.GetString ("Searching packages..."); + } + } + + void ShowNoPackagesFoundMessage () + { + if (!String.IsNullOrWhiteSpace (packageSearchEntry.Text)) { + packagesListView.Visible = false; + noPackagesFoundFrame.Visible = true; + } + } + + void CloseButtonClicked (object sender, EventArgs e) + { + Close (); + } + + void ShowPrereleaseCheckBoxClicked (object sender, EventArgs e) + { + viewModel.IncludePrerelease = !viewModel.IncludePrerelease; + + SaveIncludePrereleaseUserPreference (); + } + + void SaveIncludePrereleaseUserPreference () + { + Solution solution = IdeApp.ProjectOperations.CurrentSelectedSolution; + if (solution != null) { + if (viewModel.IncludePrerelease) { + solution.UserProperties.SetValue (IncludePrereleaseUserPreferenceName, viewModel.IncludePrerelease); + } else { + solution.UserProperties.RemoveValue (IncludePrereleaseUserPreferenceName); + } + solution.SaveUserProperties (); + } + } + + bool GetIncludePrereleaseUserPreference () + { + Solution solution = IdeApp.ProjectOperations.CurrentSelectedSolution; + if (solution != null) { + return solution.UserProperties.GetValue (IncludePrereleaseUserPreferenceName, false); + } + + return false; + } + + void LoadViewModel (string initialSearch) + { + viewModel.SearchTerms = initialSearch; + + viewModel.IncludePrerelease = GetIncludePrereleaseUserPreference (); + showPrereleaseCheckBox.Active = viewModel.IncludePrerelease; + + ClearSelectedPackageInformation (); + PopulatePackageSources (); + viewModel.PropertyChanged += ViewModelPropertyChanged; + + if (viewModel.SelectedPackageSource != null) { + viewModel.ReadPackages (); + } else { + HideLoadingMessage (); + } + } + + void ClearSelectedPackageInformation () + { + this.packageInfoVBox.Visible = false; + this.currentPackageVersionHBox.Visible = false; + this.packageVersionsHBox.Visible = false; + projectStore?.Clear (); + } + + void RemoveSelectedPackagePropertyChangedEventHandler () + { + if (viewModel.SelectedPackage != null) { + viewModel.SelectedPackage.PropertyChanged -= SelectedPackageViewModelChanged; + viewModel.SelectedPackage = null; + } + } + + List<SourceRepositoryViewModel> PackageSources { + get { + if (packageSources == null) { + packageSources = viewModel.PackageSources.ToList (); + } + return packageSources; + } + } + + void PopulatePackageSources () + { + foreach (SourceRepositoryViewModel packageSource in PackageSources) { + AddPackageSourceToComboBox (packageSource); + } + + AddPackageSourceToComboBox (dummyPackageSourceRepresentingConfigureSettingsItem); + + packageSourceComboBox.SelectedItem = viewModel.SelectedPackageSource; + } + + void AddPackageSourceToComboBox (SourceRepositoryViewModel packageSource) + { + packageSourceComboBox.Items.Add (packageSource, packageSource.Name); + } + + void PackageSourceChanged (object sender, EventArgs e) + { + var selectedPackageSource = (SourceRepositoryViewModel)packageSourceComboBox.SelectedItem; + if (selectedPackageSource == dummyPackageSourceRepresentingConfigureSettingsItem) { + ShowPreferencesForPackageSources = true; + Close (); + } else { + viewModel.SelectedPackageSource = selectedPackageSource; + } + } + + void PackagesListViewSelectionChanged (object sender, EventArgs e) + { + try { + ShowSelectedPackage (); + } catch (Exception ex) { + LoggingService.LogError ("Error showing selected package.", ex); + ShowErrorMessage (ex.Message); + } + } + + void ShowSelectedPackage () + { + RemoveSelectedPackagePropertyChangedEventHandler (); + + ManagePackagesSearchResultViewModel packageViewModel = GetSelectedPackageViewModel (); + viewModel.SelectedPackage = packageViewModel; + if (packageViewModel != null) { + ShowPackageInformation (packageViewModel); + } else { + ClearSelectedPackageInformation (); + } + UpdateAddPackagesButton (); + } + + ManagePackagesSearchResultViewModel GetSelectedPackageViewModel () + { + if (packagesListView.SelectedRow != -1) { + return packageStore.GetValue (packagesListView.SelectedRow, packageViewModelField); + } + return null; + } + + void ShowPackageInformation (ManagePackagesSearchResultViewModel packageViewModel) + { + bool consolidate = viewModel.IsConsolidatePageSelected; + + if (consolidate) { + projectsListViewLabel.Text = GettextCatalog.GetString ("Select projects and a version for a consolidation."); + } else { + // Use the package id and not the package title to prevent a pango crash if the title + // contains Chinese characters. + this.packageNameLabel.Markup = packageViewModel.GetIdMarkup (); + this.packageAuthor.Text = packageViewModel.Author; + this.packagePublishedDate.Text = packageViewModel.GetLastPublishedDisplayText (); + this.packageDownloads.Text = packageViewModel.GetDownloadCountDisplayText (); + this.packageDescription.Text = packageViewModel.Description; + this.packageId.Text = packageViewModel.Id; + this.packageId.Visible = packageViewModel.HasNoGalleryUrl; + ShowUri (this.packageIdLink, packageViewModel.GalleryUrl, packageViewModel.Id); + ShowUri (this.packageProjectPageLink, packageViewModel.ProjectUrl); + ShowUri (this.packageLicenseLink, packageViewModel.LicenseUrl); + + PopulatePackageDependencies (packageViewModel); + } + + if (viewModel.IsInstalledPageSelected) { + packageVersionsLabel.WidthRequest = -1; + currentPackageVersionHBox.Visible = false; + packageVersionsHBox.Visible = false; + } else if (viewModel.IsUpdatesPageSelected) { + PopulatePackageVersions (packageViewModel); + ShowCurrentPackageVersion (packageViewModel); + packageVersionsHBox.Visible = true; + } else { + packageVersionsLabel.WidthRequest = -1; + currentPackageVersionHBox.Visible = false; + PopulatePackageVersions (packageViewModel); + packageVersionsHBox.Visible = true; + } + + foreach (Widget child in packageInfoVBox.Children) { + child.Visible = !consolidate; + } + + if (consolidate) { + PopulateProjectList (); + } else { + projectStore?.Clear (); + } + + projectsListViewLabel.Visible = consolidate; + projectsListViewVBox.Visible = consolidate; + this.packageInfoVBox.Visible = true; + + packageViewModel.PropertyChanged += SelectedPackageViewModelChanged; + viewModel.LoadPackageMetadata (packageViewModel); + } + + void ShowCurrentPackageVersion (ManagePackagesSearchResultViewModel packageViewModel) + { + if (maxPackageVersionLabelWidth.HasValue) { + currentPackageVersionLabel.WidthRequest = maxPackageVersionLabelWidth.Value; + packageVersionsLabel.WidthRequest = maxPackageVersionLabelWidth.Value; + } + + currentPackageVersion.Text = packageViewModel.GetCurrentPackageVersionText (); + + currentPackageVersionInfoPopoverWidget.Message = packageViewModel.GetCurrentPackageVersionAdditionalText (); + currentPackageVersionInfoPopoverWidget.Visible = !string.IsNullOrEmpty (currentPackageVersionInfoPopoverWidget.Message); + + currentPackageVersionHBox.Visible = !string.IsNullOrEmpty (currentPackageVersion.Text); + } + + void ShowUri (LinkLabel linkLabel, Uri uri, string label) + { + linkLabel.Text = label; + ShowUri (linkLabel, uri); + } + + void ShowUri (LinkLabel linkLabel, Uri uri) + { + if (uri == null) { + linkLabel.Visible = false; + } else { + linkLabel.Visible = true; + linkLabel.Uri = uri; + } + } + + void ViewModelPropertyChanged (object sender, PropertyChangedEventArgs e) + { + try { + ShowPackages (); + } catch (Exception ex) { + LoggingService.LogError ("Error showing packages.", ex); + ShowErrorMessage (ex.Message); + } + } + + void ShowPackages () + { + if (viewModel.HasError) { + ShowErrorMessage (viewModel.ErrorMessage); + } else { + ClearErrorMessage (); + } + + if (viewModel.IsLoadingNextPage) { + // Show spinner? + } else if (viewModel.IsReadingPackages) { + ClearPackages (); + } else { + HideLoadingMessage (); + } + + if (!viewModel.IsLoadingNextPage) { + AppendPackagesToListView (); + } + + UpdateAddPackagesButton (); + } + + void ClearPackages () + { + packageStore.Clear (); + ResetPackagesListViewScroll (); + UpdatePackageListViewSelectionColor (); + ShowLoadingMessage (); + ShrinkImageCache (); + DisposePopulatePackageVersionsTimer (); + } + + void ResetPackagesListViewScroll () + { + packagesListView.VerticalScrollControl.Value = 0; + } + + void ShowErrorMessage (string message) + { + errorMessageLabel.Text = message; + errorMessageHBox.Visible = true; + } + + void ClearErrorMessage () + { + errorMessageHBox.Visible = false; + errorMessageLabel.Text = ""; + } + + void ShrinkImageCache () + { + imageLoader.ShrinkImageCache (); + } + + void AppendPackagesToListView () + { + bool packagesListViewWasEmpty = (packageStore.RowCount == 0); + + for (int row = packageStore.RowCount; row < viewModel.PackageViewModels.Count; ++row) { + ManagePackagesSearchResultViewModel packageViewModel = viewModel.PackageViewModels [row]; + AppendPackageToListView (packageViewModel); + LoadPackageImage (row, packageViewModel); + } + + if (packagesListViewWasEmpty && (packageStore.RowCount > 0)) { + packagesListView.SelectRow (0); + } + + if (!viewModel.IsReadingPackages && (packageStore.RowCount == 0)) { + ShowNoPackagesFoundMessage (); + } + } + + void AppendPackageToListView (ManagePackagesSearchResultViewModel packageViewModel) + { + int row = packageStore.AddRow (); + packageStore.SetValue (row, packageHasBackgroundColorField, IsOddRow (row)); + packageStore.SetValue (row, packageCheckBoxAlphaField, GetPackageCheckBoxAlpha ()); + packageStore.SetValue (row, packageViewModelField, packageViewModel); + } + + void LoadPackageImage (int row, ManagePackagesSearchResultViewModel packageViewModel) + { + if (packageViewModel.HasIconUrl) { + imageLoader.LoadFrom (packageViewModel.IconUrl, row); + } + } + + static bool IsOddRow (int row) + { + return (row % 2) == 0; + } + + double GetPackageCheckBoxAlpha () + { + if (PackagesCheckedCount == 0) { + return packageCheckBoxSemiTransarentAlpha; + } + return 1; + } + + void ImageLoaded (object sender, ImageLoadedEventArgs e) + { + if (!e.HasError) { + int row = (int)e.State; + if (IsValidRowAndUrl (row, e.Uri)) { + packageStore.SetValue (row, packageImageField, e.Image); + } + } + } + + bool IsValidRowAndUrl (int row, Uri uri) + { + if (row < packageStore.RowCount) { + ManagePackagesSearchResultViewModel packageViewModel = packageStore.GetValue (row, packageViewModelField); + if (packageViewModel != null) { + return uri == packageViewModel.IconUrl; + } + } + return false; + } + + void AddPackagesButtonClicked (object sender, EventArgs e) + { + try { + if (viewModel.IsConsolidatePageSelected) { + List<ManagePackagesSearchResultViewModel> packageViewModels = GetSelectedPackageViewModels (); + List<IPackageAction> packageActions = viewModel.CreateConsolidatePackageActions (packageViewModels); + RunPackageActions (packageActions); + } else { + var projects = SelectProjects ().ToList (); + if (projects.Any ()) { + List<IPackageAction> packageActions = CreatePackageActionsForSelectedPackages (projects); + RunPackageActions (packageActions); + } + } + } catch (Exception ex) { + LoggingService.LogError ("Adding packages failed.", ex); + ShowErrorMessage (ex.Message); + } + } + + IEnumerable<IDotNetProject> SelectProjects () + { + return SelectProjects (GetSelectedPackageViewModels ()); + } + + IEnumerable<IDotNetProject> SelectProjects (ManagePackagesSearchResultViewModel packageViewModel) + { + return SelectProjects (new [] { packageViewModel }); + } + + IEnumerable<IDotNetProject> SelectProjects (IEnumerable<ManagePackagesSearchResultViewModel> packageViewModels) + { + if (!viewModel.IsManagingSolution) + return viewModel.DotNetProjects; + + var selectProjectsViewModel = new SelectProjectsViewModel ( + GetFilteredDotNetProjectsToSelect (packageViewModels), + GetPackagesCountForAddPackagesButtonLabel (), + viewModel.PageSelected); + + using (var dialog = new SelectProjectsDialog (selectProjectsViewModel)) { + Command result = dialog.ShowWithParent (); + if (result == Command.Ok) { + return dialog.GetSelectedProjects (); + } else { + return Enumerable.Empty<IDotNetProject> (); + } + } + } + + /// <summary> + /// Remove projects that do not make sense based on the currently selected filter. + /// If we are on the Installed page that do not include any projects that do not have + /// the selected NuGet package installed. + /// </summary> + IEnumerable<IDotNetProject> GetFilteredDotNetProjectsToSelect (IEnumerable<ManagePackagesSearchResultViewModel> packageViewModels) + { + if (viewModel.PageSelected != ManagePackagesPage.Browse) { + var packageIds = packageViewModels.Select (pvm => pvm.Id).ToList (); + return viewModel.GetDotNetProjectsToSelect (packageIds); + } + + return viewModel.DotNetProjects; + } + + void RunPackageActions (List<IPackageAction> packageActions) + { + if (packageActions.Count > 0) { + ProgressMonitorStatusMessage progressMessage = GetProgressMonitorStatusMessages (packageActions); + backgroundActionRunner.Run (progressMessage, packageActions); + + if (viewModel.PageSelected == ManagePackagesPage.Browse) { + viewModel.OnInstallingSelectedPackages (); + } + Close (); + } + } + + List<IPackageAction> CreatePackageActionsForSelectedPackages (IEnumerable<IDotNetProject> selectedProjects) + { + List<ManagePackagesSearchResultViewModel> packageViewModels = GetSelectedPackageViewModels (); + if (packageViewModels.Count > 0) { + return viewModel.CreatePackageActions (packageViewModels, selectedProjects); + } + return new List<IPackageAction> (); + } + + ProgressMonitorStatusMessage GetProgressMonitorStatusMessages (List<IPackageAction> packageActions) + { + if (viewModel.PageSelected == ManagePackagesPage.Browse) { + return GetProgressMonitorInstallMessages (packageActions); + } else if (viewModel.PageSelected == ManagePackagesPage.Installed) { + return GetProgressMonitorUninstallMessages (packageActions); + } else if (viewModel.PageSelected == ManagePackagesPage.Updates) { + return GetProgressMonitorUpdateMessages (packageActions); + } else if (viewModel.PageSelected == ManagePackagesPage.Consolidate) { + return GetProgressMonitorConsolidateMessages (packageActions); + } + return null; + } + + ProgressMonitorStatusMessage GetProgressMonitorInstallMessages (List<IPackageAction> packageActions) + { + if (packageActions.Count == 1) { + string packageId = packageActions.Cast<INuGetPackageAction> ().First ().PackageId; + if (OlderPackageInstalledThanPackageSelected ()) { + return ProgressMonitorStatusMessageFactory.CreateUpdatingSinglePackageMessage (packageId); + } + return ProgressMonitorStatusMessageFactory.CreateInstallingSinglePackageMessage (packageId); + } + return ProgressMonitorStatusMessageFactory.CreateInstallingMultiplePackagesMessage (packageActions.Count); + } + + static ProgressMonitorStatusMessage GetProgressMonitorUninstallMessages (List<IPackageAction> packageActions) + { + int count = packageActions.Count; + if (count == 1) { + string packageId = packageActions.Cast<INuGetPackageAction> ().First ().PackageId; + return ProgressMonitorStatusMessageFactory.CreateRemoveSinglePackageMessage (packageId); + } + + return new ProgressMonitorStatusMessage ( + GettextCatalog.GetString ("Removing {0} packages...", count), + GettextCatalog.GetString ("{0} packages successfully removed.", count), + GettextCatalog.GetString ("Could not remove packages."), + GettextCatalog.GetString ("{0} packages removed with warnings.", count) + ); + } + + static ProgressMonitorStatusMessage GetProgressMonitorUpdateMessages (List<IPackageAction> packageActions) + { + int count = packageActions.Count; + if (count == 1) { + string packageId = packageActions.Cast<INuGetPackageAction> ().First ().PackageId; + return ProgressMonitorStatusMessageFactory.CreateUpdatingSinglePackageMessage (packageId); + } + + return new ProgressMonitorStatusMessage ( + GettextCatalog.GetString ("Updating {0} packages...", count), + GettextCatalog.GetString ("{0} packages successfully updated.", count), + GettextCatalog.GetString ("Could not update packages."), + GettextCatalog.GetString ("{0} packages updated with warnings.", count) + ); + } + + static ProgressMonitorStatusMessage GetProgressMonitorConsolidateMessages (List<IPackageAction> packageActions) + { + int count = packageActions.Count; + if (count == 1) { + string packageId = packageActions.Cast<INuGetPackageAction> ().First ().PackageId; + return new ProgressMonitorStatusMessage ( + GettextCatalog.GetString ("Consolidating {0}...", packageId), + GettextCatalog.GetString ("{0} successfully consolidated.", packageId), + GettextCatalog.GetString ("Could not consolidate {0}.", packageId), + GettextCatalog.GetString ("{0} consolidated with warnings.", packageId) + ); + } + + return new ProgressMonitorStatusMessage ( + GettextCatalog.GetString ("Consolidating {0} packages...", count), + GettextCatalog.GetString ("{0} packages successfully consolidated.", count), + GettextCatalog.GetString ("Could not consolidate packages."), + GettextCatalog.GetString ("{0} packages consolidated with warnings.", count) + ); + } + + List<ManagePackagesSearchResultViewModel> GetSelectedPackageViewModels () + { + List<ManagePackagesSearchResultViewModel> packageViewModels = viewModel.CheckedPackageViewModels.ToList (); + if (packageViewModels.Count > 0) { + return packageViewModels; + } + + ManagePackagesSearchResultViewModel selectedPackageViewModel = GetSelectedPackageViewModel (); + if (selectedPackageViewModel != null) { + packageViewModels.Add (selectedPackageViewModel); + } + return packageViewModels; + } + + void PackageSearchEntryChanged (object sender, EventArgs e) + { + ClearErrorMessage (); + ClearPackages (); + UpdateAddPackagesButton (); + SearchAfterDelay (); + } + + void SearchAfterDelay () + { + DisposeExistingTimer (); + searchTimer = Application.TimeoutInvoke (searchDelayTimeSpan, Search); + } + + void DisposeExistingTimer () + { + if (searchTimer != null) { + searchTimer.Dispose (); + } + } + + bool Search () + { + viewModel.SearchTerms = this.packageSearchEntry.Text; + viewModel.Search (); + + return false; + } + + void PackagesListRowActivated (object sender, ListViewRowEventArgs e) + { + if (PackagesCheckedCount > 0) { + AddPackagesButtonClicked (sender, e); + } else { + ManagePackagesSearchResultViewModel packageViewModel = packageStore.GetValue (e.RowIndex, packageViewModelField); + ManagePackage (packageViewModel); + } + } + + void ManagePackage (ManagePackagesSearchResultViewModel packageViewModel) + { + try { + if (packageViewModel != null) { + if (viewModel.IsConsolidatePageSelected) { + List<IPackageAction> packageActions = viewModel.CreateConsolidatePackageActions ( + new ManagePackagesSearchResultViewModel [] { packageViewModel } + ); + RunPackageActions (packageActions); + } else { + var projects = SelectProjects (packageViewModel).ToList (); + if (!projects.Any ()) + return; + + List<IPackageAction> packageActions = viewModel.CreatePackageActions ( + new ManagePackagesSearchResultViewModel [] { packageViewModel }, + projects); + RunPackageActions (packageActions); + } + } + } catch (Exception ex) { + LoggingService.LogInternalError ("ManagePackage failed.", ex); + ShowErrorMessage (ex.Message); + } + } + + void PackageSearchEntryActivated (object sender, EventArgs e) + { + if (loadingMessageVisible) + return; + + if (PackagesCheckedCount > 0) { + AddPackagesButtonClicked (sender, e); + } else { + ManagePackagesSearchResultViewModel selectedPackageViewModel = GetSelectedPackageViewModel (); + ManagePackage (selectedPackageViewModel); + } + } + + void PackagesListViewScrollValueChanged (object sender, EventArgs e) + { + if (viewModel.IsLoadingNextPage) { + return; + } + + if (IsScrollBarNearEnd (packagesListView.VerticalScrollControl)) { + if (viewModel.HasNextPage) { + viewModel.ShowNextPage (); + } + } + } + + bool IsScrollBarNearEnd (ScrollControl scrollControl) + { + double currentValue = scrollControl.Value; + double maxValue = scrollControl.UpperValue; + double pageSize = scrollControl.PageSize; + + return (currentValue / (maxValue - pageSize)) > 0.7; + } + + void PackageCellViewPackageChecked (object sender, ManagePackagesCellViewEventArgs e) + { + UpdateAddPackagesButton (); + UpdatePackageListViewSelectionColor (); + UpdatePackageListViewCheckBoxAlpha (); + } + + void UpdateAddPackagesButton () + { + addPackagesButton.Label = GetAddPackagesButtonLabel (); + addPackagesButton.Sensitive = IsAddPackagesButtonEnabled (); + } + + string GetAddPackagesButtonLabel () + { + int packagesSelectedCount = GetPackagesCountForAddPackagesButtonLabel (); + if (viewModel.PageSelected == ManagePackagesPage.Browse) { + string label = GettextCatalog.GetPluralString ("Add Package", "Add Packages", packagesSelectedCount); + if (PackagesCheckedCount <= 1 && OlderPackageInstalledThanPackageSelected ()) { + label = GettextCatalog.GetString ("Update Package"); + } + return label; + } else if (viewModel.PageSelected == ManagePackagesPage.Installed) { + return GettextCatalog.GetPluralString ("Uninstall Package", "Uninstall Packages", packagesSelectedCount); + } else if (viewModel.PageSelected == ManagePackagesPage.Updates) { + return GettextCatalog.GetPluralString ("Update Package", "Update Packages", packagesSelectedCount); + } else if (viewModel.PageSelected == ManagePackagesPage.Consolidate) { + return GettextCatalog.GetPluralString ("Consolidate Package", "Consolidate Packages", packagesSelectedCount); + } + + throw new NotImplementedException ("Unknown package results page"); + } + + int GetPackagesCountForAddPackagesButtonLabel () + { + if (PackagesCheckedCount > 1) + return PackagesCheckedCount; + + return 1; + } + + void UpdatePackageListViewSelectionColor () + { + packageCellView.UseStrongSelectionColor = (PackagesCheckedCount == 0); + } + + void UpdatePackageListViewCheckBoxAlpha () + { + if (PackagesCheckedCount > 1) + return; + + double alpha = GetPackageCheckBoxAlpha (); + for (int row = 0; row < packageStore.RowCount; ++row) { + packageStore.SetValue (row, packageCheckBoxAlphaField, alpha); + } + } + + bool OlderPackageInstalledThanPackageSelected () + { + if (PackagesCheckedCount != 0) { + return false; + } + + ManagePackagesSearchResultViewModel selectedPackageViewModel = GetSelectedPackageViewModel (); + if (selectedPackageViewModel != null) { + return selectedPackageViewModel.IsOlderPackageInstalled (); + } + return false; + } + + bool IsAddPackagesButtonEnabled () + { + if (loadingMessageVisible) + return false; + + if (!IsAtLeastOnePackageSelected ()) + return false; + + if (viewModel.IsConsolidatePageSelected) + return viewModel.CanConsolidate (); + + return true; + } + + bool IsAtLeastOnePackageSelected () + { + return (PackagesCheckedCount) >= 1 || (packagesListView.SelectedRow != -1); + } + + int PackagesCheckedCount { + get { return viewModel.CheckedPackageViewModels.Count; } + } + + void SelectedPackageViewModelChanged (object sender, PropertyChangedEventArgs e) + { + try { + if (e.PropertyName == "Versions") { + PopulatePackageVersions (viewModel.SelectedPackage); + } else { + if (!viewModel.IsConsolidatePageSelected) { + packagePublishedDate.Text = viewModel.SelectedPackage.GetLastPublishedDisplayText (); + PopulatePackageDependencies (viewModel.SelectedPackage); + } + } + } catch (Exception ex) { + LoggingService.LogError ("Error loading package versions.", ex); + } + } + + void PopulatePackageVersions (ManagePackagesSearchResultViewModel packageViewModel) + { + DisposePopulatePackageVersionsTimer (); + + ignorePackageVersionChanges = true; + try { + packageVersionComboBox.Items.Clear (); + if (packageViewModel.Versions.Any ()) { + NuGetVersion latestStableVersion = packageViewModel.Versions.FirstOrDefault (v => !v.IsPrerelease); + int count = 0; + foreach (NuGetVersion version in packageViewModel.Versions) { + count++; + if (count > MaxVersionsToPopulate) { + packageVersionsAddedCount = count - 1; + if (version >= packageViewModel.SelectedVersion) { + AddPackageVersionToComboBox (packageViewModel.SelectedVersion); + } + PopulatePackageVersionsAfterDelay (); + break; + } + AddPackageVersionToComboBox (version, latestStableVersion == version); + } + } else { + AddPackageVersionToComboBox (packageViewModel.Version); + } + packageVersionComboBox.SelectedItem = packageViewModel.SelectedVersion; + } finally { + ignorePackageVersionChanges = false; + } + } + + void AddPackageVersionToComboBox (NuGetVersion version, bool latestStable = false) + { + string versionLabel = version.ToString (); + if (latestStable) + versionLabel += " " + GettextCatalog.GetString ("(latest stable)"); + packageVersionComboBox.Items.Add (version, versionLabel); + } + + void PackageVersionChanged (object sender, EventArgs e) + { + if (ignorePackageVersionChanges || viewModel.SelectedPackage == null) + return; + + viewModel.SelectedPackage.SelectedVersion = (NuGetVersion)packageVersionComboBox.SelectedItem; + UpdateAddPackagesButton (); + } + + void PopulatePackageDependencies (ManagePackagesSearchResultViewModel packageViewModel) + { + if (packageViewModel.IsDependencyInformationAvailable) { + this.packageDependenciesHBox.Visible = true; + this.packageDependenciesListHBox.Visible = packageViewModel.HasDependencies; + this.packageDependenciesNoneLabel.Visible = !packageViewModel.HasDependencies; + this.packageDependenciesList.Text = packageViewModel.GetPackageDependenciesDisplayText (); + } else { + this.packageDependenciesHBox.Visible = false; + this.packageDependenciesListHBox.Visible = false; + this.packageDependenciesNoneLabel.Visible = false; + this.packageDependenciesList.Text = String.Empty; + } + } + + void PopulatePackageVersionsAfterDelay () + { + populatePackageVersionsTimer = Application.TimeoutInvoke (populatePackageVersionsDelayTimeSpan, PopulateMorePackageVersions); + } + + void DisposePopulatePackageVersionsTimer () + { + if (populatePackageVersionsTimer != null) { + populatePackageVersionsTimer.Dispose (); + populatePackageVersionsTimer = null; + } + } + + bool PopulateMorePackageVersions () + { + ManagePackagesSearchResultViewModel packageViewModel = viewModel?.SelectedPackage; + if (populatePackageVersionsTimer == null || packageViewModel == null) { + return false; + } + + int count = 0; + foreach (NuGetVersion version in packageViewModel.Versions.Skip (packageVersionsAddedCount)) { + count++; + + if (count > MaxVersionsToPopulate) { + packageVersionsAddedCount += count - 1; + return true; + } + + AddPackageVersionToComboBox (version); + } + + return false; + } + + void UpdatePackageResultsPageLabels () + { + UpdatePackageResultsLabel (ManagePackagesPage.Browse, browseLabel); + UpdatePackageResultsLabel (ManagePackagesPage.Installed, installedLabel); + UpdatePackageResultsLabel (ManagePackagesPage.Updates, updatesLabel); + UpdatePackageResultsLabel (ManagePackagesPage.Consolidate, consolidateLabel); + } + + void UpdatePackageResultsLabel (ManagePackagesPage page, Label label) + { + string text = (string)label.Tag; + if (page == viewModel.PageSelected) { + label.Markup = string.Format ("<b><u>{0}</u></b>", text); + } else { + label.Markup = text; + } + } + + void BrowseLabelButtonPressed (object sender, ButtonEventArgs e) + { + viewModel.PageSelected = ManagePackagesPage.Browse; + OnPackageResultsPageSelected (); + } + + void InstalledLabelButtonPressed (object sender, ButtonEventArgs e) + { + viewModel.PageSelected = ManagePackagesPage.Installed; + OnPackageResultsPageSelected (); + } + + void UpdatesLabelButtonPressed (object sender, ButtonEventArgs e) + { + viewModel.PageSelected = ManagePackagesPage.Updates; + OnPackageResultsPageSelected (); + } + + void ConsolidateLabelButtonPressed (object sender, ButtonEventArgs e) + { + viewModel.PageSelected = ManagePackagesPage.Consolidate; + OnPackageResultsPageSelected (); + } + + void OnPackageResultsPageSelected () + { + UpdatePackageResultsPageLabels (); + ClearErrorMessage (); + ClearPackages (); + UpdateAddPackagesButton (); + Search (); + } + + void PopulateProjectList () + { + InitializeProjectsListView (); + + foreach (ManageProjectViewModel project in viewModel.ProjectViewModels) { + int row = projectStore.AddRow (); + projectStore.SetValues ( + row, + projectCheckedField, + project.IsChecked, + projectNameField, + project.ProjectName, + packageVersionField, + project.PackageVersion, + projectField, + project); + } + } + + void ProjectCheckBoxCellViewToggled (object sender, WidgetEventArgs e) + { + int row = projectsListView.CurrentEventRow; + if (row == -1) + return; + + ManageProjectViewModel selectedProject = projectStore.GetValue (row, projectField); + if (selectedProject == null) + return; + + selectedProject.IsChecked = !selectedProject.IsChecked; + + UpdateAddPackagesButton (); + } + } }
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/AddPackagesDialogRunner.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesDialogRunner.cs index 8551dfafcd..e766df17e2 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/AddPackagesDialogRunner.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/ManagePackagesDialogRunner.cs @@ -1,78 +1,85 @@ -//
-// AddPackagesDialogRunner.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2014 Xamarin Inc. (http://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.Ide;
-using MonoDevelop.Core;
-using MonoDevelop.Projects;
-
-namespace MonoDevelop.PackageManagement
-{
- internal class AddPackagesDialogRunner
- {
- static RecentNuGetPackagesRepository recentPackagesRepository = new RecentNuGetPackagesRepository ();
-
- public void Run (string initialSearch = null)
- {
- Run (null, initialSearch);
- }
-
- public void Run (DotNetProject project, string initialSearch = null)
- {
- try {
- PackageManagementCredentialService.Reset ();
- bool configurePackageSources = false;
- do {
- using (AddPackagesDialog dialog = CreateDialog (project, initialSearch)) {
- dialog.ShowWithParent ();
- configurePackageSources = dialog.ShowPreferencesForPackageSources;
- initialSearch = dialog.SearchText;
- }
- if (configurePackageSources) {
- ShowPreferencesForPackageSources ();
- }
- } while (configurePackageSources);
-
- } catch (Exception ex) {
- LoggingService.LogInternalError (ex);
- }
- }
-
- AddPackagesDialog CreateDialog (DotNetProject project, string initialSearch)
- {
- var viewModel = AllPackagesViewModel.Create (project, recentPackagesRepository);
- return new AddPackagesDialog (
- viewModel,
- initialSearch);
- }
-
- void ShowPreferencesForPackageSources ()
- {
- IdeApp.Workbench.ShowGlobalPreferencesDialog (null, "PackageSources");
- }
- }
-}
-
+// +// ManagePackagesDialogRunner.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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.Ide; +using MonoDevelop.Core; +using MonoDevelop.Projects; + +namespace MonoDevelop.PackageManagement +{ + internal class ManagePackagesDialogRunner + { + static RecentManagedNuGetPackagesRepository recentPackagesRepository = new RecentManagedNuGetPackagesRepository (); + + public void Run (IDotNetProject project = null, string initialSearch = null) + { + try { + bool configurePackageSources = false; + do { + using (ManagePackagesDialog dialog = CreateDialog (initialSearch, project)) { + dialog.ShowWithParent (); + configurePackageSources = dialog.ShowPreferencesForPackageSources; + initialSearch = dialog.SearchText; + } + if (configurePackageSources) { + ShowPreferencesForPackageSources (); + } + } while (configurePackageSources); + + } catch (Exception ex) { + LoggingService.LogInternalError (ex); + } + } + + public void Run (Project project, string initialSearch = null) + { + Run (CreateDotNetProjectProxy (project), initialSearch); + } + + IDotNetProject CreateDotNetProjectProxy (Project project) + { + if (project is DotNetProject dotNetProject) + return new DotNetProjectProxy (dotNetProject); + + return null; + } + + ManagePackagesDialog CreateDialog (string initialSearch, IDotNetProject project) + { + var viewModel = ManagePackagesViewModel.Create (recentPackagesRepository, project); + return new ManagePackagesDialog ( + viewModel, + initialSearch); + } + + void ShowPreferencesForPackageSources () + { + IdeApp.Workbench.ShowGlobalPreferencesDialog (null, "PackageSources"); + } + } +} + diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/SelectProjectsDialog.UI.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/SelectProjectsDialog.UI.cs new file mode 100644 index 0000000000..3a707ee2e6 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/SelectProjectsDialog.UI.cs @@ -0,0 +1,117 @@ +// +// SelectProjectsDialog.UI.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2014 Xamarin Inc. (http://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 System.Collections.Generic; +using System.Linq; +using MonoDevelop.Core; +using Xwt; + +namespace MonoDevelop.PackageManagement +{ + partial class SelectProjectsDialog : Dialog + { + Label topLabel; + VBox projectsListView; + DialogButton okButton; + List<CheckBox> checkBoxes = new List<CheckBox> (); + + void Build () + { + Title = GettextCatalog.GetString ("Select Projects"); + Width = 420; + Height = 330; + Padding = 20; + + var mainVBox = new VBox (); + Content = mainVBox; + + topLabel = new Label (); + topLabel.Wrap = WrapMode.Word; + mainVBox.PackStart (topLabel); + + projectsListView = new VBox (); + + var projectsListScrollView = new ScrollView (projectsListView); + projectsListScrollView.HorizontalScrollPolicy = ScrollPolicy.Never; + projectsListScrollView.VerticalScrollPolicy = ScrollPolicy.Automatic; + projectsListScrollView.BorderVisible = false; + projectsListScrollView.BackgroundColor = Ide.Gui.Styles.BackgroundColor; + projectsListScrollView.Content = projectsListView; + + mainVBox.PackStart (projectsListScrollView, true, true); + + var cancelButton = new DialogButton (Command.Cancel); + Buttons.Add (cancelButton); + + okButton = new DialogButton (Command.Ok); + okButton.Sensitive = false; + Buttons.Add (okButton); + } + + void AddProject (SelectedProjectViewModel project) + { + var hbox = new HBox (); + hbox.Tag = project; + + var checkBox = new CheckBox (); + checkBox.Label = project.Name; + checkBox.Tag = project; + checkBox.Active = project.IsSelected; + checkBox.Clicked += ProjectCheckBoxClicked; + hbox.PackStart (checkBox); + + checkBoxes.Add (checkBox); + + projectsListView.PackStart (hbox); + } + + void ProjectCheckBoxClicked (object sender, EventArgs e) + { + var checkBox = (CheckBox)sender; + var project = (SelectedProjectViewModel)checkBox.Tag; + project.IsSelected = checkBox.Active; + + UpdateOkButtonSensitivity (); + } + + void UpdateOkButtonSensitivity () + { + okButton.Sensitive = viewModel.GetSelectedProjects ().Any (); + } + + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + + if (disposing) { + foreach (CheckBox checkBox in checkBoxes) { + checkBox.Clicked -= ProjectCheckBoxClicked; + } + } + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/SelectProjectsDialog.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/SelectProjectsDialog.cs new file mode 100644 index 0000000000..0c8003b5b8 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Gui/SelectProjectsDialog.cs @@ -0,0 +1,107 @@ +// +// ManagePackagesDialog.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2016 Xamarin Inc. (http://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.Collections.Generic; +using System.Linq; +using MonoDevelop.Core; + +namespace MonoDevelop.PackageManagement +{ + partial class SelectProjectsDialog + { + SelectProjectsViewModel viewModel; + + public SelectProjectsDialog (SelectProjectsViewModel viewModel) + { + this.viewModel = viewModel; + + Build (); + + UpdateTopLabel (viewModel.Projects.Count ()); + + AddProjects (); + } + + public IEnumerable<IDotNetProject> GetSelectedProjects () + { + return viewModel.GetSelectedProjects (); + } + + void UpdateTopLabel (int projectsCount) + { + if (viewModel.IsAddingSinglePackage) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Add the package to the project:", + "Add the package to the projects:", + projectsCount); + } else if (viewModel.IsAddingMultiplePackages) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Add the packages to the project:", + "Add the packages to the projects:", + projectsCount); + } else if (viewModel.IsRemovingSinglePackage) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Remove the package from the project:", + "Remove the package from the projects:", + projectsCount); + } else if (viewModel.IsRemovingMultiplePackages) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Remove the packages from the project:", + "Remove the packages from the projects:", + projectsCount); + } else if (viewModel.IsUpdatingSinglePackage) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Update the package in the project:", + "Update the package in the projects:", + projectsCount); + } else if (viewModel.IsUpdatingMultiplePackages) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Update the packages in the project:", + "Update the packages in the projects:", + projectsCount); + } else if (viewModel.IsConsolidatingSinglePackage) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Consolidate the package in the project:", + "Consolidate the package in the projects:", + projectsCount); + } else if (viewModel.IsConsolidatingMultiplePackages) { + topLabel.Text = GettextCatalog.GetPluralString ( + "Consolidate the packages in the project:", + "Consolidate the packages in the projects:", + projectsCount); + } + } + + void AddProjects () + { + foreach (SelectedProjectViewModel project in viewModel.Projects) { + AddProject (project); + } + + UpdateOkButtonSensitivity (); + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/NuGetPackageServicesProxy.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/NuGetPackageServicesProxy.cs index 03b3924ef9..8bf2b74441 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/NuGetPackageServicesProxy.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Refactoring/NuGetPackageServicesProxy.cs @@ -191,7 +191,7 @@ namespace MonoDevelop.PackageManagement.Refactoring Runtime.RunInMainThread (delegate { var project = IdeApp.Workbench.ActiveDocument?.Owner as DotNetProject; if (project != null) { - var runner = new AddPackagesDialogRunner (); + var runner = new ManagePackagesDialogRunner (); runner.Run (project, packageName); } }); diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolution.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolution.cs index c981095f4e..2e289c0ef5 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolution.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/FakeSolution.cs @@ -83,12 +83,8 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers public IDotNetProject ResolveProject (ProjectReference projectReference)
{
- if (OnResolveProject != null)
- return OnResolveProject (projectReference);
return Projects.FirstOrDefault (project => project.Name == projectReference.Include);
}
-
- public Func<ProjectReference, IDotNetProject> OnResolveProject;
}
}
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestablePackageSearchResultViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableManagePackagesSearchResultViewModel.cs index caeec5e53e..10bb57199d 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestablePackageSearchResultViewModel.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableManagePackagesSearchResultViewModel.cs @@ -1,5 +1,5 @@ // -// TestablePackageSearchResultViewModel.cs +// TestableManagePackagesSearchResultViewModel.cs // // Author: // Matt Ward <matt.ward@xamarin.com> @@ -30,12 +30,12 @@ using NuGet.PackageManagement.UI; namespace MonoDevelop.PackageManagement.Tests.Helpers { - class TestablePackageSearchResultViewModel : PackageSearchResultViewModel + class TestableManagePackagesSearchResultViewModel : ManagePackagesSearchResultViewModel { public FakeDotNetProject Project; public FakeSolutionManager SolutionManager; - public TestablePackageSearchResultViewModel ( + public TestableManagePackagesSearchResultViewModel ( PackageItemListViewModel viewModel) : this ( new FakeSolutionManager (), @@ -44,19 +44,19 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers { } - public TestablePackageSearchResultViewModel ( - TestableAllPackagesViewModel parent, + public TestableManagePackagesSearchResultViewModel ( + TestableManagePackagesViewModel parent, PackageItemListViewModel viewModel) : base (parent, viewModel) { } - public TestablePackageSearchResultViewModel ( + public TestableManagePackagesSearchResultViewModel ( FakeSolutionManager solutionManager, FakeDotNetProject project, PackageItemListViewModel viewModel) : this ( - new TestableAllPackagesViewModel (solutionManager, project), + new TestableManagePackagesViewModel (solutionManager, project), viewModel) { SolutionManager = solutionManager; diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableAllPackagesViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableManagePackagesViewModel.cs index 9d2586dcbb..44e8697994 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableAllPackagesViewModel.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.Helpers/TestableManagePackagesViewModel.cs @@ -1,5 +1,5 @@ // -// TestableAllPackagesViewModel.cs +// TestableManagePackagesViewModel.cs // // Author: // Matt Ward <matt.ward@xamarin.com> @@ -25,36 +25,57 @@ // THE SOFTWARE. using System; -using System.Collections.Generic; +using System.IO.IsolatedStorage; using System.Threading; using System.Threading.Tasks; using NuGet.PackageManagement.UI; -using NuGet.Protocol.Core.Types; namespace MonoDevelop.PackageManagement.Tests.Helpers { - class TestableAllPackagesViewModel : AllPackagesViewModel + class TestableManagePackagesViewModel : ManagePackagesViewModel { - public RecentNuGetPackagesRepository RecentPackagesRepository; + public RecentManagedNuGetPackagesRepository RecentPackagesRepository; public FakeNuGetProjectContext FakeNuGetProjectContext; - public TestableAllPackagesViewModel ( + public TestableManagePackagesViewModel ( IMonoDevelopSolutionManager solutionManager, IDotNetProject dotNetProject) : this ( solutionManager, dotNetProject, new FakeNuGetProjectContext (), - new RecentNuGetPackagesRepository ()) + new RecentManagedNuGetPackagesRepository ()) { } - public TestableAllPackagesViewModel ( + public TestableManagePackagesViewModel ( IMonoDevelopSolutionManager solutionManager, IDotNetProject dotNetProject, FakeNuGetProjectContext projectContext, - RecentNuGetPackagesRepository recentPackagesRepository) - : base (solutionManager, dotNetProject, projectContext, recentPackagesRepository) + RecentManagedNuGetPackagesRepository recentPackagesRepository) + : base (solutionManager, dotNetProject.ParentSolution, projectContext, recentPackagesRepository, dotNetProject) + { + FakeNuGetProjectContext = projectContext; + RecentPackagesRepository = recentPackagesRepository; + } + + public TestableManagePackagesViewModel ( + IMonoDevelopSolutionManager solutionManager, + ISolution solution) + : this ( + solutionManager, + solution, + new FakeNuGetProjectContext (), + new RecentManagedNuGetPackagesRepository ()) + { + } + + public TestableManagePackagesViewModel ( + IMonoDevelopSolutionManager solutionManager, + ISolution solution, + FakeNuGetProjectContext projectContext, + RecentManagedNuGetPackagesRepository recentPackagesRepository) + : base (solutionManager, solution, projectContext, recentPackagesRepository, null) { FakeNuGetProjectContext = projectContext; RecentPackagesRepository = recentPackagesRepository; @@ -62,7 +83,7 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers public FakePackageFeed PackageFeed = new FakePackageFeed (); - protected override IPackageFeed CreatePackageFeed (IEnumerable<SourceRepository> sourceRepositories) + protected override IPackageFeed CreatePackageFeed (PackageLoadContext context) { return PackageFeed; } @@ -92,9 +113,9 @@ namespace MonoDevelop.PackageManagement.Tests.Helpers public Task GetPackagesInstalledInProjectTask; - protected override Task GetPackagesInstalledInProject () + protected override Task GetPackagesInstalledInProjects () { - GetPackagesInstalledInProjectTask = base.GetPackagesInstalledInProject (); + GetPackagesInstalledInProjectTask = base.GetPackagesInstalledInProjects (); return GetPackagesInstalledInProjectTask; } } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj index bbd00cd4ca..560775c805 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests.csproj @@ -44,7 +44,7 @@ <Compile Include="MonoDevelop.PackageManagement.Tests\PackageSourceViewModelTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests\RegisteredPackageSourcesViewModelTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\PackageSourceCollectionAssert.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Tests\RecentNuGetPackagesRepositoryTests.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests\RecentManagedNuGetPackagesRepositoryTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeSolution.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeProject.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakePackageManagementProjectService.cs" /> @@ -86,17 +86,15 @@ <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeNuGetProjectAction.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeNuGetProjectContext.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests\NuGetPackageUninstallerTests.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Tests\AllPackagesViewModelTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeSolutionManager.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeNuGetProject.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeSourceRepositoryProvider.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakeNuGetSettings.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests\FakePackageFeed.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableAllPackagesViewModel.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Tests\PackageSearchResultViewModelTests.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests\ManagePackagesSearchResultViewModelTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakePackageMetadataProvider.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakePackageSearchMetadata.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestablePackageSearchResultViewModel.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableManagePackagesSearchResultViewModel.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakePackageMetadataResource.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\FakePackageMetadataResourceProvider.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests\UpdatedNuGetPackagesInWorkspaceTests.cs" /> @@ -154,6 +152,8 @@ <Compile Include="MonoDevelop.PackageManagement.Tests\PackagesCommandHandlerTests.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableRestorePackagesHandler.cs" /> <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableRestorePackagesInProjectHandler.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests\ManagePackagesViewModelTests.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Tests.Helpers\TestableManagePackagesViewModel.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/PackageSearchResultViewModelTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/ManagePackagesSearchResultViewModelTests.cs index d2a033fe46..dad464ab8f 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/PackageSearchResultViewModelTests.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/ManagePackagesSearchResultViewModelTests.cs @@ -1,5 +1,5 @@ // -// PackageSearchResultViewModelTests.cs +// ManagePackagesSearchResultViewModelTests.cs // // Author: // Matt Ward <matt.ward@xamarin.com> @@ -43,11 +43,11 @@ using NUnit.Framework; namespace MonoDevelop.PackageManagement.Tests { [TestFixture] - public class PackageSearchResultViewModelTests + public class ManagePackagesSearchResultViewModelTests { - TestablePackageSearchResultViewModel viewModel; + TestableManagePackagesSearchResultViewModel viewModel; PackageItemListViewModel packageItemListViewModel; - TestableAllPackagesViewModel parent; + TestableManagePackagesViewModel parent; FakePackageMetadataProvider metadataProvider; List<VersionInfo> packageVersions; FakePackageSearchMetadata packageSearchMetadata; @@ -78,8 +78,8 @@ namespace MonoDevelop.PackageManagement.Tests packageSearchMetadata = metadataProvider.AddPackageMetadata (package.Id, package.Version.ToString ()); var solutionManager = new FakeSolutionManager (); var project = new FakeDotNetProject (); - parent = new TestableAllPackagesViewModel (solutionManager, project); - viewModel = new TestablePackageSearchResultViewModel (parent, packageItemListViewModel); + parent = new TestableManagePackagesViewModel (solutionManager, project); + viewModel = new TestableManagePackagesSearchResultViewModel (parent, packageItemListViewModel); } Task LoadPackageMetadata () @@ -630,5 +630,59 @@ namespace MonoDevelop.PackageManagement.Tests Assert.AreEqual ("1.2-beta1", viewModel.Versions[0].ToString ()); Assert.AreEqual ("1.0-beta2", viewModel.Versions[1].ToString ()); } + + [Test] + public async Task ReadVersions_LatestVersionNotSelectedWhenConsolidating_SelectedVersionChangedToLatestVersion () + { + CreateViewModel (); + viewModel.SelectLatestVersion = true; + AddVersionsToPackageItemListViewModel ("2.0", "1.2.3", "0.2", "0.1"); + + await ReadVersions (); + + Assert.AreEqual (4, viewModel.Versions.Count); + Assert.AreEqual ("2.0", viewModel.Versions [0].ToString ()); + Assert.AreEqual ("1.2.3", viewModel.Versions [1].ToString ()); + Assert.AreEqual ("0.2", viewModel.Versions [2].ToString ()); + Assert.AreEqual ("0.1", viewModel.Versions [3].ToString ()); + Assert.AreEqual ("2.0", viewModel.SelectedVersion.ToString ()); + } + + [Test] + public async Task ReadVersions_LatestVersionNotSelectedWhenNotConsolidating_SelectedVersionNotChanged () + { + CreateViewModel (); + viewModel.SelectLatestVersion = false; + AddVersionsToPackageItemListViewModel ("2.0", "1.2.3", "0.2", "0.1"); + + await ReadVersions (); + + Assert.AreEqual (4, viewModel.Versions.Count); + Assert.AreEqual ("2.0", viewModel.Versions [0].ToString ()); + Assert.AreEqual ("1.2.3", viewModel.Versions [1].ToString ()); + Assert.AreEqual ("0.2", viewModel.Versions [2].ToString ()); + Assert.AreEqual ("0.1", viewModel.Versions [3].ToString ()); + Assert.AreEqual ("1.2.3", viewModel.SelectedVersion.ToString ()); + } + + [Test] + public async Task UpdateFromPreviouslyCheckedViewModel_VersionSelectedWasNotLatestVersion_VersionPreviouslySelectedIsReusedAfterReadingPackages () + { + var package = CreatePackageItemListViewModel (); + package.Version = new NuGetVersion ("1.2.3"); + CreateViewModel (package); + var checkedViewModel = viewModel; + checkedViewModel.IsChecked = true; + package = CreatePackageItemListViewModel (); + package.Version = new NuGetVersion ("1.2.3"); + CreateViewModel (package); + viewModel.SelectLatestVersion = true; + AddVersionsToPackageItemListViewModel ("2.0", "1.2.3", "0.2", "0.1"); + viewModel.UpdateFromPreviouslyCheckedViewModel (checkedViewModel); + await ReadVersions (); + + Assert.IsTrue (viewModel.IsChecked); + Assert.AreEqual ("1.2.3", viewModel.SelectedVersion.ToString ()); + } } }
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/AllPackagesViewModelTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/ManagePackagesViewModelTests.cs index 5a279f58e9..c4267ab94e 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/AllPackagesViewModelTests.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/ManagePackagesViewModelTests.cs @@ -1,5 +1,5 @@ // -// AllPackagesViewModelTests.cs +// ManagePackagesViewModelTests.cs // // Author: // Matt Ward <matt.ward@xamarin.com> @@ -31,6 +31,8 @@ using System.Threading.Tasks; using MonoDevelop.PackageManagement.Tests.Helpers; using NuGet.Configuration; using NuGet.PackageManagement.UI; +using NuGet.Packaging; +using NuGet.Packaging.Core; using NuGet.ProjectManagement; using NuGet.Versioning; using NUnit.Framework; @@ -38,28 +40,46 @@ using NUnit.Framework; namespace MonoDevelop.PackageManagement.Tests { [TestFixture] - public class AllPackagesViewModelTests + public class ManagePackagesViewModelTests { - TestableAllPackagesViewModel viewModel; + TestableManagePackagesViewModel viewModel; FakeSolutionManager solutionManager; FakeDotNetProject project; + FakeSolution solution; FakePackageSourceProvider packageSourceProvider; void CreateProject () { + solution = new FakeSolution (); project = new FakeDotNetProject (); + project.ParentSolution = solution; + solution.Projects.Add (project); solutionManager = new FakeSolutionManager (); packageSourceProvider = solutionManager.SourceRepositoryProvider.FakePackageSourceProvider; } + FakeDotNetProject AddProjectToSolution (string name) + { + var newProject = new FakeDotNetProject (); + newProject.Name = name; + solution.Projects.Add (newProject); + return newProject; + } + void CreateViewModel () { - viewModel = new TestableAllPackagesViewModel ( + viewModel = new TestableManagePackagesViewModel ( solutionManager, project); EnsurePackageSourcesLoaded (); } + void CreateViewModelForSolution () + { + viewModel = new TestableManagePackagesViewModel (solutionManager, solution); + EnsurePackageSourcesLoaded (); + } + void EnsurePackageSourcesLoaded () { viewModel.PackageSources.ToList (); @@ -110,29 +130,34 @@ namespace MonoDevelop.PackageManagement.Tests viewModel.SelectedPackageSource = secondPackageSource; } - PackageSearchResultViewModel AddRecentPackage (string packageId, string packageVersion, string packageSource) + ManagePackagesSearchResultViewModel AddRecentPackage (string packageId, string packageVersion, string packageSource) { var searchResultViewModel = CreateRecentPackage (packageId, packageVersion, packageSource); viewModel.RecentPackagesRepository.AddPackage (searchResultViewModel, packageSource); return searchResultViewModel; } - PackageSearchResultViewModel CreateRecentPackage (string packageId, string packageVersion, string packageSource) + ManagePackagesSearchResultViewModel CreateRecentPackage (string packageId, string packageVersion, string packageSource) { - var allPackagesViewModelForRecentPackages = new TestableAllPackagesViewModel ( + var packagesViewModelForRecentPackages = new TestableManagePackagesViewModel ( new FakeSolutionManager (), new FakeDotNetProject ()); var recentPackage = new PackageItemListViewModel { Id = packageId, Version = new NuGetVersion (packageVersion) }; - return new PackageSearchResultViewModel (allPackagesViewModelForRecentPackages, recentPackage); + return new ManagePackagesSearchResultViewModel (packagesViewModelForRecentPackages, recentPackage); } FakeNuGetProject CreateNuGetProjectForProject () { - var nugetProject = new FakeNuGetProject (project); - solutionManager.NuGetProjects[project] = nugetProject; + return CreateNuGetProjectForProject (project); + } + + FakeNuGetProject CreateNuGetProjectForProject (IDotNetProject dotNetProject) + { + var nugetProject = new FakeNuGetProject (dotNetProject); + solutionManager.NuGetProjects [dotNetProject] = nugetProject; return nugetProject; } @@ -1014,6 +1039,7 @@ namespace MonoDevelop.PackageManagement.Tests Assert.AreEqual (1, viewModel.PackageViewModels.Count); Assert.AreEqual ("Recent1", viewModel.PackageViewModels[0].Id); Assert.AreEqual (4, recentPackage.Versions.Count); + Assert.IsFalse (viewModel.PackageViewModels[0].SelectLatestVersion); } [Test] @@ -1031,6 +1057,377 @@ namespace MonoDevelop.PackageManagement.Tests Assert.AreEqual ("All Sources", selectedPackageSource.Name); Assert.IsTrue (selectedPackageSource.IsAggregate); } + + [Test] + public async Task Consolidate_ThreeProjectsDifferentPackageVersionInTwoProjects_ProjectVersionAndSelectionInformationAvailable () + { + CreateProject (); + project.Name = "LibC"; + var nugetProject = CreateNuGetProjectForProject (project); + nugetProject.AddPackageReference ("Test", "0.1"); + + var project2 = AddProjectToSolution ("LibA"); + CreateNuGetProjectForProject (project2); + + var project3 = AddProjectToSolution ("LibB"); + nugetProject = CreateNuGetProjectForProject (project3); + nugetProject.AddPackageReference ("Test", "0.2"); + + AddOnePackageSourceToRegisteredSources (); + CreateViewModelForSolution (); + viewModel.PackageFeed.AddPackage ("Test", "0.2"); + viewModel.PageSelected = ManagePackagesPage.Consolidate; + + viewModel.ReadPackages (); + await viewModel.ReadPackagesTask; + + var package = viewModel.PackageViewModels.Single (); + viewModel.SelectedPackage = package; + Assert.AreEqual ("Test", package.Id); + Assert.AreEqual ("0.2", package.Version.ToString ()); + + Assert.AreEqual (3, viewModel.ProjectViewModels.Count); + + // Checked projects first. + var projectViewModel = viewModel.ProjectViewModels [0]; + Assert.AreEqual ("LibB", projectViewModel.ProjectName); + Assert.AreEqual ("0.2", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [1]; + Assert.AreEqual ("LibC", projectViewModel.ProjectName); + Assert.AreEqual ("0.1", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [2]; + Assert.AreEqual ("LibA", projectViewModel.ProjectName); + Assert.AreEqual ("–", projectViewModel.PackageVersion); + Assert.IsFalse (projectViewModel.IsChecked); + + Assert.IsTrue (viewModel.CanConsolidate ()); + + // Expecting one action since Test 0.2 is already installed in LibB. + var actions = viewModel.CreateConsolidatePackageActions (package).ToList (); + Assert.AreEqual (1, actions.Count); + var action = actions [0] as InstallNuGetPackageAction; + Assert.AreEqual (PackageActionType.Install, action.ActionType); + Assert.AreEqual ("Test", action.PackageId); + Assert.AreEqual ("0.2", action.Version.ToString ()); + Assert.AreEqual ("LibC", action.Project.Name); + + // Check LibA, which does not have the package. + viewModel.ProjectViewModels [2].IsChecked = true; + + // Uncheck LibC. + viewModel.ProjectViewModels [1].IsChecked = false; + + Assert.IsTrue (viewModel.CanConsolidate ()); + + actions = viewModel.CreateConsolidatePackageActions (package).ToList (); + Assert.AreEqual (1, actions.Count); + action = actions [0] as InstallNuGetPackageAction; + Assert.AreEqual (PackageActionType.Install, action.ActionType); + Assert.AreEqual ("Test", action.PackageId); + Assert.AreEqual ("0.2", action.Version.ToString ()); + Assert.AreEqual ("LibA", action.Project.Name); + + // Uncheck all projects. No items checked so cannot consolidate. + viewModel.ProjectViewModels [0].IsChecked = false; + viewModel.ProjectViewModels [1].IsChecked = false; + viewModel.ProjectViewModels [2].IsChecked = false; + Assert.IsFalse (viewModel.CanConsolidate ()); + + // Check LibB which has same package. + viewModel.ProjectViewModels [0].IsChecked = true; + Assert.IsFalse (viewModel.CanConsolidate ()); + + viewModel.SelectedPackage = null; + Assert.AreEqual (0, viewModel.ProjectViewModels.Count); + Assert.IsFalse (viewModel.CanConsolidate ()); + } + + [Test] + public async Task CheckedPackageViewModels_DifferentTabPageSelected_CheckedPackagesCleared () + { + CreateProject (); + AddOnePackageSourceToRegisteredSources (); + CreateViewModel (); + viewModel.PackageFeed.AddPackage ("A", "1.2"); + viewModel.PackageFeed.AddPackage ("B", "1.3"); + viewModel.ReadPackages (); + await viewModel.ReadPackagesTask; + + viewModel.PackageViewModels [0].IsChecked = true; + + Assert.AreEqual (2, viewModel.PackageViewModels.Count); + Assert.AreEqual (1, viewModel.CheckedPackageViewModels.Count); + Assert.AreEqual ("A", viewModel.CheckedPackageViewModels [0].Id); + Assert.IsFalse (viewModel.PackageViewModels [0].SelectLatestVersion); + Assert.IsFalse (viewModel.PackageViewModels [1].SelectLatestVersion); + + viewModel.PageSelected = ManagePackagesPage.Consolidate; + + Assert.AreEqual (0, viewModel.CheckedPackageViewModels.Count); + } + + [Test] + public async Task Consolidate_ThreeProjectsTwoPackages_ProjectVersionAndSelectionInformationAvailable () + { + CreateProject (); + project.Name = "LibA"; + var nugetProject = CreateNuGetProjectForProject (project); + nugetProject.AddPackageReference ("Test", "0.1"); + + var project2 = AddProjectToSolution ("LibB"); + nugetProject = CreateNuGetProjectForProject (project2); + nugetProject.AddPackageReference ("Other", "0.1"); + + var project3 = AddProjectToSolution ("LibC"); + nugetProject = CreateNuGetProjectForProject (project3); + nugetProject.AddPackageReference ("Test", "0.2"); + nugetProject.AddPackageReference ("Other", "0.3"); + + AddOnePackageSourceToRegisteredSources (); + CreateViewModelForSolution (); + viewModel.PackageFeed.AddPackage ("Test", "0.2"); + viewModel.PackageFeed.AddPackage ("Other", "0.3"); + viewModel.PageSelected = ManagePackagesPage.Consolidate; + + viewModel.ReadPackages (); + await viewModel.ReadPackagesTask; + + // Select Test package. + var package = viewModel.PackageViewModels [0]; + viewModel.SelectedPackage = package; + + Assert.AreEqual (2, viewModel.PackageViewModels.Count); + Assert.AreEqual ("Test", package.Id); + Assert.AreEqual ("0.2", package.Version.ToString ()); + Assert.IsTrue (package.SelectLatestVersion); + Assert.AreEqual ("Other", viewModel.PackageViewModels [1].Id); + Assert.AreEqual ("0.3", viewModel.PackageViewModels [1].Version.ToString ()); + Assert.IsTrue (viewModel.PackageViewModels [1].SelectLatestVersion); + + Assert.AreEqual (3, viewModel.ProjectViewModels.Count); + + var projectViewModel = viewModel.ProjectViewModels [0]; + Assert.AreEqual ("LibA", projectViewModel.ProjectName); + Assert.AreEqual ("0.1", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [1]; + Assert.AreEqual ("LibC", projectViewModel.ProjectName); + Assert.AreEqual ("0.2", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [2]; + Assert.AreEqual ("LibB", projectViewModel.ProjectName); + Assert.AreEqual ("–", projectViewModel.PackageVersion); + Assert.IsFalse (projectViewModel.IsChecked); + + // Check LibB, Uncheck LibC + viewModel.ProjectViewModels [2].IsChecked = true; + viewModel.ProjectViewModels [1].IsChecked = false; + + // Select Other package + package = viewModel.PackageViewModels [1]; + viewModel.SelectedPackage = package; + + Assert.AreEqual (3, viewModel.ProjectViewModels.Count); + + projectViewModel = viewModel.ProjectViewModels [0]; + Assert.AreEqual ("LibB", projectViewModel.ProjectName); + Assert.AreEqual ("0.1", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [1]; + Assert.AreEqual ("LibC", projectViewModel.ProjectName); + Assert.AreEqual ("0.3", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [2]; + Assert.AreEqual ("LibA", projectViewModel.ProjectName); + Assert.AreEqual ("–", projectViewModel.PackageVersion); + Assert.IsFalse (projectViewModel.IsChecked); + + // Select Test package again. + package = viewModel.PackageViewModels [0]; + viewModel.SelectedPackage = package; + + projectViewModel = viewModel.ProjectViewModels [0]; + Assert.AreEqual ("LibA", projectViewModel.ProjectName); + Assert.AreEqual ("0.1", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [1]; + Assert.AreEqual ("LibC", projectViewModel.ProjectName); + Assert.AreEqual ("0.2", projectViewModel.PackageVersion); + Assert.IsFalse (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [2]; + Assert.AreEqual ("LibB", projectViewModel.ProjectName); + Assert.AreEqual ("–", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + // Check that the cached project information is cleared after selecting a different + // tab and going back to the consolidate tab. + viewModel.PageSelected = ManagePackagesPage.Browse; + viewModel.SelectedPackage = null; + viewModel.PageSelected = ManagePackagesPage.Consolidate; + package = viewModel.PackageViewModels [0]; + viewModel.SelectedPackage = package; + + projectViewModel = viewModel.ProjectViewModels [0]; + Assert.AreEqual ("LibA", projectViewModel.ProjectName); + Assert.AreEqual ("0.1", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [1]; + Assert.AreEqual ("LibC", projectViewModel.ProjectName); + Assert.AreEqual ("0.2", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [2]; + Assert.AreEqual ("LibB", projectViewModel.ProjectName); + Assert.AreEqual ("–", projectViewModel.PackageVersion); + Assert.IsFalse (projectViewModel.IsChecked); + } + + [Test] + public async Task Consolidate_ThreeProjectsTwoPackages_PackagesChecked_CheckedPackagesCanBeConsolidated () + { + CreateProject (); + project.Name = "LibA"; + var nugetProject = CreateNuGetProjectForProject (project); + nugetProject.AddPackageReference ("Test", "0.1"); + + var project2 = AddProjectToSolution ("LibB"); + nugetProject = CreateNuGetProjectForProject (project2); + nugetProject.AddPackageReference ("Other", "0.1"); + + var project3 = AddProjectToSolution ("LibC"); + nugetProject = CreateNuGetProjectForProject (project3); + nugetProject.AddPackageReference ("Test", "0.2"); + nugetProject.AddPackageReference ("Other", "0.3"); + + AddOnePackageSourceToRegisteredSources (); + CreateViewModelForSolution (); + viewModel.PackageFeed.AddPackage ("Test", "0.2"); + viewModel.PackageFeed.AddPackage ("Other", "0.3"); + viewModel.PageSelected = ManagePackagesPage.Consolidate; + + viewModel.ReadPackages (); + await viewModel.ReadPackagesTask; + + // Select Test package. + var package = viewModel.PackageViewModels [0]; + viewModel.SelectedPackage = package; + + Assert.AreEqual (2, viewModel.PackageViewModels.Count); + Assert.AreEqual ("Test", package.Id); + Assert.AreEqual ("0.2", package.Version.ToString ()); + Assert.AreEqual ("Other", viewModel.PackageViewModels [1].Id); + Assert.AreEqual ("0.3", viewModel.PackageViewModels [1].Version.ToString ()); + + // Checked packages first. + var projectViewModel = viewModel.ProjectViewModels [0]; + Assert.AreEqual ("LibA", projectViewModel.ProjectName); + Assert.AreEqual ("0.1", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [1]; + Assert.AreEqual ("LibC", projectViewModel.ProjectName); + Assert.AreEqual ("0.2", projectViewModel.PackageVersion); + Assert.IsTrue (projectViewModel.IsChecked); + + projectViewModel = viewModel.ProjectViewModels [2]; + Assert.AreEqual ("LibB", projectViewModel.ProjectName); + Assert.AreEqual ("–", projectViewModel.PackageVersion); + Assert.IsFalse (projectViewModel.IsChecked); + + viewModel.ProjectViewModels [0].IsChecked = false; + viewModel.ProjectViewModels [1].IsChecked = false; + viewModel.ProjectViewModels [2].IsChecked = false; + + Assert.IsFalse (viewModel.CanConsolidate ()); + + // Select other package, check it, then switch back to + // Test package + package = viewModel.PackageViewModels [1]; + viewModel.SelectedPackage = package; + package.IsChecked = true; + viewModel.SelectedPackage = viewModel.PackageViewModels [0]; + + // Should be able to consolidate since Other package is checked even though + // Test package is selected and cannot be consolidated. + Assert.IsTrue (viewModel.CanConsolidate ()); + + // Check Test package too. + viewModel.SelectedPackage.IsChecked = true; + var actions = viewModel.CreateConsolidatePackageActions (viewModel.PackageViewModels [0]).ToList (); + // Should be no actions since all projects are unchecked for this package. + Assert.AreEqual (0, actions.Count); + + actions = viewModel.CreateConsolidatePackageActions (viewModel.PackageViewModels [1]).ToList (); + var action = actions [0] as InstallNuGetPackageAction; + Assert.AreEqual (1, actions.Count); + Assert.AreEqual ("Other", action.PackageId); + Assert.AreEqual ("0.3", action.Version.ToString ()); + Assert.AreEqual ("LibB", action.Project.Name); + } + + [Test] + public async Task CurrentVersion_OneUpdate_PackageVersionInstalledInProjectReturned () + { + CreateProject (); + var nugetProject = CreateNuGetProjectForProject (project); + nugetProject.AddPackageReference ("Test", "0.1"); + AddOnePackageSourceToRegisteredSources (); + CreateViewModel (); + viewModel.PackageFeed.AddPackage ("Test", "0.2"); + viewModel.PageSelected = ManagePackagesPage.Browse; + + viewModel.ReadPackages (); + await viewModel.ReadPackagesTask; + + var package = viewModel.PackageViewModels.Single (); + Assert.AreEqual ("Test", package.Id); + Assert.AreEqual ("0.2", package.Version.ToString ()); + Assert.AreEqual ("0.1", package.GetCurrentPackageVersionText ()); + Assert.AreEqual (string.Empty, package.GetCurrentPackageVersionAdditionalText ()); + } + + [Test] + public async Task CurrentVersion_DifferentPackageVersionInstalledInProjects_MultipleReturned () + { + CreateProject (); + CreateProject (); + project.Name = "LibC"; + var nugetProject = CreateNuGetProjectForProject (project); + nugetProject.AddPackageReference ("Test", "0.1"); + + var project2 = AddProjectToSolution ("LibA"); + CreateNuGetProjectForProject (project2); + + var project3 = AddProjectToSolution ("LibB"); + nugetProject = CreateNuGetProjectForProject (project3); + nugetProject.AddPackageReference ("Test", "0.2"); + + AddOnePackageSourceToRegisteredSources (); + CreateViewModelForSolution (); + viewModel.PackageFeed.AddPackage ("Test", "0.2"); + + viewModel.PageSelected = ManagePackagesPage.Browse; + + viewModel.ReadPackages (); + await viewModel.ReadPackagesTask; + + string expectedAdditionalText = "LibB: 0.2, LibC: 0.1"; + var package = viewModel.PackageViewModels.Single (); + Assert.AreEqual ("Test", package.Id); + Assert.AreEqual ("0.2", package.Version.ToString ()); + Assert.AreEqual ("Multiple", package.GetCurrentPackageVersionText ()); + Assert.AreEqual (expectedAdditionalText, package.GetCurrentPackageVersionAdditionalText ()); + } } } - diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RecentNuGetPackagesRepositoryTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RecentManagedNuGetPackagesRepositoryTests.cs index 9bd5595b66..0f327d163a 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RecentNuGetPackagesRepositoryTests.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/RecentManagedNuGetPackagesRepositoryTests.cs @@ -1,5 +1,5 @@ //
-// RecentNuGetPackagesRepositoryTests.cs
+// RecentManagedNuGetPackagesRepositoryTests.cs
//
// Author:
// Matt Ward <matt.ward@xamarin.com>
@@ -33,37 +33,37 @@ using NUnit.Framework; namespace MonoDevelop.PackageManagement.Tests
{
[TestFixture]
- public class RecentNuGetPackagesRepositoryTests
+ public class RecentManagedNuGetPackagesRepositoryTests
{
- RecentNuGetPackagesRepository repository;
+ RecentManagedNuGetPackagesRepository repository;
void CreateRepository ()
{
- repository = new RecentNuGetPackagesRepository ();
+ repository = new RecentManagedNuGetPackagesRepository ();
}
- PackageSearchResultViewModel AddOnePackageToRepository (string id, string packageSource)
+ ManagePackagesSearchResultViewModel AddOnePackageToRepository (string id, string packageSource)
{
var viewModel = CreatePackage (id);
repository.AddPackage (viewModel, packageSource);
return viewModel;
}
- PackageSearchResultViewModel CreatePackage (string id)
+ ManagePackagesSearchResultViewModel CreatePackage (string id)
{
var packageViewModel = new PackageItemListViewModel {
Id = id
};
- return new PackageSearchResultViewModel (null, packageViewModel);
+ return new ManagePackagesSearchResultViewModel (null, packageViewModel);
}
- IEnumerable<PackageSearchResultViewModel> AddTwoDifferentPackagesToRepository (string packageSource)
+ IEnumerable<ManagePackagesSearchResultViewModel> AddTwoDifferentPackagesToRepository (string packageSource)
{
yield return AddOnePackageToRepository ("Test.Package.1", packageSource);
yield return AddOnePackageToRepository ("Test.Package.2", packageSource);
}
- IEnumerable<PackageSearchResultViewModel> AddFourDifferentPackagesToRepository (string packageSource)
+ IEnumerable<ManagePackagesSearchResultViewModel> AddFourDifferentPackagesToRepository (string packageSource)
{
yield return AddOnePackageToRepository ("Test.Package.1", packageSource);
yield return AddOnePackageToRepository ("Test.Package.2", packageSource);
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.addin.xml b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.addin.xml index dc351294d4..19c48c6789 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.addin.xml +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.addin.xml @@ -22,6 +22,18 @@ _label = "Add NuGet _Packages..."
defaultHandler = "MonoDevelop.PackageManagement.Commands.AddPackagesHandler" />
<Command
+ id="MonoDevelop.PackageManagement.Commands.ManagePackages"
+ _description="Manage packages for the solution"
+ _label="_Manage NuGet Packages..."
+ _displayName="Manage Packages (Solution)"
+ defaultHandler="MonoDevelop.PackageManagement.ManagePackagesHandler" />
+ <Command
+ id="MonoDevelop.PackageManagement.Commands.ManagePackagesInProject"
+ _description="Manage packages for the project"
+ _label="_Manage NuGet Packages..."
+ _displayName="Manage Packages"
+ defaultHandler="MonoDevelop.PackageManagement.ManagePackagesInProjectHandler" />
+ <Command
id = "MonoDevelop.PackageManagement.Commands.RestorePackages"
_description = "Restore all missing packages in the solution"
_label = "_Restore NuGet Packages"
@@ -65,7 +77,7 @@ <Extension path="/MonoDevelop/Ide/MainMenu/Project">
<SeparatorItem insertafter="MonoDevelop.Ide.Commands.ProjectCommands.AddReference" />
- <CommandItem id="MonoDevelop.PackageManagement.Commands.AddPackages" />
+ <CommandItem id="MonoDevelop.PackageManagement.Commands.ManagePackages" />
<CommandItem id="MonoDevelop.PackageManagement.Commands.UpdateAllPackagesInSolution" />
<CommandItem id="MonoDevelop.PackageManagement.Commands.RestorePackages" />
<SeparatorItem />
@@ -73,18 +85,36 @@ <Extension path="/MonoDevelop/Ide/ContextMenu/ProjectPad">
<Condition id="ItemType" value="Solution">
+ <SeparatorItem
+ id="MonoDevelop.PackageManagement.ProjectPad.Separator"
+ insertafter="MonoDevelop.Ide.Commands.ProjectCommands.AddReference"
+ insertbefore="AddSectionEnd" />
+ <CommandItem
+ id="MonoDevelop.PackageManagement.Commands.ManagePackages"
+ insertafter="MonoDevelop.PackageManagement.ProjectPad.Separator"
+ insertbefore="AddSectionEnd" />
<CommandItem
id="MonoDevelop.PackageManagement.Commands.UpdateAllPackagesInSolution"
- insertafter="MonoDevelop.PackageManagement.Commands.AddReference"
+ insertafter="MonoDevelop.PackageManagement.Commands.ManagePackagese"
insertbefore="AddSectionEnd" />
<CommandItem
id="MonoDevelop.PackageManagement.Commands.RestorePackages"
insertafter="MonoDevelop.PackageManagement.Commands.UpdateAllPackagesInSolution"
insertbefore="AddSectionEnd" />
</Condition>
+ <Condition id="ItemType" value="DotNetProject">
+ <SeparatorItem
+ id="MonoDevelop.PackageManagement.ProjectPad.Separator"
+ insertafter="MonoDevelop.Ide.Commands.ProjectCommands.AddReference"
+ insertbefore="AddSectionEnd" />
+ <CommandItem
+ id="MonoDevelop.PackageManagement.Commands.ManagePackagesInProject"
+ insertafter="MonoDevelop.PackageManagement.ProjectPad.Separator"
+ insertbefore="AddSectionEnd" />
+ </Condition>
<Condition id="ItemType" value="MonoDevelop.PackageManagement.NodeBuilders.ProjectPackagesFolderNode">
<CommandItem
- id="MonoDevelop.PackageManagement.Commands.AddPackages" _label = "Add NuGet _Packages..." />
+ id="MonoDevelop.PackageManagement.Commands.ManagePackagesInProject" />
<CommandItem
id="MonoDevelop.PackageManagement.Commands.PackagesFolderNodeCommands.ReinstallAllPackagesInProject" />
<CommandItem
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj index 6d2e380db0..851b54e052 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.csproj @@ -102,10 +102,6 @@ <Compile Include="MonoDevelop.PackageManagement.Gui\PackageManagementOptionsPanel.cs" /> <Compile Include="MonoDevelop.PackageManagement.Gui\PackageManagementOptionsWidget.cs" /> <Compile Include="MonoDevelop.PackageManagement.Commands\AddPackagesHandler.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Gui\AddPackagesDialog.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Gui\AddPackagesDialog.UI.cs"> - <DependentUpon>AddPackagesDialog.cs</DependentUpon> - </Compile> <Compile Include="MonoDevelop.PackageManagement\PackageRepositoryNodeExtensions.cs" /> <Compile Include="MonoDevelop.PackageManagement\ProjectTemplateNuGetPackageInstaller.cs" /> <Compile Include="MonoDevelop.PackageManagement\PackageManagementEventsMonitor.cs" /> @@ -127,9 +123,7 @@ <Compile Include="MonoDevelop.PackageManagement\ProgressMonitorStatusMessageFactory.cs" /> <Compile Include="MonoDevelop.PackageManagement\ProgressMonitorStatusMessage.cs" /> <Compile Include="AddinInfo.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Gui\PackageCellView.cs" /> <Compile Include="MonoDevelop.PackageManagement\SearchPackagesSearchCategory.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Gui\AddPackagesDialogRunner.cs" /> <Compile Include="MonoDevelop.PackageManagement.Commands\ProjectPackagesFolderNodeCommandHandler.cs" /> <Compile Include="MonoDevelop.PackageManagement.NodeBuilders\ProjectReferenceNodeBuilderExtension.cs" /> <Compile Include="MonoDevelop.PackageManagement\ProjectReferenceExtensions.cs" /> @@ -142,7 +136,6 @@ <DependentUpon>AddPackageSourceDialog.cs</DependentUpon> </Compile> <Compile Include="MonoDevelop.PackageManagement.Gui\DialogExtensions.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Gui\PackageCellViewEventArgs.cs" /> <Compile Include="MonoDevelop.PackageManagement.Gui\Styles.cs" /> <Compile Include="MonoDevelop.PackageManagement\ImageLoader.cs" /> <Compile Include="MonoDevelop.PackageManagement\ImageLoadedEventArgs.cs" /> @@ -153,7 +146,7 @@ <Compile Include="MonoDevelop.PackageManagement\ImageCache.cs" /> <Compile Include="MonoDevelop.PackageManagement\ProgressMonitorExtensions.cs" /> <Compile Include="MonoDevelop.PackageManagement\PackageListViewTextFormatter.cs" /> - <Compile Include="MonoDevelop.PackageManagement.Gui\PackageCellViewCheckBox.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Gui\ManagePackagesCellViewCheckBox.cs" /> <Compile Include="MonoDevelop.PackageManagement\PackageManagementProgressMonitor.cs" /> <Compile Include="MonoDevelop.PackageManagement\ISolution.cs" /> <Compile Include="MonoDevelop.PackageManagement\IProject.cs" /> @@ -197,13 +190,11 @@ <Compile Include="MonoDevelop.PackageManagement\PackageManagementSolutionProjectService.cs" /> <Compile Include="MonoDevelop.PackageManagement\NuGetPackageNewImportsHandler.cs" /> <Compile Include="MonoDevelop.PackageManagement\INuGetPackageNewImportsHandler.cs" /> - <Compile Include="MonoDevelop.PackageManagement\AllPackagesViewModel.cs" /> <Compile Include="MonoDevelop.PackageManagement\SourceRepositoryProvider.cs" /> <Compile Include="NuGet.PackageManagement.UI\SearchResult.cs" /> <Compile Include="NuGet.PackageManagement.UI\PackageItemListViewModel.cs" /> <Compile Include="NuGet.PackageManagement.UI\PackageStatus.cs" /> <Compile Include="NuGet.PackageManagement.UI\DisplayVersion.cs" /> - <Compile Include="MonoDevelop.PackageManagement\PackageSearchResultViewModel.cs" /> <Compile Include="MonoDevelop.PackageManagement\SourceRepositoryViewModel.cs" /> <Compile Include="MonoDevelop.PackageManagement\InstallNuGetPackageAction.cs" /> <Compile Include="MonoDevelop.PackageManagement\MonoDevelopSolutionManager.cs" /> @@ -270,8 +261,6 @@ <Compile Include="NuGet.PackageManagement.UI\IPackageMetadataProvider.cs" /> <Compile Include="NuGet.PackageManagement.UI\DetailControlModel.cs" /> <Compile Include="NuGet.PackageManagement.UI\PackageDetailControlModel.cs" /> - <Compile Include="MonoDevelop.PackageManagement\RecentNuGetPackagesRepository.cs" /> - <Compile Include="MonoDevelop.PackageManagement\PackageSearchResultViewModelComparer.cs" /> <Compile Include="MonoDevelop.PackageManagement\MonoDevelopPackageSourceProvider.cs" /> <Compile Include="MonoDevelop.PackageManagement\SettingsLoader.cs" /> <Compile Include="MonoDevelop.PackageManagement\INuGetPackageManager.cs" /> @@ -347,6 +336,35 @@ <Compile Include="MonoDevelop.PackageManagement\SdkProjectReloadMonitor.cs" /> <Compile Include="MonoDevelop.PackageManagement\SdkProjectBuilderMaintainer.cs" /> <Compile Include="MonoDevelop.PackageManagement\SdkProjectFileRenamedHandler.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Commands\ManagePackagesHandler.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Commands\ManagePackagesInProjectHandler.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Gui\ManagePackagesDialog.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Gui\ManagePackagesDialog.UI.cs"> + <DependentUpon>ManagePackagesDialog.cs</DependentUpon> + </Compile> + <Compile Include="MonoDevelop.PackageManagement.Gui\ManagePackagesDialogRunner.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Gui\SelectProjectsDialog.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Gui\SelectProjectsDialog.UI.cs"> + <DependentUpon>SelectProjectsDialog.cs</DependentUpon> + </Compile> + <Compile Include="MonoDevelop.PackageManagement.Gui\ManagePackagesCellView.cs" /> + <Compile Include="MonoDevelop.PackageManagement.Gui\ManagePackagesCellViewEventArgs.cs" /> + <Compile Include="MonoDevelop.PackageManagement\ManagedPackagesSearchResultViewModelComparer.cs" /> + <Compile Include="MonoDevelop.PackageManagement\ManagePackagesPage.cs" /> + <Compile Include="MonoDevelop.PackageManagement\ManagePackagesProjectInfo.cs" /> + <Compile Include="MonoDevelop.PackageManagement\ManagePackagesSearchResultViewModel.cs" /> + <Compile Include="MonoDevelop.PackageManagement\ManagePackagesViewModel.cs" /> + <Compile Include="NuGet.PackageManagement.UI\ConsolidatePackageFeed.cs" /> + <Compile Include="NuGet.PackageManagement.UI\InstalledPackageFeed.cs" /> + <Compile Include="NuGet.PackageManagement.UI\PackageCollection.cs" /> + <Compile Include="NuGet.PackageManagement.UI\PackageFeedEnumerator.cs" /> + <Compile Include="NuGet.PackageManagement.UI\PackageSearchMetadataCache.cs" /> + <Compile Include="NuGet.PackageManagement.UI\PlainPackageFeedBase.cs" /> + <Compile Include="NuGet.PackageManagement.UI\UpdatePackageFeed.cs" /> + <Compile Include="MonoDevelop.PackageManagement\SelectedProjectViewModel.cs" /> + <Compile Include="MonoDevelop.PackageManagement\SelectProjectsViewModel.cs" /> + <Compile Include="MonoDevelop.PackageManagement\RecentManagedNuGetPackagesRepository.cs" /> + <Compile Include="MonoDevelop.PackageManagement\ManageProjectViewModel.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MonoDevelop.PackageManagement.addin.xml" /> diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/AllPackagesViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/AllPackagesViewModel.cs deleted file mode 100644 index f1c5036f80..0000000000 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/AllPackagesViewModel.cs +++ /dev/null @@ -1,608 +0,0 @@ -//
-// AllPackagesViewModel.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2016 Xamarin Inc. (http://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 System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MonoDevelop.Core;
-using MonoDevelop.Ide;
-using MonoDevelop.Projects;
-using NuGet.Configuration;
-using NuGet.PackageManagement;
-using NuGet.PackageManagement.UI;
-using NuGet.Packaging;
-using NuGet.ProjectManagement;
-using NuGet.Protocol.Core.Types;
-using NuGet.Versioning;
-
-namespace MonoDevelop.PackageManagement
-{
- internal class AllPackagesViewModel : ViewModelBase<AllPackagesViewModel>, INuGetUILogger
- {
- SourceRepositoryViewModel selectedPackageSource;
- IPackageSourceProvider packageSourceProvider;
- PackageItemLoader currentLoader;
- CancellationTokenSource cancellationTokenSource;
- List<SourceRepositoryViewModel> packageSources;
- bool includePrerelease;
- bool ignorePackageCheckedChanged;
- IMonoDevelopSolutionManager solutionManager;
- NuGetProject nugetProject;
- IDotNetProject dotNetProject;
- INuGetProjectContext projectContext;
- List<PackageReference> packageReferences = new List<PackageReference> ();
- AggregatePackageSourceErrorMessage aggregateErrorMessage;
- NuGetPackageManager packageManager;
- RecentNuGetPackagesRepository recentPackagesRepository;
-
- public static AllPackagesViewModel Create (RecentNuGetPackagesRepository recentPackagesRepository)
- {
- var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (IdeApp.ProjectOperations.CurrentSelectedSolution);
- var dotNetProject = new DotNetProjectProxy ((DotNetProject)IdeApp.ProjectOperations.CurrentSelectedProject);
- return new AllPackagesViewModel (solutionManager, dotNetProject, recentPackagesRepository);
- }
-
- public static AllPackagesViewModel Create (
- DotNetProject project,
- RecentNuGetPackagesRepository recentPackagesRepository)
- {
- if (project == null)
- return Create (recentPackagesRepository);
-
- var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (project.ParentSolution);
- var dotNetProject = new DotNetProjectProxy (project);
- return new AllPackagesViewModel (solutionManager, dotNetProject, recentPackagesRepository);
- }
-
- public AllPackagesViewModel (
- IMonoDevelopSolutionManager solutionManager,
- IDotNetProject dotNetProject,
- RecentNuGetPackagesRepository recentPackagesRepository)
- : this (
- solutionManager,
- dotNetProject,
- new NuGetProjectContext (solutionManager.Settings),
- recentPackagesRepository)
- {
- }
-
- public AllPackagesViewModel (
- IMonoDevelopSolutionManager solutionManager,
- IDotNetProject dotNetProject,
- INuGetProjectContext projectContext,
- RecentNuGetPackagesRepository recentPackagesRepository)
- {
- this.solutionManager = solutionManager;
- this.dotNetProject = dotNetProject;
- this.projectContext = projectContext;
- this.recentPackagesRepository = recentPackagesRepository;
- PackageViewModels = new ObservableCollection<PackageSearchResultViewModel> ();
- CheckedPackageViewModels = new ObservableCollection<PackageSearchResultViewModel> ();
- ErrorMessage = String.Empty;
-
- packageManager = new NuGetPackageManager (
- solutionManager.CreateSourceRepositoryProvider (),
- solutionManager.Settings,
- solutionManager,
- new DeleteOnRestartManager ()
- );
-
- nugetProject = solutionManager.GetNuGetProject (dotNetProject);
- GetPackagesInstalledInProject ();
- }
-
- public NuGetProject NuGetProject {
- get { return nugetProject; }
- }
-
- public IDotNetProject Project {
- get { return dotNetProject; }
- }
- - public string SearchTerms { get; set; }
-
- public IEnumerable<SourceRepositoryViewModel> PackageSources {
- get {
- if (packageSources == null) {
- packageSources = GetPackageSources ().ToList ();
- }
- return packageSources;
- }
- }
-
- IEnumerable<SourceRepositoryViewModel> GetPackageSources ()
- {
- ISourceRepositoryProvider provider = solutionManager.CreateSourceRepositoryProvider ();
- packageSourceProvider = provider.PackageSourceProvider;
- var repositories = provider.GetRepositories ().ToList ();
-
- if (repositories.Count > 1) {
- yield return new AggregateSourceRepositoryViewModel (repositories);
- }
-
- foreach (SourceRepository repository in repositories) {
- yield return new SourceRepositoryViewModel (repository);
- }
- }
-
- public SourceRepositoryViewModel SelectedPackageSource {
- get {
- if (selectedPackageSource == null) {
- selectedPackageSource = GetActivePackageSource ();
- }
- return selectedPackageSource;
- }
- set {
- if (selectedPackageSource != value) {
- selectedPackageSource = value;
- SaveActivePackageSource ();
- ReadPackages ();
- OnPropertyChanged (null);
- }
- }
- }
- - SourceRepositoryViewModel GetActivePackageSource ()
- {
- if (packageSources == null)
- return null;
-
- if (!String.IsNullOrEmpty (packageSourceProvider.ActivePackageSourceName)) {
- SourceRepositoryViewModel packageSource = packageSources
- .FirstOrDefault (viewModel => String.Equals (viewModel.PackageSource.Name, packageSourceProvider.ActivePackageSourceName, StringComparison.CurrentCultureIgnoreCase));
- if (packageSource != null) {
- return packageSource;
- }
- }
-
- return packageSources.FirstOrDefault (packageSource => !packageSource.IsAggregate);
- }
-
- void SaveActivePackageSource ()
- {
- if (selectedPackageSource == null || packageSourceProvider == null)
- return;
-
- packageSourceProvider.SaveActivePackageSource (selectedPackageSource.PackageSource);
- }
- - public ObservableCollection<PackageSearchResultViewModel> PackageViewModels { get; private set; }
- public ObservableCollection<PackageSearchResultViewModel> CheckedPackageViewModels { get; private set; }
-
- public bool HasError { get; private set; }
- public string ErrorMessage { get; private set; }
-
- public bool IsLoadingNextPage { get; private set; }
- public bool IsReadingPackages { get; private set; }
- public bool HasNextPage { get; private set; }
-
- public bool IncludePrerelease {
- get { return includePrerelease; }
- set {
- if (includePrerelease != value) {
- includePrerelease = value;
- ReadPackages ();
- OnPropertyChanged (null);
- }
- }
- }
-
- public void Dispose()
- {
- OnDispose ();
- CancelReadPackagesTask ();
- IsDisposed = true;
- }
- - protected virtual void OnDispose()
- {
- }
-
- public bool IsDisposed { get; private set; }
-
- public void Search ()
- {
- ReadPackages ();
- OnPropertyChanged (null);
- }
-
- public void ReadPackages ()
- {
- if (SelectedPackageSource == null) {
- return;
- }
-
- HasNextPage = false;
- IsLoadingNextPage = false;
- currentLoader = null;
- StartReadPackagesTask ();
- }
-
- void StartReadPackagesTask (bool clearPackages = true)
- {
- IsReadingPackages = true;
- ClearError ();
- if (clearPackages) {
- CancelReadPackagesTask ();
- ClearPackages ();
- }
- CreateReadPackagesTask ();
- }
-
- void CancelReadPackagesTask()
- {
- if (cancellationTokenSource != null) {
- // Cancel on another thread since CancellationTokenSource.Cancel can sometimes
- // take up to a second on Mono and we do not want to block the UI thread.
- var tokenSource = cancellationTokenSource;
- Task.Run (() => {
- try {
- tokenSource.Cancel ();
- tokenSource.Dispose ();
- } catch (Exception ex) {
- LoggingService.LogError ("Unable to cancel task.", ex);
- }
- });
- cancellationTokenSource = null;
- }
- }
-
- protected virtual Task CreateReadPackagesTask()
- {
- var loader = currentLoader ?? CreatePackageLoader ();
- cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource ();
- return LoadPackagesAsync (loader, cancellationTokenSource.Token)
- .ContinueWith (t => OnPackagesRead (t, loader), TaskScheduler.FromCurrentSynchronizationContext ());
- }
-
- PackageItemLoader CreatePackageLoader ()
- {
- var context = new PackageLoadContext (
- selectedPackageSource.GetSourceRepositories (),
- false,
- nugetProject);
-
- var loader = new PackageItemLoader (
- context,
- CreatePackageFeed (context.SourceRepositories),
- SearchTerms,
- IncludePrerelease
- );
-
- currentLoader = loader;
-
- return loader;
- }
-
- protected virtual IPackageFeed CreatePackageFeed (IEnumerable<SourceRepository> sourceRepositories)
- {
- return new MultiSourcePackageFeed (sourceRepositories, this);
- }
-
- protected virtual Task LoadPackagesAsync (PackageItemLoader loader, CancellationToken token)
- {
- return Task.Run (async () => {
- await loader.LoadNextAsync (null, token);
-
- while (loader.State.LoadingStatus == LoadingStatus.Loading) {
- token.ThrowIfCancellationRequested ();
- await loader.UpdateStateAsync (null, token);
- }
- });
- }
-
- void ClearError ()
- {
- HasError = false;
- ErrorMessage = String.Empty;
- aggregateErrorMessage = new AggregatePackageSourceErrorMessage (GetTotalPackageSources ());
- }
-
- int GetTotalPackageSources ()
- {
- if (selectedPackageSource != null) {
- return selectedPackageSource.GetSourceRepositories ().Count ();
- }
- return 0;
- }
-
- public void ShowNextPage ()
- {
- IsLoadingNextPage = true;
- StartReadPackagesTask (false);
- base.OnPropertyChanged (null);
- }
-
- void OnPackagesRead (Task task, PackageItemLoader loader)
- {
- IsReadingPackages = false;
- IsLoadingNextPage = false;
- if (task.IsFaulted) {
- SaveError (task.Exception);
- } else if (task.IsCanceled || !IsCurrentQuery (loader)) {
- // Ignore.
- return;
- } else {
- SaveAnyWarnings ();
- UpdatePackagesForSelectedPage (loader);
- }
- base.OnPropertyChanged (null);
- }
-
- bool IsCurrentQuery (PackageItemLoader loader)
- {
- return currentLoader == loader;
- }
-
- void SaveError (AggregateException ex)
- {
- HasError = true;
- ErrorMessage = GetErrorMessage (ex);
- LoggingService.LogInfo ("PackagesViewModel error", ex);
- }
-
- string GetErrorMessage (AggregateException ex)
- {
- var errorMessage = new AggregateExceptionErrorMessage (ex);
- return errorMessage.ToString ();
- }
-
- void SaveAnyWarnings ()
- {
- string warning = GetWarningMessage ();
- if (!String.IsNullOrEmpty (warning)) {
- HasError = true;
- ErrorMessage = warning;
- }
- }
-
- protected virtual string GetWarningMessage ()
- {
- return String.Empty;
- }
-
- void UpdatePackagesForSelectedPage (PackageItemLoader loader)
- {
- HasNextPage = loader.State.LoadingStatus == LoadingStatus.Ready;
-
- UpdatePackageViewModels (loader.GetCurrent ());
- }
-
- void UpdatePackageViewModels (IEnumerable<PackageItemListViewModel> newPackageViewModels)
- {
- var packages = ConvertToPackageViewModels (newPackageViewModels).ToList ();
- packages = PrioritizePackages (packages).ToList ();
- - foreach (PackageSearchResultViewModel packageViewModel in packages) {
- PackageViewModels.Add (packageViewModel);
- }
- }
-
- public IEnumerable<PackageSearchResultViewModel> ConvertToPackageViewModels (IEnumerable<PackageItemListViewModel> itemViewModels)
- {
- foreach (PackageItemListViewModel itemViewModel in itemViewModels) {
- PackageSearchResultViewModel packageViewModel = CreatePackageViewModel (itemViewModel);
- UpdatePackageViewModelIfPreviouslyChecked (packageViewModel);
- yield return packageViewModel;
- }
- }
-
- PackageSearchResultViewModel CreatePackageViewModel (PackageItemListViewModel viewModel)
- {
- return new PackageSearchResultViewModel (this, viewModel);
- }
-
- void ClearPackages ()
- {
- PackageViewModels.Clear();
- }
-
- public void OnPackageCheckedChanged (PackageSearchResultViewModel packageViewModel)
- {
- if (ignorePackageCheckedChanged)
- return;
-
- if (packageViewModel.IsChecked) {
- UncheckExistingCheckedPackageWithDifferentVersion (packageViewModel);
- CheckedPackageViewModels.Add (packageViewModel);
- } else {
- CheckedPackageViewModels.Remove (packageViewModel);
- }
- }
-
- void UpdatePackageViewModelIfPreviouslyChecked (PackageSearchResultViewModel packageViewModel)
- {
- ignorePackageCheckedChanged = true;
- try {
- PackageSearchResultViewModel existingPackageViewModel = GetExistingCheckedPackageViewModel (packageViewModel.Id);
- if (existingPackageViewModel != null) {
- packageViewModel.UpdateFromPreviouslyCheckedViewModel (existingPackageViewModel);
- CheckedPackageViewModels.Remove (existingPackageViewModel);
- CheckedPackageViewModels.Add (packageViewModel);
- }
- } finally {
- ignorePackageCheckedChanged = false;
- }
- }
-
- void UncheckExistingCheckedPackageWithDifferentVersion (PackageSearchResultViewModel packageViewModel)
- {
- PackageSearchResultViewModel existingPackageViewModel = GetExistingCheckedPackageViewModel (packageViewModel.Id);
-
- if (existingPackageViewModel != null) {
- CheckedPackageViewModels.Remove (existingPackageViewModel);
- existingPackageViewModel.IsChecked = false;
- }
- }
-
- PackageSearchResultViewModel GetExistingCheckedPackageViewModel (string packageId)
- {
- return CheckedPackageViewModels
- .FirstOrDefault (item => item.Id == packageId);
- }
-
- public IPackageAction CreateInstallPackageAction (PackageSearchResultViewModel packageViewModel)
- {
- return new InstallNuGetPackageAction (
- SelectedPackageSource.GetSourceRepositories (),
- solutionManager,
- dotNetProject,
- projectContext
- ) {
- IncludePrerelease = IncludePrerelease,
- PackageId = packageViewModel.Id,
- Version = packageViewModel.SelectedVersion
- };
- }
-
- public PackageSearchResultViewModel SelectedPackage { get; set; }
-
- public bool IsOlderPackageInstalled (string id, NuGetVersion version)
- {
- return packageReferences.Any (packageReference => IsOlderPackageInstalled (packageReference, id, version));
- }
-
- bool IsOlderPackageInstalled (PackageReference packageReference, string id, NuGetVersion version)
- {
- return packageReference.PackageIdentity.Id == id &&
- packageReference.PackageIdentity.Version < version;
- }
- - protected virtual Task GetPackagesInstalledInProject ()
- {
- return nugetProject
- .GetInstalledPackagesAsync (CancellationToken.None)
- .ContinueWith (task => OnReadInstalledPackages (task), TaskScheduler.FromCurrentSynchronizationContext ());
- }
-
- void OnReadInstalledPackages (Task<IEnumerable<PackageReference>> task)
- {
- try {
- if (task.IsFaulted) {
- LoggingService.LogError ("Unable to read installed packages.", task.Exception);
- } else {
- packageReferences = task.Result.ToList ();
- }
- } catch (Exception ex) {
- LoggingService.LogError ("OnReadInstalledPackages", ex);
- }
- }
-
- void INuGetUILogger.Log (MessageLevel level, string message, params object [] args)
- {
- if (level == MessageLevel.Error) {
- string fullErrorMessage = String.Format (message, args);
- AppendErrorMessage (fullErrorMessage);
- }
- }
-
- void AppendErrorMessage (string message)
- {
- aggregateErrorMessage.AddError (message);
- ErrorMessage = aggregateErrorMessage.ErrorMessage;
- HasError = true;
- OnPropertyChanged (null);
- }
-
- public void LoadPackageMetadata (PackageSearchResultViewModel packageViewModel) - {
- var provider = new MultiSourcePackageMetadataProvider (
- selectedPackageSource.GetSourceRepositories (),
- packageManager.PackagesFolderSourceRepository,
- packageManager.GlobalPackageFolderRepositories,
- new [] { NuGetProject },
- false,
- new NuGet.Common.NullLogger ());
- - packageViewModel.LoadPackageMetadata (provider, cancellationTokenSource.Token); - } - - public void OnInstallingSelectedPackages () - { - try {
- UpdateRecentPackages ();
- } catch (Exception ex) {
- LoggingService.LogError ("Unable to update recent packages", ex);
- } - }
-
- void UpdateRecentPackages ()
- {
- if (SelectedPackageSource == null)
- return;
-
- if (CheckedPackageViewModels.Any ()) {
- foreach (PackageSearchResultViewModel packageViewModel in CheckedPackageViewModels) {
- recentPackagesRepository.AddPackage (packageViewModel, SelectedPackageSource.Name);
- }
- } else {
- recentPackagesRepository.AddPackage (SelectedPackage, SelectedPackageSource.Name);
- }
- }
-
- IEnumerable<PackageSearchResultViewModel> PrioritizePackages (IEnumerable<PackageSearchResultViewModel> packages)
- {
- var recentPackages = GetRecentPackages ().ToList ();
-
- foreach (PackageSearchResultViewModel package in recentPackages) {
- package.Parent = this;
- package.ResetForRedisplay (IncludePrerelease);
- yield return package;
- }
-
- foreach (PackageSearchResultViewModel package in packages) {
- if (!recentPackages.Contains (package, PackageSearchResultViewModelComparer.Instance)) {
- yield return package;
- }
- }
- }
-
- IEnumerable<PackageSearchResultViewModel> GetRecentPackages ()
- {
- if (PackageViewModels.Count == 0 &&
- String.IsNullOrEmpty (SearchTerms) &&
- selectedPackageSource != null) {
- return recentPackagesRepository.GetPackages (SelectedPackageSource.Name)
- .Where (SelectedVersionMatchesIncludePreleaseFilter);
- }
-
- return Enumerable.Empty<PackageSearchResultViewModel> ();
- }
-
- bool SelectedVersionMatchesIncludePreleaseFilter (PackageSearchResultViewModel package)
- {
- if (package.SelectedVersion.IsPrerelease) {
- return IncludePrerelease;
- }
-
- return true;
- }
- }
-}
-
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/InstallNuGetPackageAction.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/InstallNuGetPackageAction.cs index c3d79c8989..25d00c0ccc 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/InstallNuGetPackageAction.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/InstallNuGetPackageAction.cs @@ -226,6 +226,10 @@ namespace MonoDevelop.PackageManagement return Version?.IsPrerelease == true;
}
+ internal IDotNetProject Project { + get { return dotNetProject; } + }
+
public bool IsForProject (DotNetProject project)
{
return dotNetProject.DotNetProject == project;
diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesPage.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesPage.cs new file mode 100644 index 0000000000..3f2f1cd3bb --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesPage.cs @@ -0,0 +1,36 @@ +// +// ManagePackagesPage.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2016 Xamarin Inc. (http://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. + +namespace MonoDevelop.PackageManagement +{ + enum ManagePackagesPage + { + Browse, + Installed, + Updates, + Consolidate + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesProjectInfo.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesProjectInfo.cs new file mode 100644 index 0000000000..91f32d76b9 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesProjectInfo.cs @@ -0,0 +1,60 @@ +// +// ManagePackagesProjectInfo.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2016 Xamarin Inc. (http://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 System.Collections.Generic; +using System.Linq; +using NuGet.Packaging.Core; +using NuGet.Packaging; +using System.Collections.Immutable; + +namespace MonoDevelop.PackageManagement +{ + class ManagePackagesProjectInfo : IComparable<ManagePackagesProjectInfo> + { + readonly ImmutableArray<PackageReference> packages; + + public ManagePackagesProjectInfo (IDotNetProject project, IEnumerable<PackageReference> packages) + { + Project = project; + this.packages = ImmutableArray<PackageReference>.Empty.AddRange (packages); + } + + public IDotNetProject Project { get; } + + public IEnumerable<PackageIdentity> Packages { + get { return packages.Select (p => p.PackageIdentity); } + } + + public int CompareTo (ManagePackagesProjectInfo other) + { + if (other == null) + return 1; + + return string.Compare (Project.Name, other.Project.Name, StringComparison.CurrentCulture); + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageSearchResultViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesSearchResultViewModel.cs index 2005caeaf0..6aa0d2c97a 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageSearchResultViewModel.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesSearchResultViewModel.cs @@ -1,453 +1,472 @@ -//
-// PackageSearchResultViewModel.cs
-//
-// Author:
-// Matt Ward <matt.ward@xamarin.com>
-//
-// Copyright (c) 2016 Xamarin Inc. (http://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 System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MonoDevelop.Core;
-using NuGet.Common;
-using NuGet.PackageManagement.UI;
-using NuGet.Packaging.Core;
-using NuGet.Protocol.Core.Types;
-using NuGet.Frameworks;
-using NuGet.Versioning;
-
-namespace MonoDevelop.PackageManagement
-{
- internal class PackageSearchResultViewModel : ViewModelBase<PackageSearchResultViewModel>
- {
- AllPackagesViewModel parent;
- PackageItemListViewModel viewModel;
- PackageDetailControlModel packageDetailModel;
- List<PackageDependencyMetadata> dependencies;
- string summary;
- bool isChecked;
-
- public PackageSearchResultViewModel (
- AllPackagesViewModel parent,
- PackageItemListViewModel viewModel)
- {
- this.parent = parent;
- this.viewModel = viewModel;
-
- Versions = new ObservableCollection<NuGetVersion> ();
- SelectedVersion = Version;
- }
-
- public AllPackagesViewModel Parent {
- get { return parent; }
- set { parent = value; }
- }
-
- public string Id {
- get { return viewModel.Id; }
- }
-
- public NuGetVersion Version {
- get { return viewModel.Version; }
- }
-
- public string Title {
- get { return viewModel.Title; }
- }
-
- public string Name {
- get {
- if (String.IsNullOrEmpty (Title))
- return Id;
-
- return Title;
- }
- }
-
- public bool IsChecked {
- get { return isChecked; }
- set {
- if (value != isChecked) {
- isChecked = value;
- parent.OnPackageCheckedChanged (this);
- }
- }
- }
-
- public bool HasLicenseUrl {
- get { return LicenseUrl != null; }
- }
-
- public Uri LicenseUrl {
- get { return viewModel.LicenseUrl; }
- }
-
- public bool HasProjectUrl {
- get { return ProjectUrl != null; }
- }
-
- public Uri ProjectUrl {
- get { return viewModel.ProjectUrl; }
- }
-
- public bool HasGalleryUrl {
- get { return GalleryUrl != null; }
- }
-
- public bool HasNoGalleryUrl {
- get { return !HasGalleryUrl; }
- }
-
- public Uri GalleryUrl {
- get { return null; }
- //get { return viewModel.GalleryUrl; }
- }
-
- public Uri IconUrl {
- get { return viewModel.IconUrl; }
- }
-
- public bool HasIconUrl {
- get { return IconUrl != null; }
- }
-
- public string Author {
- get { return viewModel.Author; }
- }
-
- public string Summary {
- get {
- if (summary == null) {
- summary = StripNewLinesAndIndentation (GetSummaryOrDescription ());
- }
- return summary;
- }
- }
-
- string GetSummaryOrDescription ()
- {
- if (String.IsNullOrEmpty (viewModel.Summary))
- return viewModel.Description;
- return viewModel.Summary;
- }
-
- string StripNewLinesAndIndentation (string text)
- {
- return PackageListViewTextFormatter.Format (text);
- }
-
- public string Description {
- get { return viewModel.Description; }
- }
-
- public bool HasDownloadCount {
- get { return viewModel.DownloadCount >= 0; }
- }
-
- public string GetIdMarkup ()
- {
- return GetBoldText (Id);
- }
-
- public string GetNameMarkup ()
- {
- return GetBoldText (Name);
- }
-
- static string GetBoldText (string text)
- {
- return String.Format ("<b>{0}</b>", text);
- }
-
- public string GetDownloadCountOrVersionDisplayText ()
- {
- if (ShowVersionInsteadOfDownloadCount) {
- return Version.ToString ();
- }
-
- return GetDownloadCountDisplayText ();
- }
-
- public string GetDownloadCountDisplayText ()
- {
- if (HasDownloadCount) {
- return viewModel.DownloadCount.Value.ToString ("N0");
- }
- return String.Empty;
- }
-
- public bool ShowVersionInsteadOfDownloadCount { get; set; }
-
- public DateTimeOffset? LastPublished {
- get { return viewModel.Published; }
- }
-
- public bool HasLastPublished {
- get { return viewModel.Published.HasValue; }
- }
-
- public string GetLastPublishedDisplayText()
- {
- if (HasLastPublished) {
- return LastPublished.Value.Date.ToShortDateString ();
- }
- return String.Empty;
- }
-
- public NuGetVersion SelectedVersion { get; set; }
- public ObservableCollection<NuGetVersion> Versions { get; private set; }
-
- protected virtual Task ReadVersions (CancellationToken cancellationToken)
- {
- try {
- packageDetailModel = new PackageDetailControlModel (parent.NuGetProject);
- packageDetailModel.SelectedVersion = new DisplayVersion (SelectedVersion, null);
- return ReadVersionsFromPackageDetailControlModel (cancellationToken).ContinueWith (
- task => OnVersionsRead (task),
- TaskScheduler.FromCurrentSynchronizationContext ());
- } catch (Exception ex) {
- LoggingService.LogError ("ReadVersions error.", ex);
- }
- return Task.FromResult (0);
- }
-
- Task ReadVersionsFromPackageDetailControlModel (CancellationToken cancellationToken)
- {
- if (!IsRecentPackage) {
- return packageDetailModel.SetCurrentPackage (viewModel);
- }
-
- return ReadVersionsForRecentPackage (cancellationToken);
- }
-
- async Task ReadVersionsForRecentPackage (CancellationToken cancellationToken)
- {
- var identity = new PackageIdentity (viewModel.Id, viewModel.Version);
- foreach (var sourceRepository in parent.SelectedPackageSource.GetSourceRepositories ()) {
- try {
- var metadata = await sourceRepository.GetPackageMetadataAsync (identity, parent.IncludePrerelease, cancellationToken);
- if (metadata != null) {
- var packageViewModel = CreatePackageItemListViewModel (metadata);
- await packageDetailModel.SetCurrentPackage (packageViewModel);
- return;
- }
- } catch (Exception ex) {
- LoggingService.LogError (
- String.Format ("Unable to get metadata for {0} from source {1}.", identity, sourceRepository.PackageSource.Name),
- ex);
- }
- }
- }
-
- PackageItemListViewModel CreatePackageItemListViewModel (IPackageSearchMetadata metadata)
- {
- return new PackageItemListViewModel {
- Id = metadata.Identity.Id,
- Version = metadata.Identity.Version,
- IconUrl = metadata.IconUrl,
- Author = metadata.Authors,
- DownloadCount = metadata.DownloadCount,
- Summary = metadata.Summary,
- Description = metadata.Description,
- Title = metadata.Title,
- LicenseUrl = metadata.LicenseUrl,
- ProjectUrl = metadata.ProjectUrl,
- Published = metadata.Published,
- Versions = AsyncLazy.New (() => metadata.GetVersionsAsync ())
- };
- }
-
- void OnVersionsRead (Task task)
- {
- try {
- if (task.IsFaulted) {
- LoggingService.LogError ("Failed to read package versions.", task.Exception);
- } else if (task.IsCanceled) {
- // Ignore.
- } else {
- Versions.Clear ();
- foreach (NuGetVersion version in packageDetailModel.AllPackageVersions.OrderByDescending (v => v.Version)) {
- Versions.Add (version);
- }
- OnPropertyChanged (viewModel => viewModel.Versions);
- }
- } catch (Exception ex) {
- LoggingService.LogError ("Failed to read package versions.", ex);
- }
- }
-
- public bool IsOlderPackageInstalled ()
- {
- return parent.IsOlderPackageInstalled (Id, SelectedVersion);
- }
-
- public override bool Equals (object obj)
- {
- var other = obj as PackageSearchResultViewModel;
- if (other == null)
- return false;
- - return StringComparer.OrdinalIgnoreCase.Equals (Id, other.Id);
- }
-
- public override int GetHashCode ()
- {
- return Id.GetHashCode ();
- } - - public void UpdateFromPreviouslyCheckedViewModel (PackageSearchResultViewModel packageViewModel) +// +// ManagedPackagesSearchResultViewModel.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2016 Xamarin Inc. (http://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 System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MonoDevelop.Core; +using NuGet.Common; +using NuGet.PackageManagement.UI; +using NuGet.Packaging.Core; +using NuGet.Protocol.Core.Types; +using NuGet.Frameworks; +using NuGet.Versioning; + +namespace MonoDevelop.PackageManagement +{ + internal class ManagePackagesSearchResultViewModel : ViewModelBase<ManagePackagesSearchResultViewModel> + { + ManagePackagesViewModel parent; + PackageItemListViewModel viewModel; + PackageDetailControlModel packageDetailModel; + PackageDependencyMetadata[] dependencies; + string summary; + bool isChecked; + + public ManagePackagesSearchResultViewModel ( + ManagePackagesViewModel parent, + PackageItemListViewModel viewModel) + { + this.parent = parent; + this.viewModel = viewModel; + + Versions = new ObservableCollection<NuGetVersion> (); + SelectedVersion = Version; + } + + public ManagePackagesViewModel Parent { + get { return parent; } + set { parent = value; } + } + + public string Id { + get { return viewModel.Id; } + } + + public NuGetVersion Version { + get { return viewModel.Version; } + } + + public string Title { + get { return viewModel.Title; } + } + + public string Name { + get { + if (String.IsNullOrEmpty (Title)) + return Id; + + return Title; + } + } + + public bool IsChecked { + get { return isChecked; } + set { + if (value != isChecked) { + isChecked = value; + parent.OnPackageCheckedChanged (this); + } + } + } + + public bool HasLicenseUrl { + get { return LicenseUrl != null; } + } + + public Uri LicenseUrl { + get { return viewModel.LicenseUrl; } + } + + public bool HasProjectUrl { + get { return ProjectUrl != null; } + } + + public Uri ProjectUrl { + get { return viewModel.ProjectUrl; } + } + + public bool HasGalleryUrl { + get { return GalleryUrl != null; } + } + + public bool HasNoGalleryUrl { + get { return !HasGalleryUrl; } + } + + public Uri GalleryUrl { + get { return null; } + //get { return viewModel.GalleryUrl; } + } + + public Uri IconUrl { + get { return viewModel.IconUrl; } + } + + public bool HasIconUrl { + get { return IconUrl != null; } + } + + public string Author { + get { return viewModel.Author; } + } + + public string Summary { + get { + if (summary == null) { + summary = StripNewLinesAndIndentation (GetSummaryOrDescription ()); + } + return summary; + } + } + + string GetSummaryOrDescription () + { + if (String.IsNullOrEmpty (viewModel.Summary)) + return viewModel.Description; + return viewModel.Summary; + } + + string StripNewLinesAndIndentation (string text) + { + return PackageListViewTextFormatter.Format (text); + } + + public string Description { + get { return viewModel.Description; } + } + + public bool HasDownloadCount { + get { return viewModel.DownloadCount >= 0; } + } + + public string GetIdMarkup () + { + return GetBoldText (Id); + } + + public string GetNameMarkup () + { + return GetBoldText (Name); + } + + static string GetBoldText (string text) + { + return String.Format ("<b>{0}</b>", text); + } + + public string GetDownloadCountOrVersionDisplayText () + { + if (ShowVersionInsteadOfDownloadCount) { + return Version.ToString (); + } + + return GetDownloadCountDisplayText (); + } + + public string GetDownloadCountDisplayText () + { + if (HasDownloadCount) { + return viewModel.DownloadCount.Value.ToString ("N0"); + } + return String.Empty; + } + + public bool ShowVersionInsteadOfDownloadCount { get; set; } + + public DateTimeOffset? LastPublished { + get { return viewModel.Published; } + } + + public bool HasLastPublished { + get { return viewModel.Published.HasValue; } + } + + public string GetLastPublishedDisplayText() + { + if (HasLastPublished) { + return LastPublished.Value.Date.ToShortDateString (); + } + return String.Empty; + } + + /// <summary> + /// Consolidating a package should select the latest version. + /// </summary> + internal bool SelectLatestVersion { get; set; } + + public NuGetVersion SelectedVersion { get; set; } + public ObservableCollection<NuGetVersion> Versions { get; private set; } + + protected virtual Task ReadVersions (CancellationToken cancellationToken) { + try { + packageDetailModel = new PackageDetailControlModel (parent.NuGetProjects); + packageDetailModel.SelectedVersion = new DisplayVersion (SelectedVersion, null); + return ReadVersionsFromPackageDetailControlModel (cancellationToken).ContinueWith ( + task => OnVersionsRead (task), + cancellationToken, + TaskContinuationOptions.NotOnCanceled, + TaskScheduler.FromCurrentSynchronizationContext ()); + } catch (Exception ex) { + LoggingService.LogError ("ReadVersions error.", ex); + } + return Task.CompletedTask; + } + + Task ReadVersionsFromPackageDetailControlModel (CancellationToken cancellationToken) + { + if (!IsRecentPackage) { + return packageDetailModel.SetCurrentPackage (viewModel); + } + + return ReadVersionsForRecentPackage (cancellationToken); + } + + async Task ReadVersionsForRecentPackage (CancellationToken cancellationToken) + { + var identity = new PackageIdentity (viewModel.Id, viewModel.Version); + foreach (var sourceRepository in parent.SelectedPackageSource.GetSourceRepositories ()) { + try { + var metadata = await sourceRepository.GetPackageMetadataAsync (identity, parent.IncludePrerelease, cancellationToken); + if (metadata != null) { + var packageViewModel = CreatePackageItemListViewModel (metadata); + await packageDetailModel.SetCurrentPackage (packageViewModel); + return; + } + } catch (Exception ex) { + LoggingService.LogError ( + String.Format ("Unable to get metadata for {0} from source {1}.", identity, sourceRepository.PackageSource.Name), + ex); + } + } + } + + PackageItemListViewModel CreatePackageItemListViewModel (IPackageSearchMetadata metadata) + { + return new PackageItemListViewModel { + Id = metadata.Identity.Id, + Version = metadata.Identity.Version, + IconUrl = metadata.IconUrl, + Author = metadata.Authors, + DownloadCount = metadata.DownloadCount, + Summary = metadata.Summary, + Description = metadata.Description, + Title = metadata.Title, + LicenseUrl = metadata.LicenseUrl, + ProjectUrl = metadata.ProjectUrl, + Published = metadata.Published, + Versions = AsyncLazy.New (() => metadata.GetVersionsAsync ()) + }; + } + + void OnVersionsRead (Task task) + { + try { + if (task.IsFaulted) { + LoggingService.LogError ("Failed to read package versions.", task.Exception); + } else if (task.IsCanceled) { + // Ignore. + } else { + Versions.Clear (); + foreach (NuGetVersion version in packageDetailModel.AllPackageVersions.OrderByDescending (v => v.Version)) { + Versions.Add (version); + } + if (SelectLatestVersion && Versions.Any ()) { + SelectedVersion = Versions [0]; + } + OnPropertyChanged (viewModel => viewModel.Versions); + } + } catch (Exception ex) { + LoggingService.LogError ("Failed to read package versions.", ex); + } + } + + public bool IsOlderPackageInstalled () + { + return parent.IsOlderPackageInstalled (Id, SelectedVersion); + } + + public override bool Equals (object obj) + { + var other = obj as ManagePackagesSearchResultViewModel; + if (other == null) + return false; + + return StringComparer.OrdinalIgnoreCase.Equals (Id, other.Id); + } + + public override int GetHashCode () + { + return Id.GetHashCode (); + } + + public void UpdateFromPreviouslyCheckedViewModel (ManagePackagesSearchResultViewModel packageViewModel) + { + SelectLatestVersion = false; IsChecked = packageViewModel.IsChecked; SelectedVersion = packageViewModel.SelectedVersion; if (SelectedVersion != Version) { Versions.Add (Version); Versions.Add (SelectedVersion); } - }
-
- public void LoadPackageMetadata (IPackageMetadataProvider metadataProvider, CancellationToken cancellationToken)
- {
- if (packageDetailModel != null) {
- return;
- }
-
- if (IsRecentPackage) {
- ReadVersions (cancellationToken).ContinueWith (
- task => LoadPackageMetadataFromPackageDetailModel (metadataProvider, cancellationToken),
- TaskScheduler.FromCurrentSynchronizationContext ());
- } else {
- ReadVersions (cancellationToken);
- LoadPackageMetadataFromPackageDetailModel (metadataProvider, cancellationToken);
- }
- }
- - void LoadPackageMetadataFromPackageDetailModel (IPackageMetadataProvider metadataProvider, CancellationToken cancellationToken)
- {
- try {
- LoadPackageMetadataFromPackageDetailModelAsync (metadataProvider, cancellationToken);
- } catch (Exception ex) {
- LoggingService.LogError ("Error getting detailed package metadata.", ex);
- }
- }
-
- protected virtual Task LoadPackageMetadataFromPackageDetailModelAsync (
- IPackageMetadataProvider metadataProvider,
- CancellationToken cancellationToken)
- {
- return packageDetailModel.LoadPackageMetadaAsync (metadataProvider, cancellationToken).ContinueWith (
- task => OnPackageMetadataLoaded (task),
- TaskScheduler.FromCurrentSynchronizationContext ());
- }
-
- void OnPackageMetadataLoaded (Task task)
- {
- try {
- if (task.IsFaulted) {
- LoggingService.LogError ("Failed to read package metadata.", task.Exception);
- } else if (task.IsCanceled) {
- // Ignore.
- } else {
- var metadata = packageDetailModel?.PackageMetadata;
- if (metadata != null) {
- viewModel.Published = metadata.Published;
- dependencies = GetCompatibleDependencies ().ToList ();
- OnPropertyChanged ("Dependencies");
- }
- }
- } catch (Exception ex) {
- LoggingService.LogError ("Failed to read package metadata.", ex);
- }
- }
-
- public bool HasDependencies {
- get { return CompatibleDependencies.Any (); }
- }
-
- public bool HasNoDependencies {
- get { return !HasDependencies; }
- }
-
- public string GetPackageDependenciesDisplayText ()
- {
- var displayText = new StringBuilder ();
- foreach (PackageDependencyMetadata dependency in CompatibleDependencies) {
- displayText.AppendLine (dependency.ToString ());
- }
- return displayText.ToString ();
- }
-
- IEnumerable<PackageDependencyMetadata> CompatibleDependencies {
- get { return dependencies ?? new List<PackageDependencyMetadata> (); }
- }
-
- IEnumerable<PackageDependencyMetadata> GetCompatibleDependencies ()
- {
- var metadata = packageDetailModel?.PackageMetadata;
- if (metadata?.HasDependencies == true) {
- var projectTargetFramework = new ProjectTargetFramework (parent.Project);
- var targetFramework = NuGetFramework.Parse (projectTargetFramework.TargetFrameworkName.FullName);
-
- foreach (var dependencySet in packageDetailModel.PackageMetadata.DependencySets) {
- if (DefaultCompatibilityProvider.Instance.IsCompatible (targetFramework, dependencySet.TargetFramework)) {
- return dependencySet.Dependencies;
- }
- }
- }
-
- return Enumerable.Empty<PackageDependencyMetadata> ();
- }
-
- public bool IsDependencyInformationAvailable {
- get { return dependencies != null; }
- }
-
- public bool IsRecentPackage { get; set; }
-
- public void ResetDetailedPackageMetadata ()
- {
- packageDetailModel = null;
+ } + + public void LoadPackageMetadata (IPackageMetadataProvider metadataProvider, CancellationToken cancellationToken) + { + if (packageDetailModel != null) { + return; + } + + if (IsRecentPackage) { + ReadVersions (cancellationToken).ContinueWith ( + task => LoadPackageMetadataFromPackageDetailModel (metadataProvider, cancellationToken), + TaskScheduler.FromCurrentSynchronizationContext ()); + } else { + ReadVersions (cancellationToken); + LoadPackageMetadataFromPackageDetailModel (metadataProvider, cancellationToken); + } + } + + void LoadPackageMetadataFromPackageDetailModel (IPackageMetadataProvider metadataProvider, CancellationToken cancellationToken) + { + try { + LoadPackageMetadataFromPackageDetailModelAsync (metadataProvider, cancellationToken); + } catch (Exception ex) { + LoggingService.LogError ("Error getting detailed package metadata.", ex); + } + } + + protected virtual Task LoadPackageMetadataFromPackageDetailModelAsync ( + IPackageMetadataProvider metadataProvider, + CancellationToken cancellationToken) + { + return packageDetailModel.LoadPackageMetadaAsync (metadataProvider, cancellationToken).ContinueWith ( + task => OnPackageMetadataLoaded (task), + TaskScheduler.FromCurrentSynchronizationContext ()); + } + + void OnPackageMetadataLoaded (Task task) + { + try { + if (task.IsFaulted) { + LoggingService.LogError ("Failed to read package metadata.", task.Exception); + } else { + var metadata = packageDetailModel?.PackageMetadata; + if (metadata != null) { + viewModel.Published = metadata.Published; + dependencies = GetCompatibleDependencies ().ToArray (); + OnPropertyChanged ("Dependencies"); + } + } + } catch (Exception ex) { + LoggingService.LogError ("Failed to read package metadata.", ex); + } + } + + public bool HasDependencies { + get { return CompatibleDependencies.Any (); } + } + + public bool HasNoDependencies { + get { return !HasDependencies; } + } + + public string GetPackageDependenciesDisplayText () + { + var displayText = StringBuilderCache.Allocate (); + foreach (PackageDependencyMetadata dependency in CompatibleDependencies) { + displayText.AppendLine (dependency.ToString ()); + } + return StringBuilderCache.ReturnAndFree (displayText); + } + + IEnumerable<PackageDependencyMetadata> CompatibleDependencies { + get { return dependencies ?? Array.Empty<PackageDependencyMetadata> (); } + } + + IEnumerable<PackageDependencyMetadata> GetCompatibleDependencies () + { + var metadata = packageDetailModel?.PackageMetadata; + if (metadata?.HasDependencies == true) { + var projectTargetFramework = new ProjectTargetFramework (parent.Project); + var targetFramework = NuGetFramework.Parse (projectTargetFramework.TargetFrameworkName.FullName); + + foreach (var dependencySet in packageDetailModel.PackageMetadata.DependencySets) { + if (DefaultCompatibilityProvider.Instance.IsCompatible (targetFramework, dependencySet.TargetFramework)) { + return dependencySet.Dependencies; + } + } + } + + return Enumerable.Empty<PackageDependencyMetadata> (); + } + + public bool IsDependencyInformationAvailable { + get { return dependencies != null; } + } + + public bool IsRecentPackage { get; set; } + + public void ResetDetailedPackageMetadata () + { + packageDetailModel = null; } public void ResetForRedisplay (bool includePrereleaseVersions) { - ResetDetailedPackageMetadata ();
- IsChecked = false;
- if (!includePrereleaseVersions) {
- RemovePrereleaseVersions ();
+ ResetDetailedPackageMetadata (); + IsChecked = false; + if (!includePrereleaseVersions) { + RemovePrereleaseVersions (); } } void RemovePrereleaseVersions () { - var prereleaseVersions = Versions.Where (version => version.IsPrerelease).ToArray ();
-
- foreach (NuGetVersion prereleaseVersion in prereleaseVersions) {
- Versions.Remove (prereleaseVersion);
+ var prereleaseVersions = Versions.Where (version => version.IsPrerelease).ToArray (); + + foreach (NuGetVersion prereleaseVersion in prereleaseVersions) { + Versions.Remove (prereleaseVersion); } - }
- }
-}
-
+ } + + public string GetCurrentPackageVersionText () + { + return parent.GetCurrentPackageVersionText (Id); + } + + public string GetCurrentPackageVersionAdditionalText () + { + return parent.GetCurrentPackageVersionAdditionalText (Id); + } + } +} + diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesViewModel.cs new file mode 100644 index 0000000000..02637c476a --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagePackagesViewModel.cs @@ -0,0 +1,1019 @@ +// +// ManagePackagesViewModel.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2016 Xamarin Inc. (http://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 System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MonoDevelop.Core; +using MonoDevelop.Ide; +using NuGet.Common; +using NuGet.Configuration; +using NuGet.PackageManagement; +using NuGet.PackageManagement.UI; +using NuGet.Packaging.Core; +using NuGet.ProjectManagement; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; + +namespace MonoDevelop.PackageManagement +{ + internal class ManagePackagesViewModel : ViewModelBase<ManagePackagesViewModel>, INuGetUILogger + { + SourceRepositoryViewModel selectedPackageSource; + ManagePackagesSearchResultViewModel selectedPackage; + ManagePackagesPage pageSelected; + IPackageSourceProvider packageSourceProvider; + PackageItemLoader currentLoader; + CancellationTokenSource cancellationTokenSource; + List<SourceRepositoryViewModel> packageSources; + bool includePrerelease; + bool ignorePackageCheckedChanged; + IMonoDevelopSolutionManager solutionManager; + List<NuGetProject> nugetProjects; + List<IDotNetProject> dotNetProjects; + INuGetProjectContext projectContext; + AggregatePackageSourceErrorMessage aggregateErrorMessage; + NuGetPackageManager packageManager; + RecentManagedNuGetPackagesRepository recentPackagesRepository; + List<ManagePackagesProjectInfo> projectInformation = new List<ManagePackagesProjectInfo> (); + Dictionary<string, List<ManageProjectViewModel>> cachedProjectViewModels = + new Dictionary<string, List<ManageProjectViewModel>> (StringComparer.OrdinalIgnoreCase); + + public static ManagePackagesViewModel Create ( + RecentManagedNuGetPackagesRepository recentPackagesRepository, + IDotNetProject project) + { + var solutionManager = PackageManagementServices.Workspace.GetSolutionManager (IdeApp.ProjectOperations.CurrentSelectedSolution); + var solution = new SolutionProxy (IdeApp.ProjectOperations.CurrentSelectedSolution); + return new ManagePackagesViewModel (solutionManager, solution, recentPackagesRepository, project); + } + + public ManagePackagesViewModel ( + IMonoDevelopSolutionManager solutionManager, + ISolution solution, + RecentManagedNuGetPackagesRepository recentPackagesRepository, + IDotNetProject project) + : this ( + solutionManager, + solution, + new NuGetProjectContext (solutionManager.Settings), + recentPackagesRepository, + project) + { + } + + public ManagePackagesViewModel ( + IMonoDevelopSolutionManager solutionManager, + ISolution solution, + INuGetProjectContext projectContext, + RecentManagedNuGetPackagesRepository recentPackagesRepository, + IDotNetProject project) + { + this.solutionManager = solutionManager; + this.projectContext = projectContext; + this.recentPackagesRepository = recentPackagesRepository; + IsManagingSolution = project == null; + PackageViewModels = new ObservableCollection<ManagePackagesSearchResultViewModel> (); + CheckedPackageViewModels = new ObservableCollection<ManagePackagesSearchResultViewModel> (); + ProjectViewModels = new ObservableCollection<ManageProjectViewModel> (); + ErrorMessage = String.Empty; + PageSelected = ManagePackagesPage.Browse; + + if (project != null) { + dotNetProjects = new List<IDotNetProject> (); + dotNetProjects.Add (project); + } else { + dotNetProjects = solution.GetAllProjects ().ToList (); + } + + packageManager = new NuGetPackageManager ( + solutionManager.CreateSourceRepositoryProvider (), + solutionManager.Settings, + solutionManager, + new DeleteOnRestartManager () + ); + + nugetProjects = dotNetProjects + .Select (dotNetProject => solutionManager.GetNuGetProject (dotNetProject)) + .ToList (); + + GetPackagesInstalledInProjects ().Ignore (); + } + + public bool IsManagingSolution { get; private set; } + + public IEnumerable<NuGetProject> NuGetProjects { + get { return nugetProjects; } + } + + public IDotNetProject Project { + get { return dotNetProjects[0]; } + } + + public IEnumerable<IDotNetProject> DotNetProjects { + get { return dotNetProjects; } + } + + public string SearchTerms { get; set; } + + public ManagePackagesPage PageSelected { + get { return pageSelected; } + set { + if (pageSelected != value) { + pageSelected = value; + CheckedPackageViewModels.Clear (); + cachedProjectViewModels.Clear (); + } + } + } + + public bool IsConsolidatePageSelected { + get { return PageSelected == ManagePackagesPage.Consolidate; } + } + + public bool IsInstalledPageSelected { + get { return PageSelected == ManagePackagesPage.Installed; } + } + + public bool IsBrowsePageSelected { + get { return PageSelected == ManagePackagesPage.Browse; } + } + + public bool IsUpdatesPageSelected { + get { return PageSelected == ManagePackagesPage.Updates; } + } + + public IEnumerable<SourceRepositoryViewModel> PackageSources { + get { + if (packageSources == null) { + packageSources = GetPackageSources ().ToList (); + } + return packageSources; + } + } + + IEnumerable<SourceRepositoryViewModel> GetPackageSources () + { + ISourceRepositoryProvider provider = solutionManager.CreateSourceRepositoryProvider (); + packageSourceProvider = provider.PackageSourceProvider; + var repositories = provider.GetRepositories ().ToList (); + + if (repositories.Count > 1) { + // All Sources - first package source if there are multiple sources. + yield return new AggregateSourceRepositoryViewModel (repositories); + } + + foreach (SourceRepository repository in repositories) { + yield return new SourceRepositoryViewModel (repository); + } + } + + public SourceRepositoryViewModel SelectedPackageSource { + get { + if (selectedPackageSource == null) { + selectedPackageSource = GetActivePackageSource (); + } + return selectedPackageSource; + } + set { + if (selectedPackageSource != value) { + selectedPackageSource = value; + SaveActivePackageSource (); + ReadPackages (); + OnPropertyChanged (null); + } + } + } + + SourceRepositoryViewModel GetActivePackageSource () + { + if (packageSources == null) + return null; + + if (!String.IsNullOrEmpty (packageSourceProvider.ActivePackageSourceName)) { + SourceRepositoryViewModel packageSource = packageSources + .FirstOrDefault (viewModel => String.Equals (viewModel.PackageSource.Name, packageSourceProvider.ActivePackageSourceName, StringComparison.CurrentCultureIgnoreCase)); + if (packageSource != null) { + return packageSource; + } + } + + return packageSources.FirstOrDefault (packageSource => !packageSource.IsAggregate); + } + + void SaveActivePackageSource () + { + if (selectedPackageSource == null || packageSourceProvider == null) + return; + + packageSourceProvider.SaveActivePackageSource (selectedPackageSource.PackageSource); + } + + public ObservableCollection<ManagePackagesSearchResultViewModel> PackageViewModels { get; private set; } + public ObservableCollection<ManagePackagesSearchResultViewModel> CheckedPackageViewModels { get; private set; } + public ObservableCollection<ManageProjectViewModel> ProjectViewModels { get; private set; } + + public bool HasError { get; private set; } + public string ErrorMessage { get; private set; } + + public bool IsLoadingNextPage { get; private set; } + public bool IsReadingPackages { get; private set; } + public bool HasNextPage { get; private set; } + + public bool IncludePrerelease { + get { return includePrerelease; } + set { + if (includePrerelease != value) { + includePrerelease = value; + ReadPackages (); + OnPropertyChanged (null); + } + } + } + + public void Dispose() + { + OnDispose (); + CancelReadPackagesTask (); + IsDisposed = true; + } + + protected virtual void OnDispose() + { + } + + public bool IsDisposed { get; private set; } + + public void Search () + { + ReadPackages (); + OnPropertyChanged (null); + } + + public void ReadPackages () + { + if (SelectedPackageSource == null) { + return; + } + + HasNextPage = false; + IsLoadingNextPage = false; + currentLoader = null; + StartReadPackagesTask (); + } + + void StartReadPackagesTask (bool clearPackages = true) + { + IsReadingPackages = true; + ClearError (); + if (clearPackages) { + CancelReadPackagesTask (); + ClearPackages (); + } + CreateReadPackagesTask (); + } + + void CancelReadPackagesTask() + { + if (cancellationTokenSource != null) { + // Cancel on another thread since CancellationTokenSource.Cancel can sometimes + // take up to a second on Mono and we do not want to block the UI thread. + var tokenSource = cancellationTokenSource; + Task.Run (() => { + try { + tokenSource.Cancel (); + tokenSource.Dispose (); + } catch (Exception ex) { + LoggingService.LogError ("Unable to cancel task.", ex); + } + }); + cancellationTokenSource = null; + } + } + + protected virtual Task CreateReadPackagesTask() + { + var loader = currentLoader ?? CreatePackageLoader (); + cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource (); + return LoadPackagesAsync (loader, cancellationTokenSource.Token) + .ContinueWith (t => OnPackagesRead (t, loader), TaskScheduler.FromCurrentSynchronizationContext ()); + } + + PackageItemLoader CreatePackageLoader () + { + var context = new PackageLoadContext ( + selectedPackageSource.GetSourceRepositories (), + true, + nugetProjects); + + var loader = new PackageItemLoader ( + context, + CreatePackageFeed (context), + SearchTerms, + IncludePrerelease + ); + + currentLoader = loader; + + return loader; + } + + protected virtual IPackageFeed CreatePackageFeed (PackageLoadContext context) + { + if (PageSelected == ManagePackagesPage.Browse) + return new MultiSourcePackageFeed (context.SourceRepositories, this); + + if (PageSelected == ManagePackagesPage.Installed) + return new InstalledPackageFeed (context, CreatePackageMetadataProvider (), new NullLogger ()); + + if (PageSelected == ManagePackagesPage.Updates) { + return new UpdatePackageFeed ( + context, + CreatePackageMetadataProvider (), + new PackageSearchMetadataCache (), + new NullLogger ()); + } + + if (PageSelected == ManagePackagesPage.Consolidate) + return new ConsolidatePackageFeed (context, CreatePackageMetadataProvider (), new NullLogger ()); + + throw new InvalidOperationException ("Unsupported package feed"); + } + + protected virtual Task LoadPackagesAsync (PackageItemLoader loader, CancellationToken token) + { + return Task.Run (async () => { + await loader.LoadNextAsync (null, token); + + while (loader.State.LoadingStatus == LoadingStatus.Loading) { + token.ThrowIfCancellationRequested (); + await loader.UpdateStateAsync (null, token); + } + }); + } + + void ClearError () + { + HasError = false; + ErrorMessage = String.Empty; + aggregateErrorMessage = new AggregatePackageSourceErrorMessage (GetTotalPackageSources ()); + } + + int GetTotalPackageSources () + { + if (selectedPackageSource != null) { + return selectedPackageSource.GetSourceRepositories ().Count (); + } + return 0; + } + + public void ShowNextPage () + { + IsLoadingNextPage = true; + StartReadPackagesTask (false); + base.OnPropertyChanged (null); + } + + void OnPackagesRead (Task task, PackageItemLoader loader) + { + IsReadingPackages = false; + IsLoadingNextPage = false; + if (task.IsFaulted) { + SaveError (task.Exception); + } else if (task.IsCanceled || !IsCurrentQuery (loader)) { + // Ignore. + return; + } else { + SaveAnyWarnings (); + UpdatePackagesForSelectedPage (loader); + } + base.OnPropertyChanged (null); + } + + bool IsCurrentQuery (PackageItemLoader loader) + { + return currentLoader == loader; + } + + void SaveError (AggregateException ex) + { + HasError = true; + ErrorMessage = GetErrorMessage (ex); + LoggingService.LogInfo ("PackagesViewModel error", ex); + } + + string GetErrorMessage (AggregateException ex) + { + var errorMessage = new AggregateExceptionErrorMessage (ex); + return errorMessage.ToString (); + } + + void SaveAnyWarnings () + { + string warning = GetWarningMessage (); + if (!String.IsNullOrEmpty (warning)) { + HasError = true; + ErrorMessage = warning; + } + } + + protected virtual string GetWarningMessage () + { + return String.Empty; + } + + void UpdatePackagesForSelectedPage (PackageItemLoader loader) + { + HasNextPage = loader.State.LoadingStatus == LoadingStatus.Ready; + + UpdatePackageViewModels (loader.GetCurrent ()); + } + + void UpdatePackageViewModels (IEnumerable<PackageItemListViewModel> newPackageViewModels) + { + var packages = PrioritizePackages (ConvertToPackageViewModels (newPackageViewModels)).ToList (); + + foreach (ManagePackagesSearchResultViewModel packageViewModel in packages) { + PackageViewModels.Add (packageViewModel); + } + } + + public IEnumerable<ManagePackagesSearchResultViewModel> ConvertToPackageViewModels (IEnumerable<PackageItemListViewModel> itemViewModels) + { + foreach (PackageItemListViewModel itemViewModel in itemViewModels) { + ManagePackagesSearchResultViewModel packageViewModel = CreatePackageViewModel (itemViewModel); + UpdatePackageViewModelIfPreviouslyChecked (packageViewModel); + yield return packageViewModel; + } + } + + ManagePackagesSearchResultViewModel CreatePackageViewModel (PackageItemListViewModel viewModel) + { + bool showVersion = ShowPackageVersionInsteadOfDownloadCount (); + return new ManagePackagesSearchResultViewModel (this, viewModel) { + ShowVersionInsteadOfDownloadCount = showVersion, + SelectLatestVersion = IsConsolidatePageSelected + }; + } + + bool ShowPackageVersionInsteadOfDownloadCount () + { + return PageSelected != ManagePackagesPage.Browse; + } + + void ClearPackages () + { + PackageViewModels.Clear(); + } + + public void OnPackageCheckedChanged (ManagePackagesSearchResultViewModel packageViewModel) + { + if (ignorePackageCheckedChanged) + return; + + if (packageViewModel.IsChecked) { + UncheckExistingCheckedPackageWithDifferentVersion (packageViewModel); + CheckedPackageViewModels.Add (packageViewModel); + } else { + CheckedPackageViewModels.Remove (packageViewModel); + } + } + + void UpdatePackageViewModelIfPreviouslyChecked (ManagePackagesSearchResultViewModel packageViewModel) + { + ignorePackageCheckedChanged = true; + try { + ManagePackagesSearchResultViewModel existingPackageViewModel = GetExistingCheckedPackageViewModel (packageViewModel.Id); + if (existingPackageViewModel != null) { + packageViewModel.UpdateFromPreviouslyCheckedViewModel (existingPackageViewModel); + CheckedPackageViewModels.Remove (existingPackageViewModel); + CheckedPackageViewModels.Add (packageViewModel); + } + } finally { + ignorePackageCheckedChanged = false; + } + } + + void UncheckExistingCheckedPackageWithDifferentVersion (ManagePackagesSearchResultViewModel packageViewModel) + { + ManagePackagesSearchResultViewModel existingPackageViewModel = GetExistingCheckedPackageViewModel (packageViewModel.Id); + + if (existingPackageViewModel != null) { + CheckedPackageViewModels.Remove (existingPackageViewModel); + existingPackageViewModel.IsChecked = false; + } + } + + ManagePackagesSearchResultViewModel GetExistingCheckedPackageViewModel (string packageId) + { + return CheckedPackageViewModels + .FirstOrDefault (item => item.Id == packageId); + } + + public IEnumerable<IPackageAction> CreateInstallPackageActions ( + ManagePackagesSearchResultViewModel packageViewModel, + IEnumerable<IDotNetProject> projects) + { + bool firstInstall = true; + foreach (IDotNetProject project in projects) { + yield return new InstallNuGetPackageAction ( + SelectedPackageSource.GetSourceRepositories (), + solutionManager, + project, + projectContext + ) { + IncludePrerelease = IncludePrerelease, + PackageId = packageViewModel.Id, + Version = packageViewModel.SelectedVersion, + LicensesMustBeAccepted = firstInstall + }; + + firstInstall = false; + } + } + + public IEnumerable<IPackageAction> CreateUninstallPackageActions ( + ManagePackagesSearchResultViewModel packageViewModel, + IEnumerable<IDotNetProject> projects) + { + foreach (IDotNetProject project in projects) { + if (IsPackageInstalledInProject (project, packageViewModel.Id)) { + yield return new UninstallNuGetPackageAction ( + solutionManager, + project + ) { + PackageId = packageViewModel.Id, + }; + } + } + } + + public IEnumerable<IPackageAction> CreateUpdatePackageActions ( + ManagePackagesSearchResultViewModel packageViewModel, + IEnumerable<IDotNetProject> projects) + { + bool firstInstall = true; + foreach (IDotNetProject project in projects) { + if (IsPackageInstalledInProject (project, packageViewModel.Id)) { + yield return new InstallNuGetPackageAction ( + SelectedPackageSource.GetSourceRepositories (), + solutionManager, + project, + projectContext + ) { + IncludePrerelease = IncludePrerelease, + PackageId = packageViewModel.Id, + Version = packageViewModel.SelectedVersion, + LicensesMustBeAccepted = firstInstall + }; + + firstInstall = false; + } + } + } + + public IEnumerable<IPackageAction> CreateConsolidatePackageActions ( + ManagePackagesSearchResultViewModel packageViewModel) + { + bool firstInstall = true; + foreach (ManageProjectViewModel project in GetProjectViewModelsForPackage (packageViewModel)) { + if (!CanConsolidate (project, packageViewModel)) { + continue; + } + + yield return new InstallNuGetPackageAction ( + SelectedPackageSource.GetSourceRepositories (), + solutionManager, + project.Project, + projectContext + ) { + IncludePrerelease = IncludePrerelease, + PackageId = packageViewModel.Id, + Version = packageViewModel.SelectedVersion, + LicensesMustBeAccepted = firstInstall + }; + + firstInstall = false; + } + } + + public List<IPackageAction> CreatePackageActions ( + IEnumerable<ManagePackagesSearchResultViewModel> packageViewModels, + IEnumerable<IDotNetProject> selectedProjects) + { + if (PageSelected == ManagePackagesPage.Browse) { + return CreatePackageActions (packageViewModels, selectedProjects, CreateInstallPackageActions); + } else if (PageSelected == ManagePackagesPage.Installed) { + return CreatePackageActions (packageViewModels, selectedProjects, CreateUninstallPackageActions); + } else if (PageSelected == ManagePackagesPage.Updates) { + return CreatePackageActions (packageViewModels, selectedProjects, CreateUpdatePackageActions); + } + return null; + } + + List<IPackageAction> CreatePackageActions ( + IEnumerable<ManagePackagesSearchResultViewModel> packageViewModels, + IEnumerable<IDotNetProject> selectedProjects, + Func<ManagePackagesSearchResultViewModel, IEnumerable<IDotNetProject>, IEnumerable<IPackageAction>> transform) + { + var actions = new List<IPackageAction> (); + foreach (var packageViewModel in packageViewModels) { + actions.AddRange (transform (packageViewModel, selectedProjects)); + } + return actions; + } + + public List<IPackageAction> CreateConsolidatePackageActions (IEnumerable<ManagePackagesSearchResultViewModel> packageViewModels) + { + var actions = new List<IPackageAction> (); + foreach (var packageViewModel in packageViewModels) { + actions.AddRange (CreateConsolidatePackageActions (packageViewModel)); + } + return actions; + } + + bool IsPackageInstalledInProject (IDotNetProject project, string packageId) + { + var matchedProjectInfo = projectInformation.FirstOrDefault (p => p.Project == project); + return matchedProjectInfo.Packages.Any (package => StringComparer.OrdinalIgnoreCase.Equals (packageId, package.Id)); + } + + bool CanConsolidate (ManageProjectViewModel projectViewModel, ManagePackagesSearchResultViewModel packageViewModel) + { + if (!projectViewModel.IsChecked) + return false; + + if (IsPackageInstalledInProjectWithSameVersion ( + projectViewModel.ProjectInfo, + packageViewModel.Id, + packageViewModel.SelectedVersion)) { + return false; + } + + return true; + } + + bool IsPackageInstalledInProjectWithSameVersion (ManagePackagesProjectInfo projectInfo, string packageId, NuGetVersion version) + { + var matchPackageId = new PackageIdentity (packageId, version); + return projectInfo.Packages.Any (package => PackageIdentityComparer.Default.Equals (package, matchPackageId)); + } + + public ManagePackagesSearchResultViewModel SelectedPackage { + get { return selectedPackage; } + set { + if (selectedPackage != value) { + selectedPackage = value; + OnSelectedPackageChanged (); + } + } + } + + public bool IsOlderPackageInstalled (string id, NuGetVersion version) + { + foreach (ManagePackagesProjectInfo projectInfo in projectInformation) { + foreach (PackageIdentity package in projectInfo.Packages) { + if (IsOlderPackageInstalled (package, id, version)) { + return true; + } + } + } + return false; + } + + bool IsOlderPackageInstalled (PackageIdentity packageIdentity, string id, NuGetVersion version) + { + return packageIdentity.Id == id && + packageIdentity.Version < version; + } + + protected virtual Task GetPackagesInstalledInProjects () + { + return GetInstalledPackagesProjectInfo () + .ContinueWith (task => OnReadInstalledPackages (task), TaskScheduler.FromCurrentSynchronizationContext ()); + } + + async Task<List<ManagePackagesProjectInfo>> GetInstalledPackagesProjectInfo () + { + var projectInfo = new List<ManagePackagesProjectInfo> (); + for (int i = 0; i < nugetProjects.Count; ++i) { + var nugetProject = nugetProjects[i]; + var packages = await nugetProject.GetInstalledPackagesAsync (CancellationToken.None); + projectInfo.Add (new ManagePackagesProjectInfo (dotNetProjects[i], packages)); + } + projectInfo.Sort (); + return projectInfo; + } + + void OnReadInstalledPackages (Task<List<ManagePackagesProjectInfo>> task) + { + try { + if (task.IsFaulted) { + LoggingService.LogError ("Unable to read installed packages.", task.Exception); + } else { + projectInformation = task.Result; + } + } catch (Exception ex) { + LoggingService.LogError ("OnReadInstalledPackages", ex); + } + } + + void INuGetUILogger.Log (MessageLevel level, string message, params object [] args) + { + if (level == MessageLevel.Error) { + string fullErrorMessage = String.Format (message, args); + AppendErrorMessage (fullErrorMessage); + } + } + + void AppendErrorMessage (string message) + { + aggregateErrorMessage.AddError (message); + ErrorMessage = aggregateErrorMessage.ErrorMessage; + HasError = true; + OnPropertyChanged (null); + } + + public void LoadPackageMetadata (ManagePackagesSearchResultViewModel packageViewModel) + { + IPackageMetadataProvider provider = CreatePackageMetadataProvider (); + + packageViewModel.LoadPackageMetadata (provider, cancellationTokenSource.Token); + } + + IPackageMetadataProvider CreatePackageMetadataProvider () + { + return new MultiSourcePackageMetadataProvider ( + selectedPackageSource.GetSourceRepositories (), + packageManager.PackagesFolderSourceRepository, + packageManager.GlobalPackageFolderRepositories, + nugetProjects.ToArray (), + IsManagingSolution, + new NullLogger ()); + } + + public void OnInstallingSelectedPackages () + { + try { + UpdateRecentPackages (); + } catch (Exception ex) { + LoggingService.LogError ("Unable to update recent packages", ex); + } + } + + void UpdateRecentPackages () + { + if (SelectedPackageSource == null) + return; + + if (CheckedPackageViewModels.Any ()) { + foreach (ManagePackagesSearchResultViewModel packageViewModel in CheckedPackageViewModels) { + recentPackagesRepository.AddPackage (packageViewModel, SelectedPackageSource.Name); + } + } else { + recentPackagesRepository.AddPackage (SelectedPackage, SelectedPackageSource.Name); + } + } + + IEnumerable<ManagePackagesSearchResultViewModel> PrioritizePackages (IEnumerable<ManagePackagesSearchResultViewModel> packages) + { + var recentPackages = GetRecentPackages ().ToList (); + + foreach (ManagePackagesSearchResultViewModel package in recentPackages) { + package.Parent = this; + package.ResetForRedisplay (IncludePrerelease); + yield return package; + } + + foreach (ManagePackagesSearchResultViewModel package in packages) { + if (!recentPackages.Contains (package, ManagedPackagesSearchResultViewModelComparer.Instance)) { + yield return package; + } + } + } + + IEnumerable<ManagePackagesSearchResultViewModel> GetRecentPackages () + { + if (PackageViewModels.Count == 0 && + String.IsNullOrEmpty (SearchTerms) && + selectedPackageSource != null && + PageSelected == ManagePackagesPage.Browse) { + return recentPackagesRepository.GetPackages (SelectedPackageSource.Name) + .Where (SelectedVersionMatchesIncludePreleaseFilter); + } + + return Enumerable.Empty<ManagePackagesSearchResultViewModel> (); + } + + bool SelectedVersionMatchesIncludePreleaseFilter (ManagePackagesSearchResultViewModel package) + { + if (package.SelectedVersion.IsPrerelease) { + return IncludePrerelease; + } + + return true; + } + + public IEnumerable<IDotNetProject> GetDotNetProjectsToSelect (IEnumerable<string> packageIds) + { + if (PageSelected == ManagePackagesPage.Browse) { + return dotNetProjects; + } + + return GetFilteredProjectsToSelect (packageIds); + } + + IEnumerable<IDotNetProject> GetFilteredProjectsToSelect (IEnumerable<string> packageIds) + { + foreach (IDotNetProject project in dotNetProjects) { + foreach (var projectInfo in projectInformation) { + if (projectInfo.Project != project) + continue; + + foreach (PackageIdentity package in projectInfo.Packages) { + foreach (string packageId in packageIds) { + if (StringComparer.OrdinalIgnoreCase.Equals (package.Id, packageId)) { + yield return project; + } + } + } + } + } + } + + void OnSelectedPackageChanged () + { + if (!IsConsolidatePageSelected) { + return; + } + + ProjectViewModels.Clear (); + + if (SelectedPackage == null) { + return; + } + + List<ManageProjectViewModel> projectViewModels; + if (!cachedProjectViewModels.TryGetValue (SelectedPackage.Id, out projectViewModels)) { + projectViewModels = new List<ManageProjectViewModel> (); + var uncheckedProjectViewModels = new List<ManageProjectViewModel> (); + foreach (ManagePackagesProjectInfo projectInfo in projectInformation) { + var projectViewModel = new ManageProjectViewModel (projectInfo, SelectedPackage.Id); + if (projectViewModel.IsChecked) + projectViewModels.Add (projectViewModel); + else + uncheckedProjectViewModels.Add (projectViewModel); + } + // Show projects that have the package installed before the other projects. + projectViewModels.AddRange (uncheckedProjectViewModels); + + cachedProjectViewModels [SelectedPackage.Id] = projectViewModels; + } + + foreach (ManageProjectViewModel projectViewModel in projectViewModels) { + ProjectViewModels.Add (projectViewModel); + } + } + + public bool CanConsolidate () + { + if (!IsConsolidatePageSelected) { + return false; + } + + if (CheckedPackageViewModels.Count == 0) { + return CanConsolidateSelectedPackage (); + } + + return CanConsolidateCheckedPackages (); + } + + bool CanConsolidateSelectedPackage () + { + if (SelectedPackage == null) { + return false; + } + + foreach (ManageProjectViewModel projectViewModel in ProjectViewModels) { + if (CanConsolidate (projectViewModel, SelectedPackage)) { + return true; + } + } + + return false; + } + + bool CanConsolidateCheckedPackages () + { + foreach (ManagePackagesSearchResultViewModel packageViewModel in CheckedPackageViewModels) { + foreach (ManageProjectViewModel projectViewModel in GetProjectViewModelsForPackage (packageViewModel)) { + if (CanConsolidate (projectViewModel, packageViewModel)) { + return true; + } + } + } + + return false; + } + + IEnumerable<ManageProjectViewModel> GetProjectViewModelsForPackage (ManagePackagesSearchResultViewModel packageViewModel) + { + List<ManageProjectViewModel> projectViewModels; + if (cachedProjectViewModels.TryGetValue (packageViewModel.Id, out projectViewModels)) { + return projectViewModels; + } + return Enumerable.Empty<ManageProjectViewModel> (); + } + + public string GetCurrentPackageVersionText (string packageId) + { + PackageIdentity currentPackage = null; + foreach (ManagePackagesProjectInfo projectInfo in projectInformation) { + foreach (PackageIdentity package in projectInfo.Packages) { + if (IsPackageIdMatch (package, packageId)) { + if (currentPackage == null) { + currentPackage = package; + } else { + return GettextCatalog.GetString ("Multiple"); + } + } + } + } + + if (currentPackage != null) { + return currentPackage.Version.ToString (); + } + + return string.Empty; + } + + static bool IsPackageIdMatch (PackageIdentity package, string packageId) + { + return StringComparer.OrdinalIgnoreCase.Equals (package.Id, packageId); + } + + public string GetCurrentPackageVersionAdditionalText (string packageId) + { + if (!IsManagingSolution) + return string.Empty; + + StringBuilder additionalText = null; + (PackageIdentity package, ManagePackagesProjectInfo projectInfo) previousMatch = (null, null); + foreach (ManagePackagesProjectInfo projectInfo in projectInformation) { + PackageIdentity foundPackage = projectInfo.Packages.FirstOrDefault (package => IsPackageIdMatch (package, packageId)); + if (foundPackage != null) { + if (previousMatch.package == null) { + previousMatch.package = foundPackage; + previousMatch.projectInfo = projectInfo; + } else { + if (additionalText == null) { + additionalText = StringBuilderCache.Allocate (); + AppendAdditionalText (additionalText, previousMatch.projectInfo, previousMatch.package); + } + additionalText.Append (", "); + AppendAdditionalText (additionalText, projectInfo, foundPackage); + } + } + } + + if (additionalText != null) { + return StringBuilderCache.ReturnAndFree (additionalText); + } + + return string.Empty; + } + + static void AppendAdditionalText (StringBuilder additionalText, ManagePackagesProjectInfo projectInfo, PackageIdentity foundPackage) + { + additionalText.AppendFormat ("{0}: {1}", projectInfo.Project.Name, foundPackage.Version); + } + } +} + diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManageProjectViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManageProjectViewModel.cs new file mode 100644 index 0000000000..324164fb18 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManageProjectViewModel.cs @@ -0,0 +1,61 @@ +// +// ManageProjectViewModel.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// 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.Linq; + +namespace MonoDevelop.PackageManagement +{ + class ManageProjectViewModel + { + public ManageProjectViewModel (ManagePackagesProjectInfo projectInfo, string packageId) + { + ProjectInfo = projectInfo; + + var package = ProjectInfo.Packages.FirstOrDefault (package => StringComparer.OrdinalIgnoreCase.Equals (package.Id, packageId)); + if (package != null) { + IsChecked = true; + PackageVersion = package.Version.ToString (); + } else { + PackageVersion = "–"; + } + } + + public IDotNetProject Project { + get { return ProjectInfo.Project; } + } + + public string ProjectName { + get { return Project.Name; } + } + + public string PackageVersion { get; set; } + + public bool IsChecked { get; set; } + + internal ManagePackagesProjectInfo ProjectInfo { get; set; } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageSearchResultViewModelComparer.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagedPackagesSearchResultViewModelComparer.cs index 69bb202950..756fdaac9b 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/PackageSearchResultViewModelComparer.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/ManagedPackagesSearchResultViewModelComparer.cs @@ -1,5 +1,5 @@ // -// PackageSearchResultViewModelComparer.cs +// ManagedPackagesSearchResultViewModelComparer.cs // // Author: // Matt Ward <matt.ward@xamarin.com> @@ -29,17 +29,17 @@ using System.Collections.Generic; namespace MonoDevelop.PackageManagement { - class PackageSearchResultViewModelComparer : IEqualityComparer<PackageSearchResultViewModel> + class ManagedPackagesSearchResultViewModelComparer : IEqualityComparer<ManagePackagesSearchResultViewModel> { - public static readonly PackageSearchResultViewModelComparer Instance = - new PackageSearchResultViewModelComparer (); + public static readonly ManagedPackagesSearchResultViewModelComparer Instance = + new ManagedPackagesSearchResultViewModelComparer (); - public bool Equals (PackageSearchResultViewModel x, PackageSearchResultViewModel y) + public bool Equals (ManagePackagesSearchResultViewModel x, ManagePackagesSearchResultViewModel y) { return x.Id.Equals (y.Id, StringComparison.OrdinalIgnoreCase); } - public int GetHashCode (PackageSearchResultViewModel obj) + public int GetHashCode (ManagePackagesSearchResultViewModel obj) { return obj.Id.GetHashCode (); } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RecentNuGetPackagesRepository.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RecentManagedNuGetPackagesRepository.cs index 05d7c1b8a3..8833f3cb51 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RecentNuGetPackagesRepository.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/RecentManagedNuGetPackagesRepository.cs @@ -1,5 +1,5 @@ // -// RecentNuGetPackagesRepository.cs +// RecentManagedNuGetPackagesRepository.cs // // Author: // Matt Ward <matt.ward@xamarin.com> @@ -30,7 +30,7 @@ using System.Linq; namespace MonoDevelop.PackageManagement { - internal class RecentNuGetPackagesRepository + internal class RecentManagedNuGetPackagesRepository { public const int DefaultMaximumPackagesCount = 20; @@ -43,14 +43,14 @@ namespace MonoDevelop.PackageManagement set { maximumPackagesCount = value; } } - public IEnumerable<PackageSearchResultViewModel> GetPackages (string source) + public IEnumerable<ManagePackagesSearchResultViewModel> GetPackages (string source) { return packages .Where (package => String.Equals (package.Source, source, StringComparison.OrdinalIgnoreCase)) .Select (package => package.PackageViewModel); } - public void AddPackage (PackageSearchResultViewModel viewModel, string source) + public void AddPackage (ManagePackagesSearchResultViewModel viewModel, string source) { var package = new RecentPackage (viewModel, source); viewModel.IsRecentPackage = true; @@ -74,7 +74,7 @@ namespace MonoDevelop.PackageManagement bool IsMatch (RecentPackage x, RecentPackage y) { - return PackageSearchResultViewModelComparer.Instance.Equals (x.PackageViewModel, y.PackageViewModel); + return ManagedPackagesSearchResultViewModelComparer.Instance.Equals (x.PackageViewModel, y.PackageViewModel); } void AddPackageAtBeginning (RecentPackage package) @@ -97,13 +97,13 @@ namespace MonoDevelop.PackageManagement class RecentPackage { - public RecentPackage (PackageSearchResultViewModel viewModel, string source) + public RecentPackage (ManagePackagesSearchResultViewModel viewModel, string source) { PackageViewModel = viewModel; Source = source; } - public PackageSearchResultViewModel PackageViewModel { get; set; } + public ManagePackagesSearchResultViewModel PackageViewModel { get; set; } public string Source { get; set; } } } diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SearchPackagesSearchCategory.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SearchPackagesSearchCategory.cs index 95cc768120..cf565f615f 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SearchPackagesSearchCategory.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SearchPackagesSearchCategory.cs @@ -70,8 +70,8 @@ namespace MonoDevelop.PackageManagement public override void Activate () { - var runner = new AddPackagesDialogRunner ();
- runner.Run (pattern.UnparsedPattern);
+ var runner = new ManagePackagesDialogRunner ();
+ runner.Run (IdeApp.ProjectOperations.CurrentSelectedProject, pattern.UnparsedPattern);
}
public override string GetMarkupText (bool selected) diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SelectProjectsViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SelectProjectsViewModel.cs new file mode 100644 index 0000000000..c2b00625df --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SelectProjectsViewModel.cs @@ -0,0 +1,101 @@ +// +// SelectProjectsViewModel.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2016 Xamarin Inc. (http://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.Collections.Generic; +using System.Linq; + +namespace MonoDevelop.PackageManagement +{ + class SelectProjectsViewModel + { + readonly List<SelectedProjectViewModel> projects; + + public SelectProjectsViewModel ( + IEnumerable<IDotNetProject> projects, + int packagesCount, + ManagePackagesPage page) + { + this.projects = projects + .OrderBy (project => project.Name) + .Select (project => new SelectedProjectViewModel (project)) + .ToList (); + + if (this.projects.Count == 1) { + this.projects[0].IsSelected = true; + } + + bool multiplePackages = packagesCount > 1; + + switch (page) { + case ManagePackagesPage.Installed: + IsRemovingMultiplePackages = multiplePackages; + IsRemovingSinglePackage = !IsAddingMultiplePackages; + break; + case ManagePackagesPage.Browse: + IsAddingMultiplePackages = multiplePackages; + IsAddingSinglePackage = !IsAddingMultiplePackages; + break; + case ManagePackagesPage.Updates: + IsUpdatingMultiplePackages = multiplePackages; + IsUpdatingSinglePackage = !IsAddingMultiplePackages; + SelectAllProjectsByDefault (); + break; + case ManagePackagesPage.Consolidate: + IsConsolidatingMultiplePackages = multiplePackages; + IsConsolidatingSinglePackage = !IsAddingMultiplePackages; + SelectAllProjectsByDefault (); + break; + } + } + + public IEnumerable<SelectedProjectViewModel> Projects { + get { return projects; } + } + + public bool IsAddingSinglePackage { get; } + public bool IsAddingMultiplePackages { get; } + public bool IsRemovingSinglePackage { get; } + public bool IsRemovingMultiplePackages { get; } + public bool IsUpdatingSinglePackage { get; } + public bool IsUpdatingMultiplePackages { get; } + public bool IsConsolidatingSinglePackage { get; } + public bool IsConsolidatingMultiplePackages { get; } + + public IEnumerable<IDotNetProject> GetSelectedProjects () + { + return projects + .Where (viewModel => viewModel.IsSelected) + .Select (viewModel => viewModel.Project); + } + + void SelectAllProjectsByDefault () + { + foreach (SelectedProjectViewModel project in projects) { + project.IsSelected = true; + } + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SelectedProjectViewModel.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SelectedProjectViewModel.cs new file mode 100644 index 0000000000..fa3b0ca585 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement/SelectedProjectViewModel.cs @@ -0,0 +1,48 @@ +// +// SelectedProjectViewModel.cs +// +// Author: +// matt <matt.ward@xamarin.com> +// +// Copyright (c) 2016 matt +// +// 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 MonoDevelop.Core; + +namespace MonoDevelop.PackageManagement +{ + class SelectedProjectViewModel + { + public SelectedProjectViewModel (IDotNetProject project) + { + Project = project; + } + + public IDotNetProject Project { get; } + public bool IsSelected { get; set; } + + public string Name { + get { return Project.Name; } + } + + public IconId Icon { + get { return Project.DotNetProject.StockIcon; } + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/ConsolidatePackageFeed.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/ConsolidatePackageFeed.cs new file mode 100644 index 0000000000..5b68f63444 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/ConsolidatePackageFeed.cs @@ -0,0 +1,111 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging.Core; +using NuGet.Protocol.Core.Types; + +namespace NuGet.PackageManagement.UI +{ + /// <summary> + /// Package feed facilitating iteration over installed packages needing version consolidation + /// </summary> + internal class ConsolidatePackageFeed : PlainPackageFeedBase + { + IEnumerable<PackageIdentity> _installedPackages; + readonly IPackageMetadataProvider _metadataProvider; + PackageLoadContext _context; + + public ConsolidatePackageFeed ( + PackageLoadContext context, + IPackageMetadataProvider metadataProvider, + Common.ILogger logger) + : this (new PackageIdentity[0], metadataProvider, logger) + { + _context = context; + } + + public ConsolidatePackageFeed ( + IEnumerable<PackageIdentity> installedPackages, + IPackageMetadataProvider metadataProvider, + Common.ILogger logger) + { + if (installedPackages == null) { + throw new ArgumentNullException (nameof (installedPackages)); + } + _installedPackages = installedPackages; + + if (metadataProvider == null) { + throw new ArgumentNullException (nameof (metadataProvider)); + } + _metadataProvider = metadataProvider; + + if (logger == null) { + throw new ArgumentNullException (nameof (logger)); + } + + PageSize = 25; + } + + public override async Task<SearchResult<IPackageSearchMetadata>> ContinueSearchAsync (ContinuationToken continuationToken, CancellationToken cancellationToken) + { + var searchToken = continuationToken as FeedSearchContinuationToken; + if (searchToken == null) { + throw new InvalidOperationException ("Invalid token"); + } + + if (_context != null) { + _installedPackages = await _context.GetInstalledPackagesAsync (); + _context = null; + } + + var packagesNeedingConsolidation = _installedPackages + .GroupById () + .Where (g => g.Count () > 1) + .Select (g => new PackageIdentity (g.Key, g.Max ())) + .ToArray (); + + var packages = packagesNeedingConsolidation + .Where (p => p.Id.IndexOf (searchToken.SearchString, StringComparison.OrdinalIgnoreCase) != -1) + .OrderBy (p => p.Id) + .Skip (searchToken.StartIndex) + .Take (PageSize + 1) + .ToArray (); + + var hasMoreItems = packages.Length > PageSize; + if (hasMoreItems) { + packages = packages.Take (packages.Length - 1).ToArray (); + } + + var items = await TaskCombinators.ThrottledAsync ( + packages, + (p, t) => _metadataProvider.GetPackageMetadataAsync (p, searchToken.SearchFilter.IncludePrerelease, t), + cancellationToken); + + var result = SearchResult.FromItems (items.ToArray ()); + + var loadingStatus = hasMoreItems + ? LoadingStatus.Ready + : packages.Length == 0 + ? LoadingStatus.NoItemsFound + : LoadingStatus.NoMoreItems; + result.SourceSearchStatus = new Dictionary<string, LoadingStatus> { + { "Consolidate", loadingStatus } + }; + + if (hasMoreItems) { + result.NextToken = new FeedSearchContinuationToken { + SearchString = searchToken.SearchString, + SearchFilter = searchToken.SearchFilter, + StartIndex = searchToken.StartIndex + packages.Length + }; + } + + return result; + } + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/InstalledPackageFeed.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/InstalledPackageFeed.cs new file mode 100644 index 0000000000..9bbc222d85 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/InstalledPackageFeed.cs @@ -0,0 +1,119 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging.Core; +using NuGet.Protocol.Core.Types; + +namespace NuGet.PackageManagement.UI +{ + /// <summary> + /// Represents a package feed enumerating installed packages. + /// </summary> + internal class InstalledPackageFeed : PlainPackageFeedBase + { + IEnumerable<PackageIdentity> _installedPackages; + readonly IPackageMetadataProvider _metadataProvider; + PackageLoadContext _context; + + public InstalledPackageFeed ( + PackageLoadContext context, + IPackageMetadataProvider metadataProvider, + Common.ILogger logger) + : this (new PackageIdentity[0], metadataProvider, logger) + { + this._context = context; + } + + public InstalledPackageFeed ( + IEnumerable<PackageIdentity> installedPackages, + IPackageMetadataProvider metadataProvider, + Common.ILogger logger) + { + if (installedPackages == null) { + throw new ArgumentNullException (nameof (installedPackages)); + } + _installedPackages = installedPackages; + + if (metadataProvider == null) { + throw new ArgumentNullException (nameof (metadataProvider)); + } + _metadataProvider = metadataProvider; + + if (logger == null) { + throw new ArgumentNullException (nameof (logger)); + } + + PageSize = 25; + } + + public override async Task<SearchResult<IPackageSearchMetadata>> ContinueSearchAsync (ContinuationToken continuationToken, CancellationToken cancellationToken) + { + var searchToken = continuationToken as FeedSearchContinuationToken; + if (searchToken == null) { + throw new InvalidOperationException ("Invalid token"); + } + + if (_context != null) { + _installedPackages = await _context.GetInstalledPackagesAsync (); + _context = null; + } + + var packages = _installedPackages + .GetLatest () + .Where (p => p.Id.IndexOf (searchToken.SearchString, StringComparison.OrdinalIgnoreCase) != -1) + .OrderBy (p => p.Id) + .Skip (searchToken.StartIndex) + .Take (PageSize + 1) + .ToArray (); + + var hasMoreItems = packages.Length > PageSize; + if (hasMoreItems) { + packages = packages.Take (packages.Length - 1).ToArray (); + } + + var items = await TaskCombinators.ThrottledAsync ( + packages, + (p, t) => GetPackageMetadataAsync (p, searchToken.SearchFilter.IncludePrerelease, t), + cancellationToken); + + // The packages were originally sorted which is important because we Skip and Take based on that sort + // however the asynchronous execution has randomly reordered the set. So we need to resort. + var result = SearchResult.FromItems (items.OrderBy (p => p.Identity.Id).ToArray ()); + + var loadingStatus = hasMoreItems + ? LoadingStatus.Ready + : packages.Length == 0 + ? LoadingStatus.NoItemsFound + : LoadingStatus.NoMoreItems; + result.SourceSearchStatus = new Dictionary<string, LoadingStatus> { + { "Installed", loadingStatus } + }; + + if (hasMoreItems) { + result.NextToken = new FeedSearchContinuationToken { + SearchString = searchToken.SearchString, + SearchFilter = searchToken.SearchFilter, + StartIndex = searchToken.StartIndex + packages.Length + }; + } + + return result; + } + + async Task<IPackageSearchMetadata> GetPackageMetadataAsync (PackageIdentity identity, bool includePrerelease, CancellationToken cancellationToken) + { + // first we try and load the metadata from a local package + var packageMetadata = await _metadataProvider.GetLocalPackageMetadataAsync (identity, includePrerelease, cancellationToken); + if (packageMetadata == null) { + // and failing that we go to the network + packageMetadata = await _metadataProvider.GetPackageMetadataAsync (identity, includePrerelease, cancellationToken); + } + return packageMetadata; + } + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageCollection.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageCollection.cs new file mode 100644 index 0000000000..c369377f40 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageCollection.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging.Core; +using NuGet.ProjectManagement; +using NuGet.Versioning; + +namespace NuGet.PackageManagement.UI +{ + /// <summary> + /// Wrapper class consolidating common queries against a collection of packages + /// </summary> + internal class PackageCollection : IEnumerable<PackageIdentity> + { + readonly PackageIdentity[] _packages; + readonly ISet<string> _uniqueIds = new HashSet<string> (StringComparer.OrdinalIgnoreCase); + + public PackageCollection (PackageIdentity[] packages) + { + _packages = packages; + _uniqueIds.UnionWith (_packages.Select (p => p.Id)); + } + + public IEnumerator<PackageIdentity> GetEnumerator () + { + return ((IEnumerable<PackageIdentity>)_packages).GetEnumerator (); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return ((IEnumerable<PackageIdentity>)_packages).GetEnumerator (); + } + + public bool ContainsId (string packageId) => _uniqueIds.Contains (packageId); + + public static async Task<PackageCollection> FromProjectsAsync (IEnumerable<NuGetProject> projects, CancellationToken cancellationToken) + { + var tasks = projects + .Select (project => project.GetInstalledPackagesAsync (cancellationToken)); + var packageReferences = await Task.WhenAll (tasks); + var packages = packageReferences + .SelectMany (p => p) + .Where (p => p != null) + .Select (p => p.PackageIdentity) + .Distinct (PackageIdentity.Comparer) + .ToArray (); + + return new PackageCollection (packages); + } + } + + /// <summary> + /// Common package queries implementes as extension methods + /// </summary> + internal static class PackageCollectionExtensions + { + public static NuGetVersion[] GetPackageVersions (this IEnumerable<PackageIdentity> packages, string packageId) + { + return packages + .Where (p => StringComparer.OrdinalIgnoreCase.Equals (p.Id, packageId)) + .Select (p => p.Version) + .ToArray (); + } + + public static IEnumerable<IGrouping<string, NuGetVersion>> GroupById (this IEnumerable<PackageIdentity> packages) + { + return packages + .GroupBy (p => p.Id, p => p.Version, StringComparer.OrdinalIgnoreCase); + } + + public static PackageIdentity[] GetLatest (this IEnumerable<PackageIdentity> packages) + { + return packages + .GroupById () + .Select (g => new PackageIdentity (g.Key, g.MaxOrDefault ())) + .ToArray (); + } + + public static PackageIdentity[] GetEarliest (this IEnumerable<PackageIdentity> packages) + { + return packages + .GroupById () + .Select (g => new PackageIdentity (g.Key, g.MinOrDefault ())) + .ToArray (); + } + } + + internal static class VersionCollectionExtensions + { + public static NuGetVersion MinOrDefault (this IEnumerable<NuGetVersion> versions) + { + return versions + .OrderBy (v => v, VersionComparer.Default) + .FirstOrDefault (); + } + + public static NuGetVersion MaxOrDefault (this IEnumerable<NuGetVersion> versions) + { + return versions + .OrderByDescending (v => v, VersionComparer.Default) + .FirstOrDefault (); + } + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageDetailControlModel.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageDetailControlModel.cs index 954558f2f6..89b94c5815 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageDetailControlModel.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageDetailControlModel.cs @@ -25,7 +25,7 @@ namespace NuGet.PackageManagement.UI IEnumerable<NuGetProject> nugetProjects) : base(nugetProjects) { - Debug.Assert(nugetProjects.Count() == 1); + //Debug.Assert(nugetProjects.Count() == 1); } //public async override Task SetCurrentPackage( diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageFeedEnumerator.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageFeedEnumerator.cs new file mode 100644 index 0000000000..b52e1f8c8a --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageFeedEnumerator.cs @@ -0,0 +1,119 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Protocol.Core.Types; + +namespace NuGet.PackageManagement.UI +{ + internal class PackageFeedEnumerator : IEnumerator<IPackageSearchMetadata>, IDisposable, IEnumerator + { + private readonly IPackageFeed _packageFeed; + private readonly Task<SearchResult<IPackageSearchMetadata>> _startFromTask; + private readonly CancellationToken _cancellationToken; + + private Task<SearchResult<IPackageSearchMetadata>> _searchTask; + private IEnumerator<IPackageSearchMetadata> _current; + + private PackageFeedEnumerator ( + IPackageFeed packageFeed, + Task<SearchResult<IPackageSearchMetadata>> searchTask, + CancellationToken cancellationToken) + { + if (packageFeed == null) { + throw new ArgumentNullException (nameof (packageFeed)); + } + _packageFeed = packageFeed; + + if (searchTask == null) { + throw new ArgumentNullException (nameof (searchTask)); + } + _startFromTask = searchTask; + + _cancellationToken = cancellationToken; + + Reset (); + } + + private PackageFeedEnumerator (PackageFeedEnumerator other) + { + if (other == null) { + throw new ArgumentNullException (nameof (other)); + } + _packageFeed = other._packageFeed; + _startFromTask = other._startFromTask; + _cancellationToken = other._cancellationToken; + + Reset (); + } + + public IPackageSearchMetadata Current => _current.Current; + + object IEnumerator.Current => _current.Current; + + public void Dispose () + { + } + + public bool MoveNext () + { + if (_current.MoveNext ()) { + return true; + } + + LoadNextAsync ().Wait (); + return _current.MoveNext (); + } + + private async Task LoadNextAsync () + { + var searchResult = await _searchTask; + + while (searchResult.RefreshToken != null) { + searchResult = await _packageFeed.RefreshSearchAsync (searchResult.RefreshToken, _cancellationToken); + } + + _current = searchResult.GetEnumerator (); + + if (searchResult.NextToken != null) { + _searchTask = _packageFeed.ContinueSearchAsync (searchResult.NextToken, _cancellationToken); + } else { + _searchTask = Task.FromResult (SearchResult.Empty<IPackageSearchMetadata> ()); + } + } + + public void Reset () + { + _searchTask = _startFromTask; + _current = Enumerable.Empty<IPackageSearchMetadata> ().GetEnumerator (); + } + + public static IEnumerable<IPackageSearchMetadata> Enumerate ( + IPackageFeed packageFeed, + Task<SearchResult<IPackageSearchMetadata>> searchTask, + CancellationToken cancellationToken) + { + var enumerator = new PackageFeedEnumerator (packageFeed, searchTask, cancellationToken); + return new PackageFeedEnumerable (enumerator); + } + + private sealed class PackageFeedEnumerable : IEnumerable<IPackageSearchMetadata> + { + private readonly PackageFeedEnumerator _enumerator; + + public PackageFeedEnumerable (PackageFeedEnumerator enumerator) + { + _enumerator = enumerator; + } + + public IEnumerator<IPackageSearchMetadata> GetEnumerator () => new PackageFeedEnumerator (_enumerator); + + IEnumerator IEnumerable.GetEnumerator () => this.GetEnumerator (); + } + } +} diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageLoadContext.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageLoadContext.cs index e69db3cefb..994c60d122 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageLoadContext.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageLoadContext.cs @@ -14,7 +14,7 @@ namespace NuGet.PackageManagement.UI {
internal class PackageLoadContext
{
- //private readonly Task<PackageCollection> _installedPackagesTask;
+ Task<PackageCollection> installedPackagesTask;
public IEnumerable<SourceRepository> SourceRepositories { get; private set; }
@@ -32,18 +32,18 @@ namespace NuGet.PackageManagement.UI public PackageLoadContext(
IEnumerable<SourceRepository> sourceRepositories,
bool isSolution,
- NuGetProject project)
+ IEnumerable<NuGetProject> projects)
{
SourceRepositories = sourceRepositories;
IsSolution = isSolution;
//PackageManager = uiContext.PackageManager;
- Projects = new [] { project };
+ Projects = projects.ToArray ();
//PackageManagerProviders = uiContext.PackageManagerProviders;
- //_installedPackagesTask = PackageCollection.FromProjectsAsync(Projects, CancellationToken.None);
+ installedPackagesTask = PackageCollection.FromProjectsAsync(Projects, CancellationToken.None);
}
- //public Task<PackageCollection> GetInstalledPackagesAsync() =>_installedPackagesTask;
+ public Task<PackageCollection> GetInstalledPackagesAsync() => installedPackagesTask;
// Returns the list of frameworks that we need to pass to the server during search
public IEnumerable<string> GetSupportedFrameworks()
diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageSearchMetadataCache.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageSearchMetadataCache.cs new file mode 100644 index 0000000000..e61d3dc360 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PackageSearchMetadataCache.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using NuGet.Protocol.Core.Types; + +namespace NuGet.PackageManagement.UI +{ + class PackageSearchMetadataCache + { + // Cached Package Metadata + public IReadOnlyList<IPackageSearchMetadata> Packages { get; set; } + + // Remember the IncludePrerelease setting corresponding to the Cached Metadata + public bool IncludePrerelease { get; set; } + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PlainPackageFeedBase.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PlainPackageFeedBase.cs new file mode 100644 index 0000000000..360656f33e --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/PlainPackageFeedBase.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; +using NuGet.Protocol.Core.Types; + +namespace NuGet.PackageManagement.UI +{ + /// <summary> + /// Shared base implementation of plain package feeds. + /// </summary> + internal abstract class PlainPackageFeedBase : IPackageFeed + { + public int PageSize { get; protected set; } = 100; + + // No, it's not. + public bool IsMultiSource => false; + + public Task<SearchResult<IPackageSearchMetadata>> SearchAsync (string searchText, SearchFilter searchFilter, CancellationToken cancellationToken) + { + var searchToken = new FeedSearchContinuationToken { + SearchString = searchText, + SearchFilter = searchFilter, + StartIndex = 0 + }; + + return ContinueSearchAsync (searchToken, cancellationToken); + } + + public abstract Task<SearchResult<IPackageSearchMetadata>> ContinueSearchAsync (ContinuationToken continuationToken, CancellationToken cancellationToken); + + public Task<SearchResult<IPackageSearchMetadata>> RefreshSearchAsync (RefreshToken refreshToken, CancellationToken cancellationToken) + => Task.FromResult (SearchResult.Empty<IPackageSearchMetadata> ()); + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/UpdatePackageFeed.cs b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/UpdatePackageFeed.cs new file mode 100644 index 0000000000..173cee7c64 --- /dev/null +++ b/main/src/addins/MonoDevelop.PackageManagement/NuGet.PackageManagement.UI/UpdatePackageFeed.cs @@ -0,0 +1,121 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging.Core; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; + +namespace NuGet.PackageManagement.UI +{ + /// <summary> + /// A package feed providing services of package enumeration of installed packages having updated versions in upstream source(s). + /// </summary> + internal class UpdatePackageFeed : PlainPackageFeedBase + { + IEnumerable<PackageIdentity> _installedPackages; + readonly IPackageMetadataProvider _metadataProvider; + readonly PackageSearchMetadataCache _cachedUpdates; + PackageLoadContext _context; + + public UpdatePackageFeed ( + PackageLoadContext context, + IPackageMetadataProvider metadataProvider, + PackageSearchMetadataCache cachedUpdates, + Common.ILogger logger) + : this (new PackageIdentity[0], metadataProvider, cachedUpdates, logger) + { + _context = context; + } + + public UpdatePackageFeed ( + IEnumerable<PackageIdentity> installedPackages, + IPackageMetadataProvider metadataProvider, + PackageSearchMetadataCache cachedUpdates, + Common.ILogger logger) + { + if (installedPackages == null) { + throw new ArgumentNullException (nameof (installedPackages)); + } + _installedPackages = installedPackages; + + if (metadataProvider == null) { + throw new ArgumentNullException (nameof (metadataProvider)); + } + _metadataProvider = metadataProvider; + + _cachedUpdates = cachedUpdates; + + if (logger == null) { + throw new ArgumentNullException (nameof (logger)); + } + } + + public override async Task<SearchResult<IPackageSearchMetadata>> ContinueSearchAsync (ContinuationToken continuationToken, CancellationToken cancellationToken) + { + var searchToken = continuationToken as FeedSearchContinuationToken; + if (searchToken == null) { + throw new InvalidOperationException ("Invalid token"); + } + + var packagesWithUpdates = (_cachedUpdates.Packages != null) && (_cachedUpdates?.IncludePrerelease == searchToken.SearchFilter.IncludePrerelease) + ? + GetPackagesFromCache (searchToken.SearchString) + : + await GetPackagesWithUpdatesAsync (searchToken.SearchString, searchToken.SearchFilter, cancellationToken); + + var items = packagesWithUpdates + .Skip (searchToken.StartIndex) + .ToArray (); + + var result = SearchResult.FromItems (items); + + var loadingStatus = items.Length == 0 + ? LoadingStatus.NoItemsFound + : LoadingStatus.NoMoreItems; + result.SourceSearchStatus = new Dictionary<string, LoadingStatus> { + { "Update", loadingStatus } + }; + + return result; + } + + IEnumerable<IPackageSearchMetadata> GetPackagesFromCache (string searchText) + { + return _cachedUpdates.Packages.Where (p => p.Identity.Id.IndexOf (searchText, StringComparison.OrdinalIgnoreCase) != -1); + } + + async Task<IEnumerable<IPackageSearchMetadata>> GetPackagesWithUpdatesAsync (string searchText, SearchFilter searchFilter, CancellationToken cancellationToken) + { + if (_context != null) { + _installedPackages = await _context.GetInstalledPackagesAsync (); + _context = null; + } + + var packages = _installedPackages + .GetEarliest () + .Where (p => p.Id.IndexOf (searchText, StringComparison.OrdinalIgnoreCase) != -1) + .OrderBy (p => p.Id); + + var latestItems = await TaskCombinators.ThrottledAsync ( + packages, + (p, t) => _metadataProvider.GetLatestPackageMetadataAsync (p, searchFilter.IncludePrerelease, t), + cancellationToken); + + var packagesWithUpdates = packages + .Join (latestItems.Where (i => i != null), + p => p.Id, + m => m.Identity.Id, + (p, m) => new { cv = p.Version, m = m }, + StringComparer.OrdinalIgnoreCase) + .Where (j => VersionComparer.VersionRelease.Compare (j.cv, j.m.Identity.Version) < 0) + .Select (j => j.m); + + return packagesWithUpdates; + } + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs index 36398008a6..336df933d5 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.AnalysisCore/Gui/ResultsEditorExtension.cs @@ -76,11 +76,19 @@ namespace MonoDevelop.AnalysisCore.Gui protected override void Initialize () { base.Initialize (); - + if (Editor.Options is DefaultSourceEditorOptions options) + options.Changed += Options_Changed; AnalysisOptions.AnalysisEnabled.Changed += AnalysisOptionsChanged; AnalysisOptionsChanged (null, null); } + void Options_Changed (object sender, EventArgs e) + { + if (DocumentContext == null || !enabled) + return; + UpdateInitialDiagnostics (); + } + void AnalysisOptionsChanged (object sender, EventArgs e) { Enabled = AnalysisOptions.AnalysisEnabled; @@ -91,6 +99,8 @@ namespace MonoDevelop.AnalysisCore.Gui if (disposed) return; enabled = false; + if (Editor.Options is DefaultSourceEditorOptions options) + options.Changed -= Options_Changed; diagService.DiagnosticsUpdated -= OnDiagnosticsUpdated; diagService = null; CancelUpdateTimout (); @@ -154,7 +164,7 @@ namespace MonoDevelop.AnalysisCore.Gui if (!AnalysisOptions.EnableFancyFeatures) return; - var doc = DocumentContext.AnalysisDocument; + var doc = DocumentContext?.AnalysisDocument; if (doc == null || DocumentContext.IsAdHocProject) return; diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PackageInstaller/PackageInstallerService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PackageInstaller/PackageInstallerService.cs index 75aabfe88a..a630aca56b 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PackageInstaller/PackageInstallerService.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PackageInstaller/PackageInstallerService.cs @@ -128,6 +128,9 @@ namespace MonoDevelop.Refactoring.PackageInstaller installedPackages.ContainsKey (packageName); } + public bool CanShowManagePackagesDialog () + => true; + public void ShowManagePackagesDialog (string packageName) { PackageServices.ShowManagePackagesDialog (packageName); diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs index 38d4003d5b..30fbbd073e 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.QuickTasks/QuickTaskOverviewMode.cs @@ -725,20 +725,18 @@ namespace MonoDevelop.SourceEditor.QuickTasks { var breakpointStore = parentStrip.SourceEditorView.Breakpoints; - lock (breakpointStore) { - var breakPoints = breakpointStore.GetBreakpointsAtFile (TextEditor.FileName); + var breakPoints = breakpointStore.GetBreakpointsAtFile (TextEditor.FileName); - if (breakPoints == null) - return; + if (breakPoints == null) + return; - foreach (var point in breakPoints) { - int y = (int)GetYPosition (point.Line); + foreach (var point in breakPoints) { + int y = (int)GetYPosition (point.Line); - cr.SetSourceColor (SyntaxHighlightingService.GetColor (TextEditor.EditorTheme, EditorThemeColors.BreakpointMarker)); - int r = 4; - cr.Rectangle (0, y - r / 2, r, r); - cr.Fill (); - } + cr.SetSourceColor (SyntaxHighlightingService.GetColor (TextEditor.EditorTheme, EditorThemeColors.BreakpointMarker)); + int r = 4; + cr.Rectangle (0, y - r / 2, r, r); + cr.Fill (); } } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs index c9407e4dbe..365927bafe 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorView.cs @@ -1348,17 +1348,15 @@ namespace MonoDevelop.SourceEditor int i = 0, count = 0; bool mismatch = false; - lock (breakpoints) { - foreach (var bp in breakpoints.GetBreakpointsAtFile (fp.FullPath)) { - count++; - if (i < breakpointSegments.Count) { - int lineNumber = document.OffsetToLineNumber (breakpointSegments [i].TextMarker.Offset); - if (lineNumber != bp.Line) { - mismatch = true; - break; - } - i++; + foreach (var bp in breakpoints.GetBreakpointsAtFile (fp.FullPath)) { + count++; + if (i < breakpointSegments.Count) { + int lineNumber = document.OffsetToLineNumber (breakpointSegments [i].TextMarker.Offset); + if (lineNumber != bp.Line) { + mismatch = true; + break; } + i++; } } @@ -1383,11 +1381,9 @@ namespace MonoDevelop.SourceEditor breakpointSegments.Clear (); - lock (breakpoints) { - foreach (var bp in breakpoints.GetBreakpointsAtFile (fp.FullPath)) { - lineNumbers.Add (bp.Line); - AddBreakpoint (bp); - } + foreach (var bp in breakpoints.GetBreakpointsAtFile (fp.FullPath)) { + lineNumbers.Add (bp.Line); + AddBreakpoint (bp); } foreach (int lineNumber in lineNumbers) { @@ -1506,8 +1502,7 @@ namespace MonoDevelop.SourceEditor int column = TextEditor.Caret.Line == args.LineNumber ? Math.Min (TextEditor.Caret.Column, args.LineSegment.Length) : 1; - lock (breakpoints) - breakpoints.Toggle (Document.FileName, args.LineNumber, column); + breakpoints.Toggle (Document.FileName, args.LineNumber, column); } } } diff --git a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/Properties/MonoDevelop.TextEditor.Cocoa.addin.xml b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/Properties/MonoDevelop.TextEditor.Cocoa.addin.xml index 3cbda1c2bc..c78c5cae0f 100644 --- a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/Properties/MonoDevelop.TextEditor.Cocoa.addin.xml +++ b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor.Cocoa/Properties/MonoDevelop.TextEditor.Cocoa.addin.xml @@ -97,8 +97,12 @@ --> <Map id="MonoDevelop.Ide.Commands.SearchCommands.Find" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.FindCommandArgs" /> <Map id="MonoDevelop.Ide.Commands.SearchCommands.FindNext" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.FindNextCommandArgs" /> + <Map id="MonoDevelop.Ide.Commands.SearchCommands.FindNextSelection" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.FindNextLikeSelectionCommandArgs" /> <Map id="MonoDevelop.Ide.Commands.SearchCommands.FindPrevious" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.FindPreviousCommandArgs" /> + <Map id="MonoDevelop.Ide.Commands.SearchCommands.FindPreviousSelection" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.FindPreviousLikeSelectionCommandArgs" /> <Map id="MonoDevelop.Ide.Commands.SearchCommands.Replace" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.ReplaceCommandArgs" /> + <Map id="MonoDevelop.Ide.Commands.SearchCommands.UseSelectionForReplace" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.ReplaceCommandArgs" /> + <Map id="MonoDevelop.Ide.Commands.SearchCommands.UseSelectionForFind" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.SetSearchStringFromSelectionCommandArgs" /> <Map id="MonoDevelop.Refactoring.Navigate.GotoBaseMember" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.Navigation.GoToBaseMemberCommandArgs" /> <Map id="MonoDevelop.Ide.Commands.SearchCommands.GotoLineNumber" argsType="Microsoft.VisualStudio.Text.Extras.GoToLine.GoToLineCommandArgs" /> </Extension> diff --git a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/Properties/MonoDevelop.TextEditor.addin.xml b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/Properties/MonoDevelop.TextEditor.addin.xml index 969f61f267..0e5345b0a0 100644 --- a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/Properties/MonoDevelop.TextEditor.addin.xml +++ b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/Properties/MonoDevelop.TextEditor.addin.xml @@ -146,7 +146,7 @@ <Map id="MonoDevelop.Ide.Commands.TextEditorCommands.ShowCodeTemplateWindow" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.InsertSnippetCommandArgs" /> <Map id="MonoDevelop.Ide.Commands.TextEditorCommands.ShowCodeSurroundingsWindow" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.SurroundWithCommandArgs" /> - <Map id="MonoDevelop.Ide.Commands.TextEditorCommands.GotoMatchingBrace" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.GoToMatchingBraceCommandArgs" /> + <Map id="MonoDevelop.Ide.Commands.TextEditorCommands.GotoMatchingBrace" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.GotoBraceCommandArgs" /> <Map id="MonoDevelop.Ide.Commands.EditCommands.ToggleFolding" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.ToggleOutliningExpansionCommandArgs" /> <Map id="MonoDevelop.Ide.Commands.EditCommands.ToggleAllFoldings" argsType="Microsoft.VisualStudio.Text.Editor.Commanding.Commands.ToggleAllOutliningCommandArgs" /> diff --git a/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProject.xpt.xml b/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProject.xpt.xml index 3d0d711c33..0e1bf46362 100644 --- a/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProject.xpt.xml +++ b/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProject.xpt.xml @@ -32,7 +32,7 @@ </References> <Packages> - <Package ID="NUnit" Version="2.6.4" /> + <Package ID="NUnit" Version="3.12.0" /> </Packages> <Files> diff --git a/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProjectVBNet.xpt.xml b/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProjectVBNet.xpt.xml index c6aa7ce400..f64fde474b 100644 --- a/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProjectVBNet.xpt.xml +++ b/main/src/addins/MonoDevelop.UnitTesting.NUnit/templates/NUnitProjectVBNet.xpt.xml @@ -32,7 +32,7 @@ </References> <Packages> - <Package ID="NUnit" Version="2.6.4" /> + <Package ID="NUnit" Version="3.12.0" /> </Packages> <Files> diff --git a/main/src/addins/MonoDeveloperExtensions/MonoMakefileProjectReader.cs b/main/src/addins/MonoDeveloperExtensions/MonoMakefileProjectReader.cs index 317e5d1840..017c404aef 100644 --- a/main/src/addins/MonoDeveloperExtensions/MonoMakefileProjectReader.cs +++ b/main/src/addins/MonoDeveloperExtensions/MonoMakefileProjectReader.cs @@ -53,12 +53,12 @@ namespace MonoDeveloper public override Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, SolutionLoadContext ctx, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) { - return Task.Run (() => (SolutionItem) ReadFile (fileName, false, monitor)); + return Task.FromResult ((SolutionItem) ReadFile (fileName, false, monitor)); } public override Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) { - return Task.Run (() => (WorkspaceItem) ReadFile (fileName, false, monitor)); + return Task.FromResult ((WorkspaceItem) ReadFile (fileName, false, monitor)); } public object ReadFile (FilePath fileName, bool hasParentSolution, ProgressMonitor monitor) diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git.Tests/MonoDevelop.VersionControl.Git.Tests.csproj b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git.Tests/MonoDevelop.VersionControl.Git.Tests.csproj index 700e35fb91..9634a9d7df 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git.Tests/MonoDevelop.VersionControl.Git.Tests.csproj +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Git.Tests/MonoDevelop.VersionControl.Git.Tests.csproj @@ -51,6 +51,8 @@ <ProjectReference Include="..\..\..\..\external\libgit2sharp\LibGit2Sharp\LibGit2Sharp.csproj"> <Project>{EE6ED99F-CB12-4683-B055-D28FC7357A34}</Project> <Name>LibGit2Sharp</Name> + <IncludeInPackage>true</IncludeInPackage> + <Private>False</Private> </ProjectReference> <ProjectReference Include="..\..\MonoDevelop.SourceEditor2\MonoDevelop.SourceEditor.csproj"> <Project>{F8F92AA4-A376-4679-A9D4-60E7B7FBF477}</Project> diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/DefaultBlameViewHandler.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/DefaultBlameViewHandler.cs index f4bfaf9b98..0669b396e5 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/DefaultBlameViewHandler.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/DefaultBlameViewHandler.cs @@ -38,10 +38,7 @@ namespace MonoDevelop.VersionControl { public static bool DefaultVCSViewCanHandle (VersionControlItem item, DocumentController controller) { - if (controller == null) - return item.Repository.GetFileIsText (item.Path); - - return controller is TextEditorViewContent || controller.GetContent<ITextBuffer> () != null; + return item.Repository.GetFileIsText (item.Path); } public bool CanHandle (VersionControlItem item, DocumentController controller) => DefaultVCSViewCanHandle (item, controller); diff --git a/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/WPFToolbar.cs b/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/WPFToolbar.cs index 2ed8162614..d96e660e96 100644 --- a/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/WPFToolbar.cs +++ b/main/src/addins/WindowsPlatform/WindowsPlatform/MainToolbar/WPFToolbar.cs @@ -1,4 +1,4 @@ -using MonoDevelop.Components.MainToolbar;
+using MonoDevelop.Components.MainToolbar;
using MonoDevelop.Components.Windows;
using MonoDevelop.Core;
using System;
@@ -25,24 +25,25 @@ namespace WindowsPlatform.MainToolbar public class WPFToolbar : GtkWPFWidget, IMainToolbarView, INotifyPropertyChanged
{
ToolBar toolbar;
+
WPFToolbar (ToolBar toolbar) : base (toolbar)
{
this.toolbar = toolbar;
toolbar.ConfigurationMenu.SelectionChanged += (o, e) => {
- var comboMenu = (ComboMenu<IConfigurationModel>)o;
+ var comboMenu = (ComboMenu<IConfigurationModel>) o;
var newModel = e.Added;
if (newModel == null)
return;
- Runtime.RunInMainThread(() => {
+ Runtime.RunInMainThread (() => {
ActiveConfiguration = newModel;
ConfigurationChanged?.Invoke (o, e);
});
};
toolbar.RunConfigurationMenu.SelectionChanged += (o, e) => {
- var comboMenu = (ComboMenu<IRunConfigurationModel>)o;
+ var comboMenu = (ComboMenu<IRunConfigurationModel>) o;
var newModel = e.Added;
if (newModel == null)
return;
@@ -58,11 +59,11 @@ namespace WindowsPlatform.MainToolbar if (newModel == null)
return;
- using (var mutableModel = newModel.GetMutableModel()) {
- Runtime.RunInMainThread(() => {
+ using (var mutableModel = newModel.GetMutableModel ()) {
+ Runtime.RunInMainThread (() => {
ActiveRuntime = newModel;
- var ea = new MonoDevelop.Components.MainToolbar.HandledEventArgs();
+ var ea = new MonoDevelop.Components.MainToolbar.HandledEventArgs ();
RuntimeChanged?.Invoke (o, ea);
if (ea.Handled)
@@ -80,7 +81,7 @@ namespace WindowsPlatform.MainToolbar if (SearchText == SearchPlaceholderMessage)
return;
- if (SearchEntryChanged != null) + if (SearchEntryChanged != null)
SearchEntryChanged (o, e);
};
@@ -90,9 +91,7 @@ namespace WindowsPlatform.MainToolbar toolbar.SearchBar.SearchText = toolbar.SearchBar.PlaceholderText;
};
- toolbar.SearchBar.SearchBar.GotKeyboardFocus += (o, e) => {
- SearchEntryActivated?.Invoke (o, e);
- };
+ toolbar.SearchBar.SearchBar.GotKeyboardFocus += (o, e) => { SearchEntryActivated?.Invoke (o, e); };
toolbar.SearchBar.SearchBar.SizeChanged += (o, e) => {
if (SearchEntryResized != null)
@@ -100,41 +99,41 @@ namespace WindowsPlatform.MainToolbar };
toolbar.SearchBar.SearchBar.PreviewKeyDown += (o, e) => {
- var ka = new KeyEventArgs(KeyboardUtil.TranslateToXwtKey(e.Key), KeyboardUtil.GetModifiers(), e.IsRepeat, e.Timestamp);
- SendKeyPress(ka);
+ var ka = new KeyEventArgs (KeyboardUtil.TranslateToXwtKey (e.Key), KeyboardUtil.GetModifiers (),
+ e.IsRepeat, e.Timestamp);
+ SendKeyPress (ka);
e.Handled = ka.Handled;
};
- toolbar.SearchBar.ClearIconClicked += (o, e) =>
- {
- SendKeyPress(new KeyEventArgs(Xwt.Key.Escape, KeyboardUtil.GetModifiers(), false, 0));
+ toolbar.SearchBar.ClearIconClicked += (o, e) => {
+ SendKeyPress (new KeyEventArgs (Xwt.Key.Escape, KeyboardUtil.GetModifiers (), false, 0));
};
- }
+ }
- protected override void RepositionWpfWindow()
- {
- int scale = (int)MonoDevelop.Components.GtkWorkarounds.GetScaleFactor(this);
- RepositionWpfWindow (scale, scale);
- } + protected override void RepositionWpfWindow ()
+ {
+ int scale = (int) MonoDevelop.Components.GtkWorkarounds.GetScaleFactor (this);
+ RepositionWpfWindow (scale, scale);
+ }
- void SendKeyPress(KeyEventArgs ka)
+ void SendKeyPress (KeyEventArgs ka)
{
if (SearchEntryKeyPressed != null)
- SearchEntryKeyPressed(this, ka);
+ SearchEntryKeyPressed (this, ka);
}
public WPFToolbar () : this (new ToolBar ())
{
}
-
+
public IConfigurationModel ActiveConfiguration {
get { return toolbar.ConfigurationMenu.Active; }
set { toolbar.ConfigurationMenu.Active = value; }
}
-
+
public IRuntimeModel ActiveRuntime {
- get { return toolbar.RuntimeMenu.Active; }
- set { toolbar.RuntimeMenu.Active = value; }
+ get { return toolbar.RuntimeMenu.Active; }
+ set { toolbar.RuntimeMenu.Active = value; }
}
public IRunConfigurationModel ActiveRunConfiguration {
@@ -143,11 +142,11 @@ namespace WindowsPlatform.MainToolbar }
public bool ButtonBarSensitivity {
- set { toolbar.ButtonBarPanel.IsEnabled = value; }
+ set { toolbar.ButtonBarPanel.IsEnabled = value; }
}
public IEnumerable<IConfigurationModel> ConfigurationModel {
- get { return toolbar.ConfigurationMenu.Model; }
+ get { return toolbar.ConfigurationMenu.Model; }
set { toolbar.ConfigurationMenu.Model = value; }
}
@@ -162,29 +161,26 @@ namespace WindowsPlatform.MainToolbar }
bool configurationPlatformSensitivity;
- public bool ConfigurationPlatformSensitivity {
+
+ public bool ConfigurationPlatformSensitivity {
get { return configurationPlatformSensitivity; }
set {
configurationPlatformSensitivity = value;
- toolbar.ConfigurationMenu.IsEnabled = value && ConfigurationModel.Count() > 1;
- toolbar.RuntimeMenu.IsEnabled = value && RuntimeModel.Count() > 1;
- }
+ toolbar.ConfigurationMenu.IsEnabled = value && ConfigurationModel.Count () > 1;
+ toolbar.RuntimeMenu.IsEnabled = value && RuntimeModel.Count () > 1;
+ }
}
-
+
public bool PlatformSensitivity {
- set {
- toolbar.RuntimeMenu.IsEnabled = value && RuntimeModel.Count() > 1;
- }
+ set { toolbar.RuntimeMenu.IsEnabled = value && RuntimeModel.Count () > 1; }
}
public Gtk.Widget PopupAnchor {
- get {
- return this;
- }
+ get { return this; }
}
public OperationIcon RunButtonIcon {
- set { toolbar.RunButton.Icon = value; }
+ set { toolbar.RunButton.Icon = value; }
}
public bool RunButtonSensitivity {
@@ -195,14 +191,15 @@ namespace WindowsPlatform.MainToolbar public bool RunConfigurationVisible {
get { return toolbar.RunConfigurationMenu.IsVisible; }
set {
- System.Windows.Visibility visible = value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
+ System.Windows.Visibility visible =
+ value ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
toolbar.RunConfigurationMenu.Visibility = visible;
toolbar.RunConfigurationSeparator.Visibility = visible;
}
}
public string SearchCategory {
- set {
+ set {
toolbar.SearchBar.SearchText = value;
FocusSearchBar ();
toolbar.SearchBar.SearchBar.Select (value.Length, 0);
@@ -210,18 +207,16 @@ namespace WindowsPlatform.MainToolbar }
public IEnumerable<ISearchMenuModel> SearchMenuItems {
- set {
- toolbar.SearchBar.SearchMenuItems = value;
- }
+ set { toolbar.SearchBar.SearchMenuItems = value; }
}
public string SearchPlaceholderMessage {
get { return toolbar.SearchBar.PlaceholderText; }
- set { toolbar.SearchBar.PlaceholderText = value; }
+ set { toolbar.SearchBar.PlaceholderText = value; }
}
public bool SearchSensivitity {
- set { toolbar.SearchBar.IsEnabled = value; }
+ set { toolbar.SearchBar.IsEnabled = value; }
}
public string SearchText {
@@ -236,7 +231,7 @@ namespace WindowsPlatform.MainToolbar }
public StatusBar StatusBar {
- get { return toolbar.StatusBar; }
+ get { return toolbar.StatusBar; }
}
public event EventHandler ConfigurationChanged;
@@ -245,6 +240,7 @@ namespace WindowsPlatform.MainToolbar public event EventHandler SearchEntryActivated;
public event EventHandler SearchEntryChanged;
public event EventHandler<KeyEventArgs> SearchEntryKeyPressed;
+ public event EventHandler<SearchEntryCommandArgs> PerformCommand;
public event EventHandler SearchEntryLostFocus;
public event EventHandler SearchEntryResized;
@@ -258,27 +254,24 @@ namespace WindowsPlatform.MainToolbar foreach (var item in toolbar.ButtonBarPanel.Children.OfType<IDisposable> ())
item.Dispose ();
- toolbar.ButtonBarPanel.Children.Clear (); - - // Remove empty groups so we know when to put a separator + toolbar.ButtonBarPanel.Children.Clear ();
+
+ // Remove empty groups so we know when to put a separator
var groupList = groups.ToList ();
groupList.RemoveAll ((g) => g.Buttons.Count == 0);
int idx = 0;
int count = groupList.Count;
- var sepStyle = toolbar.FindResource (System.Windows.Controls.ToolBar.SeparatorStyleKey) as System.Windows.Style;
+ var sepStyle =
+ toolbar.FindResource (System.Windows.Controls.ToolBar.SeparatorStyleKey) as System.Windows.Style;
foreach (var buttonGroup in groupList) {
bool needsSeparator = (idx < count - 1);
foreach (var button in buttonGroup.Buttons) {
if (needsSeparator)
toolbar.ButtonBarPanel.Children.Add (new DottedSeparator {
- Margin = new System.Windows.Thickness {
- Left = 3,
- Right = 3,
- },
- UseLayoutRounding = true,
+ Margin = new System.Windows.Thickness {Left = 3, Right = 3,}, UseLayoutRounding = true,
});
toolbar.ButtonBarPanel.Children.Add (new ButtonBarButton (button));
@@ -295,10 +288,10 @@ namespace WindowsPlatform.MainToolbar PropertyChanged (this, new System.ComponentModel.PropertyChangedEventArgs (propName));
}
- public void Focus (Gtk.DirectionType direction, Action<Gtk.DirectionType> exitAction) - { + public void Focus (Gtk.DirectionType direction, Action<Gtk.DirectionType> exitAction)
+ {
FocusSearchBar ();
- exitAction (direction); + exitAction (direction);
}
public void ShowAccessibilityAnnouncement (string message)
@@ -312,7 +305,7 @@ namespace WindowsPlatform.MainToolbar public class NotNullConverter : IValueConverter
{
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
@@ -321,5 +314,5 @@ namespace WindowsPlatform.MainToolbar {
throw new NotImplementedException ();
}
- }
+ }
}
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFrameworkMoniker.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFrameworkMoniker.cs index 7b49c3be82..e6d4a4b32d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFrameworkMoniker.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFrameworkMoniker.cs @@ -333,6 +333,10 @@ namespace MonoDevelop.Core.Assemblies get { return new TargetFrameworkMoniker ("4.7.1"); } } + public static TargetFrameworkMoniker NET_4_7_2 { + get { return new TargetFrameworkMoniker ("4.7.2"); } + } + public static TargetFrameworkMoniker PORTABLE_4_0 { get { return new TargetFrameworkMoniker (ID_PORTABLE, "4.0", "Profile1"); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs index c0e4238cb8..d48d076f05 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs @@ -143,6 +143,14 @@ namespace MonoDevelop.Core }
}
+ [Pure]
+ internal bool HasFileName (string name) + { + return fileName.Length > name.Length + && fileName.EndsWith (name, PathComparison) + && fileName [fileName.Length - name.Length - 1] == Path.DirectorySeparatorChar; + }
+
public string Extension {
get {
return Path.GetExtension (fileName);
@@ -153,8 +161,27 @@ namespace MonoDevelop.Core public bool HasExtension (string extension) { return fileName.Length > extension.Length - && fileName.EndsWith (extension, PathComparison) - && fileName[fileName.Length - extension.Length - 1] != Path.PathSeparator; + && (extension == string.Empty + ? HasNoExtension (fileName) + : fileName.EndsWith (extension, PathComparison) && fileName [fileName.Length - extension.Length] == '.'); + + static bool HasNoExtension (string path) + { + // Look for the last dot that's after the last path separator + for (int i = path.Length - 1; i >= 0; --i) { + var ch = path [i]; + if (ch == '.') { + // Check if it's the dot is the last character + // if it is, then we have no extension + return i == path.Length - 1; + } + + if (ch == Path.DirectorySeparatorChar) + return true; + } + + return true; + } }
public string FileNameWithoutExtension {
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs index 18d9d0e167..13d5beeaae 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/ProjectTypeNode.cs @@ -45,7 +45,7 @@ namespace MonoDevelop.Projects.Extensions Project project = null; if (!string.IsNullOrEmpty (fileName)) { - p = await MSBuildProject.LoadAsync (fileName); + p = await MSBuildProject.LoadAsync (fileName).ConfigureAwait (false); if (ctx != null && ctx.Solution != null) { p.EngineManager = ctx.Solution.MSBuildEngineManager; p.SolutionDirectory = ctx.Solution.ItemDirectory; @@ -53,7 +53,7 @@ namespace MonoDevelop.Projects.Extensions var migrators = MSBuildProjectService.GetMigrableFlavors (p.ProjectTypeGuids); if (migrators.Count > 0) - await MSBuildProjectService.MigrateFlavors (monitor, fileName, Guid, p, migrators); + await MSBuildProjectService.MigrateFlavors (monitor, fileName, Guid, p, migrators).ConfigureAwait (false); var unsupporedFlavor = p.ProjectTypeGuids.FirstOrDefault (fid => !MSBuildProjectService.IsKnownFlavorGuid (fid) && !MSBuildProjectService.IsKnownTypeGuid (fid)); if (unsupporedFlavor != null) { @@ -72,7 +72,7 @@ namespace MonoDevelop.Projects.Extensions } if (project == null) - project = await base.CreateSolutionItem (monitor, ctx, fileName) as Project; + project = await base.CreateSolutionItem (monitor, ctx, fileName).ConfigureAwait(false) as Project; if (project == null) throw new InvalidOperationException ("Project node type is not a subclass of MonoDevelop.Projects.Project"); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs index 46fa87c880..3cd16adde0 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Extensions/SolutionItemTypeNode.cs @@ -99,7 +99,7 @@ namespace MonoDevelop.Projects.Extensions if (typeof(SolutionItemFactory).IsAssignableFrom (ItemType)) { if (factory == null) factory = (SolutionItemFactory)Activator.CreateInstance (ItemType); - item = await factory.CreateItem (fileName, Guid); + item = await factory.CreateItem (fileName, Guid).ConfigureAwait (false); } else item = MSBuildProjectService.CreateUninitializedInstance (ItemType); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs index e821b48bf6..a22fc426c5 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs @@ -850,7 +850,7 @@ namespace MonoDevelop.Projects.MSBuild static bool IsWildcardInclude (string include) { - return include.IndexOf ('*') != -1; + return include.IndexOfAny (wildcards) != -1; } IEnumerable<MSBuildItemEvaluated> ExpandWildcardFilePath (ProjectInfo pinfo, MSBuildEvaluationContext context, MSBuildItem sourceItem, string path, Regex directoryExcludeRegex) @@ -1018,7 +1018,7 @@ namespace MonoDevelop.Projects.MSBuild return it; } - static char[] wildcards = { '*', '%' }; + static char[] wildcards = { '*', '?' }; void Evaluate (ProjectInfo project, MSBuildEvaluationContext context, MSBuildProperty prop) { @@ -1174,7 +1174,7 @@ namespace MonoDevelop.Projects.MSBuild var fileName = Path.GetFileName (path); - if (fileName.IndexOfAny (new [] { '*', '?' }) == -1) { + if (fileName.IndexOfAny (wildcards) == -1) { // Not a wildcard. Keep searching if the file doesn't exist. var result = File.Exists (path) ? new [] { path } : null; keepSearching = result == null; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs index 94ea9abfa3..e7626bece3 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs @@ -614,10 +614,6 @@ namespace MonoDevelop.Projects.MSBuild if (memberName.Length == 0) return false; - var member = ResolveMember (type, memberName.ToString (), instance == null); - if (member == null || member.Length == 0) - return false; - if (j < str.Length && str[j] == '(') { // It is a method invocation object [] parameterValues; @@ -625,6 +621,10 @@ namespace MonoDevelop.Projects.MSBuild if (!EvaluateParameters (str, ref j, out parameterValues)) return false; + var member = ResolveMember (type, memberName.ToString (), instance == null, MemberTypes.Method); + if (member == null || member.Length == 0) + return false; + if (!EvaluateMethod (str, member, instance, parameterValues, out val)) return false; @@ -634,6 +634,10 @@ namespace MonoDevelop.Projects.MSBuild } else { // It has to be a property or field try { + var member = ResolveMember (type, memberName.ToString (), instance == null, MemberTypes.Property | MemberTypes.Field); + if (member == null || member.Length == 0) + return false; + if (member[0] is PropertyInfo) val = ((PropertyInfo)member[0]).GetValue (instance); else if (member[0] is FieldInfo) @@ -683,7 +687,7 @@ namespace MonoDevelop.Projects.MSBuild internal bool EvaluateMember (ReadOnlySpan<char> str, Type type, string memberName, object instance, object [] parameterValues, out object val) { val = null; - var member = ResolveMember (type, memberName, instance == null); + var member = ResolveMember (type, memberName, instance == null, MemberTypes.Method); if (member == null || member.Length == 0) return false; return EvaluateMethod (str, member, instance, parameterValues, out val); @@ -694,22 +698,17 @@ namespace MonoDevelop.Projects.MSBuild val = null; // Find a method with a matching number of parameters - var method = FindBestOverload (member.OfType<MethodBase> (), parameterValues); + var (method, methodParams) = FindBestOverload (member, parameterValues, out var paramsArgType); if (method == null) return false; try { // Convert the given parameters to the types specified in the method signature - var methodParams = method.GetParameters (); - var convertedArgs = (methodParams.Length == parameterValues.Length) ? parameterValues : new object [methodParams.Length]; int numArgs = methodParams.Length; - Type paramsArgType = null; - if (methodParams.Length > 0 && methodParams [methodParams.Length - 1].ParameterType.IsArray && methodParams [methodParams.Length - 1].IsDefined (typeof (ParamArrayAttribute))) { - paramsArgType = methodParams [methodParams.Length - 1].ParameterType.GetElementType (); + if (paramsArgType != null) numArgs--; - } if (method.DeclaringType == typeof (IntrinsicFunctions) && method.Name == nameof (IntrinsicFunctions.GetPathOfFileAbove) && parameterValues.Length == methodParams.Length - 1) { string startingDirectory = String.IsNullOrWhiteSpace (FullFileName) ? String.Empty : Path.GetDirectoryName (FullFileName); @@ -792,58 +791,158 @@ namespace MonoDevelop.Projects.MSBuild return false; } - MethodBase FindBestOverload (IEnumerable<MethodBase> methods, object [] args) + (MethodBase method, ParameterInfo[] parameters) FindBestOverload (IEnumerable<MemberInfo> members, object [] args, out Type paramsArgType) { - MethodBase methodWithParams = null; + (MethodBase, ParameterInfo[]) methodWithParams = default; + (MethodBase, ParameterInfo[]) validMatch = default; - foreach (var m in methods) { - var argInfo = m.GetParameters (); + paramsArgType = null; - // Exclude methods which take a complex object as argument - if (argInfo.Any (a => a.ParameterType != typeof(object) && Type.GetTypeCode (a.ParameterType) == TypeCode.Object && !IsParamsArg(a))) + foreach (var member in members) { + if (!(member is MethodBase m)) continue; - if (args.Length >= argInfo.Length - 1 && argInfo.Length > 0 && IsParamsArg (argInfo [argInfo.Length - 1])) { - methodWithParams = m; - continue; - } - if (args.Length != argInfo.Length) { - if (args.Length == argInfo.Length - 1 && m.DeclaringType != typeof (IntrinsicFunctions) || m.Name != nameof(IntrinsicFunctions.GetPathOfFileAbove)) { + var argInfo = m.GetParameters (); + + if (args.Length == argInfo.Length - 1) { + if (m.DeclaringType == typeof (IntrinsicFunctions) && m.Name == nameof (IntrinsicFunctions.GetPathOfFileAbove)) { + validMatch = (m, argInfo); continue; } } - bool isValid = true; - for (int n = 0; n < args.Length; n++) { - if (!CanConvertArg (m, n, args [n], argInfo [n].ParameterType)) { - isValid = false; - break; - } + // Unable to match in this case. + if (args.Length < argInfo.Length - 1) + continue; + + var kind = MatchArgs (args, argInfo); + if (kind == MatchKind.Exact) + return (m, argInfo); + + if (kind == MatchKind.CanConvert) + validMatch = (m, argInfo); + else if (kind == MatchKind.Params) { + methodWithParams = (m, argInfo); + paramsArgType = argInfo [argInfo.Length - 1].ParameterType.GetElementType (); } - if (isValid) - return m; } - return methodWithParams; + + return validMatch != default ? validMatch : methodWithParams; } - bool IsParamsArg (ParameterInfo pi) + enum MatchKind + { + None, + Params, + CanConvert, + Exact, + } + + static MatchKind MatchArgs (object[] args, ParameterInfo[] parameters) + { + bool isParams = parameters.Length > 0 && IsParamsArg (parameters [parameters.Length - 1]); + + int last = parameters.Length; + if (isParams) + last--; + else if (args.Length != parameters.Length) + return MatchKind.None; + + var kind = MatchKind.Exact; + for (int n = 0; n < last; n++) { + var parameterType = parameters [n].ParameterType; + + var other = Match (parameterType, args [n]); + if (other == MatchKind.None) + return MatchKind.None; + + if (other == MatchKind.CanConvert) + kind = MatchKind.CanConvert; + } + + if (!isParams) + return kind; + + // Check implicit argument + if (args.Length == last) + return MatchKind.Params; + + var elementType = parameters[last].ParameterType.GetElementType (); + if (IsComplexType (elementType)) + return MatchKind.None; + + int argsRemaining = args.Length - last; + if (argsRemaining == 1 && elementType == typeof(char)) { + if (Match (parameters [last].ParameterType, args [last], checkComplexType: false) != MatchKind.None) + return MatchKind.Params; + } + + for (int n_arg = last; n_arg < args.Length; ++n_arg) { + if (Match (elementType, args [n_arg]) == MatchKind.None) + return MatchKind.None; + } + + return MatchKind.Params; + + static bool IsComplexType (Type type) + { + return Type.GetTypeCode (type) == TypeCode.Object && type != typeof (object); + } + + static MatchKind Match (Type parameterType, object argument, bool checkComplexType = true) + { + if (parameterType.IsInstanceOfType (argument)) + return MatchKind.Exact; + + if (checkComplexType && IsComplexType (parameterType)) + return MatchKind.None; + + if (CanConvertArg (argument, parameterType)) + return MatchKind.CanConvert; + + + return MatchKind.None; + } + } + + static bool IsParamsArg (ParameterInfo pi) { return pi.ParameterType.IsArray && pi.IsDefined (typeof (ParamArrayAttribute)); } - bool CanConvertArg (MethodBase method, int argNum, object value, Type parameterType) + static bool CanConvertArg (object value, Type parameterType) { var sval = value as string; if (sval == "null" || value == null) - return !parameterType.IsValueType || typeof(Nullable).IsInstanceOfType (parameterType); + return !parameterType.IsValueType || Nullable.GetUnderlyingType (parameterType) != null; if (sval != null && parameterType == typeof (char [])) return true; - if (parameterType == typeof (char) && sval != null && sval.Length != 1) + if (sval != null && sval.Length != 1 && parameterType == typeof (char)) { return false; + } - return true; + if (sval != null && parameterType.IsEnum) { + // Enum.Parse expects comma separated values. + var enumValue = sval.Replace ('|', ',') + .Replace (parameterType.FullName + ".", "") + .Replace (parameterType.Name + ".", ""); + + try { + _ = Enum.Parse (parameterType, enumValue, ignoreCase: true); + return true; + } catch { + return false; + } + } + + try { + _ = Convert.ChangeType (value, parameterType, CultureInfo.InvariantCulture); + return true; + } catch { + return false; + } } object ConvertArg (MethodBase method, int argNum, object value, Type parameterType) @@ -856,11 +955,11 @@ namespace MonoDevelop.Projects.MSBuild return sval.ToCharArray (); if (sval != null && parameterType.IsEnum) { - var enumValue = sval; - if (enumValue.StartsWith (parameterType.Name, StringComparison.Ordinal)) - enumValue = enumValue.Substring (parameterType.Name.Length + 1); - if (enumValue.StartsWith (parameterType.FullName, StringComparison.Ordinal)) - enumValue = enumValue.Substring (parameterType.FullName.Length + 1); + // Enum.Parse expects comma separated values. + var enumValue = sval.Replace ('|', ',') + .Replace (parameterType.FullName + ".", "") + .Replace (parameterType.Name + ".", ""); + return Enum.Parse(parameterType, enumValue, ignoreCase: true); } @@ -907,15 +1006,15 @@ namespace MonoDevelop.Projects.MSBuild { if (typeName == "MSBuild") return typeof (Microsoft.Build.Evaluation.IntrinsicFunctions); - else { - var t = supportedTypeMembers.FirstOrDefault (st => st.Item1.FullName == typeName); - if (t == null) - return null; - return t.Item1; + + foreach (var kvp in supportedTypeMembers) { + if (kvp.Key.FullName == typeName) + return kvp.Key; } + return null; } - MemberInfo[] ResolveMember (Type type, string memberName, bool isStatic) + MemberInfo[] ResolveMember (Type type, string memberName, bool isStatic, MemberTypes memberTypes) { if (type == typeof (string) && memberName == "new") memberName = "Copy"; @@ -923,52 +1022,68 @@ namespace MonoDevelop.Projects.MSBuild type = typeof (Array); var flags = isStatic ? BindingFlags.Static : BindingFlags.Instance; if (type != typeof (Microsoft.Build.Evaluation.IntrinsicFunctions)) { - var t = supportedTypeMembers.FirstOrDefault (st => st.Item1 == type); - if (t == null) + if (!supportedTypeMembers.TryGetValue (type, out var list)) return null; - if (t.Item2 != null && !t.Item2.Contains (memberName)) + + if (list != null && !list.Contains (memberName)) return null; } else flags |= BindingFlags.NonPublic; - - return type.GetMember (memberName, flags | BindingFlags.Public | BindingFlags.IgnoreCase); + + return type.GetMember (memberName, memberTypes, flags | BindingFlags.Public | BindingFlags.IgnoreCase); } - static Tuple<Type, string []> [] supportedTypeMembers = { - Tuple.Create (typeof(System.Array), (string[]) null), - Tuple.Create (typeof(System.Byte), (string[]) null), - Tuple.Create (typeof(System.Char), (string[]) null), - Tuple.Create (typeof(System.Convert), (string[]) null), - Tuple.Create (typeof(System.DateTime), (string[]) null), - Tuple.Create (typeof(System.Decimal), (string[]) null), - Tuple.Create (typeof(System.Double), (string[]) null), - Tuple.Create (typeof(System.Enum), (string[]) null), - Tuple.Create (typeof(System.Guid), (string[]) null), - Tuple.Create (typeof(System.Int16), (string[]) null), - Tuple.Create (typeof(System.Int32), (string[]) null), - Tuple.Create (typeof(System.Int64), (string[]) null), - Tuple.Create (typeof(System.IO.Path), (string[]) null), - Tuple.Create (typeof(System.Math), (string[]) null), - Tuple.Create (typeof(System.UInt16), (string[]) null), - Tuple.Create (typeof(System.UInt32), (string[]) null), - Tuple.Create (typeof(System.UInt64), (string[]) null), - Tuple.Create (typeof(System.SByte), (string[]) null), - Tuple.Create (typeof(System.Single), (string[]) null), - Tuple.Create (typeof(System.String), (string[]) null), - Tuple.Create (typeof(System.StringComparer), (string[]) null), - Tuple.Create (typeof(System.TimeSpan), (string[]) null), - Tuple.Create (typeof(System.Text.RegularExpressions.Regex), (string[]) null), - Tuple.Create (typeof(Microsoft.Build.Utilities.ToolLocationHelper), (string[]) null), - Tuple.Create (typeof(System.Globalization.CultureInfo), (string[]) null), - Tuple.Create (typeof(System.Environment), new string [] { - "CommandLine", "ExpandEnvironmentVariables", "GetEnvironmentVariable", "GetEnvironmentVariables", "GetFolderPath", "GetLogicalDrives" - }), - Tuple.Create (typeof(System.IO.Directory), new string [] { - "GetDirectories", "GetFiles", "GetLastAccessTime", "GetLastWriteTime", "GetParent" - }), - Tuple.Create (typeof(System.IO.File), new string [] { - "Exists", "GetCreationTime", "GetAttributes", "GetLastAccessTime", "GetLastWriteTime", "ReadAllText" - }), + sealed class TypeEqualityComparer : IEqualityComparer<Type> + { + public bool Equals (Type x, Type y) => x == y; + + public int GetHashCode (Type obj) => obj?.GetHashCode () ?? 0; + } + + static readonly Dictionary<Type, string []> supportedTypeMembers = new Dictionary<Type, string []> (new TypeEqualityComparer()) { + { typeof(System.Array), null }, + { typeof(System.Byte), null }, + { typeof(System.Char), null }, + { typeof(System.Convert), null }, + { typeof(System.DateTime), null }, + { typeof(System.Decimal), null }, + { typeof(System.Double), null }, + { typeof(System.Enum), null }, + { typeof(System.Guid), null }, + { typeof(System.Int16), null }, + { typeof(System.Int32), null }, + { typeof(System.Int64), null }, + { typeof(System.IO.Path), null }, + { typeof(System.Math), null }, + { typeof(System.UInt16), null }, + { typeof(System.UInt32), null }, + { typeof(System.UInt64), null }, + { typeof(System.SByte), null }, + { typeof(System.Single), null }, + { typeof(System.String), null }, + { typeof(System.StringComparer), null }, + { typeof(System.TimeSpan), null }, + { typeof(System.Text.RegularExpressions.Regex), null }, + { typeof(Microsoft.Build.Utilities.ToolLocationHelper), null }, + { typeof(System.Globalization.CultureInfo), null }, + { + typeof (System.Environment), + new string [] { + "CommandLine", "ExpandEnvironmentVariables", "GetEnvironmentVariable", "GetEnvironmentVariables", "GetFolderPath", "GetLogicalDrives" + } + }, + { + typeof (System.IO.Directory), + new string [] { + "GetDirectories", "GetFiles", "GetLastAccessTime", "GetLastWriteTime", "GetParent" + } + }, + { + typeof (System.IO.File), + new string [] { + "Exists", "GetCreationTime", "GetAttributes", "GetLastAccessTime", "GetLastWriteTime", "ReadAllText" + } + }, }; int FindNextTag (string str, int i) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs index e7c8945f90..fe0e089335 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs @@ -168,7 +168,7 @@ namespace MonoDevelop.Projects.MSBuild internal async Task WriteFile (FilePath file, object obj, ProgressMonitor monitor) { if (slnFileFormat.CanWriteFile (obj, this)) { - await slnFileFormat.WriteFile (file, obj, true, monitor); + await slnFileFormat.WriteFile (file, obj, true, monitor).ConfigureAwait (false); } else { throw new NotSupportedException (); } @@ -177,7 +177,7 @@ namespace MonoDevelop.Projects.MSBuild internal async Task<object> ReadFile (FilePath file, Type expectedType, MonoDevelop.Core.ProgressMonitor monitor) { if (slnFileFormat.CanReadFile (file, this)) - return await slnFileFormat.ReadFile (file, monitor); + return await slnFileFormat.ReadFile (file, monitor).ConfigureAwait(false); else throw new NotSupportedException (); }
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs index a23254c86c..4b21073b76 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs @@ -115,7 +115,7 @@ namespace MonoDevelop.Projects.MSBuild } catch (Exception ex) { // If the project can't be evaluated don't crash LoggingService.LogError ("MSBuild project could not be evaluated", ex); - throw new ProjectEvaluationException (msproject, ex.Message); + throw new ProjectEvaluationException (msproject, ex.Message, ex); } finally { if (oldProjectInstance != null) engine.DisposeProjectInstance (oldProjectInstance); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectService.cs index 703bbcf97d..398aa31187 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectService.cs @@ -784,26 +784,24 @@ namespace MonoDevelop.Projects.MSBuild return Task.FromResult<SolutionItem> (new GenericProject ()); // Unknown project types are already displayed in the solution view, we don't need to tell the user with a modal dialog as well - return Task<SolutionItem>.Factory.StartNew (delegate { - var t = ReadGenericProjectType (file); - if (t == null) - throw new UnknownSolutionItemTypeException (GettextCatalog.GetString ("Unknown project type")); + var t = ReadGenericProjectType (file); + if (t == null) + return Task.FromException<SolutionItem> (new UnknownSolutionItemTypeException (GettextCatalog.GetString ("Unknown project type"))); - var dt = Services.ProjectService.DataContext.GetConfigurationDataType (t); - if (dt != null) { - if (!typeof (Project).IsAssignableFrom (dt.ValueType)) - throw new UnknownSolutionItemTypeException (GettextCatalog.GetString ("Unknown project type: {0}", t)); + var dt = Services.ProjectService.DataContext.GetConfigurationDataType (t); + if (dt != null) { + if (!typeof (Project).IsAssignableFrom (dt.ValueType)) + return Task.FromException<SolutionItem> (new UnknownSolutionItemTypeException (GettextCatalog.GetString ("Unknown project type: {0}", t))); - return (SolutionItem)Activator.CreateInstance (dt.ValueType); - } + return Task.FromResult ((SolutionItem)Activator.CreateInstance (dt.ValueType)); + } - Type type; - lock (genericProjectTypes) { - if (!genericProjectTypes.TryGetValue (t, out type)) - throw new UnknownSolutionItemTypeException (GettextCatalog.GetString ("Unknown project type: {0}", t)); - } - return (SolutionItem)Activator.CreateInstance (type); - }); + Type type; + lock (genericProjectTypes) { + if (!genericProjectTypes.TryGetValue (t, out type)) + return Task.FromException<SolutionItem> (new UnknownSolutionItemTypeException (GettextCatalog.GetString ("Unknown project type: {0}", t))); + } + return Task.FromResult ((SolutionItem)Activator.CreateInstance (type)); } static string ReadGenericProjectType (string file) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/ProjectEvaluationException.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/ProjectEvaluationException.cs index 000fcea525..88775b3e5d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/ProjectEvaluationException.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/ProjectEvaluationException.cs @@ -29,12 +29,16 @@ namespace MonoDevelop.Projects.MSBuild { public class ProjectEvaluationException: ApplicationException { - public ProjectEvaluationException (MSBuildProject project, string message): base (message) + internal ProjectEvaluationException (MSBuildProject project, string message, Exception innerException) : base (message, innerException) { Project = project; } - public MSBuildProject Project { get; private set; } + public ProjectEvaluationException (MSBuildProject project, string message): this (project, message, null) + { + } + + public MSBuildProject Project { get; } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SlnFileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SlnFileFormat.cs index 742edb3d99..581fef41d4 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SlnFileFormat.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SlnFileFormat.cs @@ -64,22 +64,20 @@ namespace MonoDevelop.Projects.MSBuild return obj is Solution; } - public Task WriteFile (string file, object obj, bool saveProjects, ProgressMonitor monitor) + public async Task WriteFile (string file, object obj, bool saveProjects, ProgressMonitor monitor) { - return Task.Run (async delegate { - Solution sol = (Solution)obj; + Solution sol = (Solution)obj; - try { - monitor.BeginTask (GettextCatalog.GetString ("Saving solution: {0}", file), 1); - await WriteFileInternal (file, file, sol, saveProjects, monitor).ConfigureAwait (false); - } catch (Exception ex) { - monitor.ReportError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); - LoggingService.LogError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); - throw; - } finally { - monitor.EndTask (); - } - }); + try { + monitor.BeginTask (GettextCatalog.GetString ("Saving solution: {0}", file), 1); + await WriteFileInternal (file, file, sol, saveProjects, monitor).ConfigureAwait (false); + } catch (Exception ex) { + monitor.ReportError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); + LoggingService.LogError (GettextCatalog.GetString ("Could not save solution: {0}", file), ex); + throw; + } finally { + monitor.EndTask (); + } } async Task WriteFileInternal (string file, string sourceFile, Solution solution, bool saveProjects, ProgressMonitor monitor) @@ -91,7 +89,7 @@ namespace MonoDevelop.Projects.MSBuild try { monitor.BeginStep (); item.SavingSolution = true; - await item.SaveAsync (monitor); + await item.SaveAsync (monitor).ConfigureAwait (false); } finally { item.SavingSolution = false; } @@ -377,21 +375,19 @@ namespace MonoDevelop.Projects.MSBuild try { monitor.BeginTask (string.Format (GettextCatalog.GetString ("Loading solution: {0}"), fileName), 1); monitor.BeginStep (); - await sol.OnBeginLoad (); + await sol.OnBeginLoad ().ConfigureAwait (false); var projectLoadMonitor = monitor as ProjectLoadProgressMonitor; if (projectLoadMonitor != null) projectLoadMonitor.CurrentSolution = sol; - await Task.Run (() => { - sol.ReadSolution (monitor); - }); + sol.ReadSolution (monitor); } catch (Exception ex) { monitor.ReportError (GettextCatalog.GetString ("Could not load solution: {0}", fileName), ex); - await sol.OnEndLoad (); + await sol.OnEndLoad ().ConfigureAwait (false); sol.NotifyItemReady (); monitor.EndTask (); throw; } - await sol.OnEndLoad (); + await sol.OnEndLoad ().ConfigureAwait (false); sol.NotifyItemReady (); monitor.EndTask (); return sol; @@ -533,7 +529,7 @@ namespace MonoDevelop.Projects.MSBuild } } monitor.Step (1); - }); + }, TaskScheduler.Default); loadTasks.Add (ft); // Limit the number of concurrent tasks. Por solutions with many projects, spawning one thread per diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs index ffaaef930f..4bb348eddc 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/CompiledAssemblyProject.cs @@ -215,11 +215,9 @@ namespace MonoDevelop.Projects public override Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, SolutionLoadContext ctx, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) { - return Task<SolutionItem>.Factory.StartNew (delegate { - CompiledAssemblyProject p = new CompiledAssemblyProject (); - p.LoadFrom (fileName); - return p; - }); + CompiledAssemblyProject p = new CompiledAssemblyProject (); + p.LoadFrom (fileName); + return Task.FromResult<SolutionItem> (p); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MSBuildSerializationExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MSBuildSerializationExtension.cs index f5b1f1922c..8172ed8212 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MSBuildSerializationExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MSBuildSerializationExtension.cs @@ -41,26 +41,22 @@ namespace MonoDevelop.Projects return false; } - public override Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, SolutionLoadContext ctx, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) + public override async Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, SolutionLoadContext ctx, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) { - return Task.Run (() => { - foreach (var f in MSBuildFileFormat.GetSupportedFormats ()) { - if (f.CanReadFile (fileName, typeof(SolutionItem))) - return MSBuildProjectService.LoadItem (monitor, fileName, f, typeGuid, itemGuid, ctx); - } - throw new NotSupportedException (); - }); + foreach (var f in MSBuildFileFormat.GetSupportedFormats ()) { + if (f.CanReadFile (fileName, typeof(SolutionItem))) + return await MSBuildProjectService.LoadItem (monitor, fileName, f, typeGuid, itemGuid, ctx).ConfigureAwait (false); + } + throw new NotSupportedException (); } - public override Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) + public override async Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) { - return Task.Run (async () => { - foreach (var f in MSBuildFileFormat.GetSupportedFormats ()) { - if (f.CanReadFile (fileName, typeof (WorkspaceItem))) - return (WorkspaceItem)await f.ReadFile (fileName, typeof (WorkspaceItem), monitor).ConfigureAwait (false); - } - throw new NotSupportedException (); - }); + foreach (var f in MSBuildFileFormat.GetSupportedFormats ()) { + if (f.CanReadFile (fileName, typeof (WorkspaceItem))) + return (WorkspaceItem)await f.ReadFile (fileName, typeof (WorkspaceItem), monitor).ConfigureAwait (false); + } + throw new NotSupportedException (); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs index 3df66730cc..a8a7c72dd0 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs @@ -415,9 +415,7 @@ namespace MonoDevelop.Projects protected override Task OnLoad (ProgressMonitor monitor) { - return Task.Run (async delegate { - await LoadAsync (monitor); - }); + return LoadAsync (monitor); } async Task LoadAsync (ProgressMonitor monitor) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs index 7997a72146..4b57332e0d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs @@ -54,7 +54,7 @@ namespace MonoDevelop.Projects TargetFramework defaultTargetFramework; string defaultPlatformTarget = "anycpu"; - static readonly TargetFrameworkMoniker DefaultTargetFrameworkId = TargetFrameworkMoniker.NET_4_7; + static readonly TargetFrameworkMoniker DefaultTargetFrameworkId = TargetFrameworkMoniker.NET_4_7_2; public const string BuildTarget = "Build"; public const string CleanTarget = "Clean"; @@ -123,7 +123,7 @@ namespace MonoDevelop.Projects var r = GetObjectReaderForFile (file, typeof(SolutionItem)); if (r == null) throw new UnknownSolutionItemTypeException (); - SolutionItem loadedItem = await r.LoadSolutionItem (monitor, ctx, file, format, typeGuid, itemGuid); + SolutionItem loadedItem = await Task.Run (() => r.LoadSolutionItem (monitor, ctx, file, format, typeGuid, itemGuid)); if (loadedItem != null) { loadedItem.NeedsReload = false; UpdateReadSolutionItemMetadata (metadata, loadedItem); @@ -216,7 +216,7 @@ namespace MonoDevelop.Projects var r = GetObjectReaderForFile (file, typeof(WorkspaceItem)); if (r == null) throw new InvalidOperationException ("Invalid file format: " + file); - WorkspaceItem item = await r.LoadWorkspaceItem (monitor, fullpath); + WorkspaceItem item = await Task.Run (() => r.LoadWorkspaceItem (monitor, fullpath)); if (item != null) item.NeedsReload = false; else diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectExtension.cs index e7514fe8f1..195b692af1 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectExtension.cs @@ -38,6 +38,8 @@ namespace MonoDevelop.Projects [ExportProjectModelExtension] public class SdkProjectExtension : DotNetProjectExtension { + HashSet<(string Name, string Include)> evaluatedItems; + MSBuildSdkProject msbuildSdkProject = new MSBuildSdkProject (); string[] cachedBuildActions; @@ -53,8 +55,7 @@ namespace MonoDevelop.Projects /// </summary> internal static bool FileShouldBeHidden (FilePath file) { - return file.HasExtension (".userprefs") || - file.FileName == ".DS_Store"; + return file.HasExtension (".userprefs") || file.HasFileName (".DS_Store"); } public IEnumerable<string> TargetFrameworks { @@ -109,6 +110,7 @@ namespace MonoDevelop.Projects Project.UseFileWatcher = true; cachedBuildActions = null; + evaluatedItems = null; } internal protected override void OnWriteProject (ProgressMonitor monitor, MSBuildProject msproject) @@ -129,36 +131,15 @@ namespace MonoDevelop.Projects { var sourceFiles = await base.OnGetSourceFiles (monitor, configuration); - return AddMissingProjectFiles (sourceFiles, configuration); - } - - ImmutableArray<ProjectFile> AddMissingProjectFiles (ImmutableArray<ProjectFile> files, ConfigurationSelector configuration) - { - ImmutableArray<ProjectFile>.Builder missingFiles = null; - foreach (ProjectFile existingFile in Project.Files.Where (file => file.BuildAction == BuildAction.Compile)) { - if (!files.Any (file => file.FilePath == existingFile.FilePath)) { - if (missingFiles == null) - missingFiles = ImmutableArray.CreateBuilder<ProjectFile> (); - missingFiles.Add (existingFile); - } - } - // Ensure generated assembly info file is available to type system. It is created in the obj // directory and is excluded from the project with a wildcard exclude but the type system needs it to // ensure the project's assembly information is correct to prevent diagnostic errors. var generatedAssemblyInfoFile = GetGeneratedAssemblyInfoFile (configuration); if (generatedAssemblyInfoFile != null) { - if (missingFiles == null) - missingFiles = ImmutableArray.CreateBuilder<ProjectFile> (); - missingFiles.Add (generatedAssemblyInfoFile); + return sourceFiles.Add (generatedAssemblyInfoFile); } - if (missingFiles == null) - return files; - - missingFiles.Capacity = missingFiles.Count + files.Length; - missingFiles.AddRange (files); - return missingFiles.MoveToImmutable (); + return sourceFiles; } ProjectFile GetGeneratedAssemblyInfoFile (ConfigurationSelector configuration) @@ -185,14 +166,23 @@ namespace MonoDevelop.Projects // project file is being saved. } - bool IsFSharpSdkProject () + bool? isFSharpSdkProject; + bool IsLegacyFSharpSdkProject () { - var sdks = Project.MSBuildProject.GetReferencedSDKs (); - for (var i = 0; i < sdks.Length; i++) { - if (sdks [i].Contains ("FSharp")) - return true; + if (isFSharpSdkProject is null) { + isFSharpSdkProject = ContainsFSharpSdk (Project.MSBuildProject.GetReferencedSDKs ()); + } + + return isFSharpSdkProject.Value; + + static bool ContainsFSharpSdk (string[] sdks) + { + for (var i = 0; i < sdks.Length; i++) { + if (sdks [i].Contains ("FSharp")) + return true; + } + return false; } - return false; } internal protected override bool OnGetSupportsImportedItem (IMSBuildItemEvaluated buildItem) @@ -200,7 +190,7 @@ namespace MonoDevelop.Projects if (!IsBuildActionSupported (buildItem.Name)) return false; - if (IsFSharpSdkProject ()) { + if (IsLegacyFSharpSdkProject ()) { // Ignore imported F# files. F# files are defined in the main project. // This prevents duplicate F# files when a new project is first created. if (buildItem.Include.EndsWith (".fs", StringComparison.OrdinalIgnoreCase)) @@ -210,11 +200,15 @@ namespace MonoDevelop.Projects if (IsFromSharedProject (buildItem)) return false; - // HACK: Remove any imported items that are not in the EvaluatedItems - // This may happen if a condition excludes the item. All items passed to the - // OnGetSupportsImportedItem are from the EvaluatedItemsIgnoringCondition - return Project.MSBuildProject.EvaluatedItems - .Any (item => item.IsImported && item.Name == buildItem.Name && item.Include == buildItem.Include); + evaluatedItems ??= CreateEvaluatedItemsCache (Project.MSBuildProject); + return evaluatedItems.Contains ((buildItem.Name, buildItem.Include)); + + static HashSet<(string, string)> CreateEvaluatedItemsCache (MSBuildProject project) + => new HashSet<(string Name, string Include)> ( + project.EvaluatedItems + .Where (x => x.IsImported) + .Select (x => (x.Name, x.Include)) + ); } /// <summary> @@ -262,6 +256,8 @@ namespace MonoDevelop.Projects internal protected override async Task OnReevaluateProject (ProgressMonitor monitor) { await base.OnReevaluateProject (monitor); + + isFSharpSdkProject = null; UpdateHiddenFiles (Project.Files); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectReader.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectReader.cs index 6765edf762..2d42a1576f 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectReader.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SdkProjectReader.cs @@ -96,15 +96,13 @@ namespace MonoDevelop.Projects return null; } - public override Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, SolutionLoadContext ctx, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) + public override async Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, SolutionLoadContext ctx, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) { - return Task.Run (() => { - if (CanRead (fileName, typeof (SolutionItem))) { - return MSBuildProjectService.LoadItem (monitor, fileName, MSBuildFileFormat.VS2012, typeGuid, itemGuid, ctx); - } + if (CanRead (fileName, typeof (SolutionItem))) { + return await MSBuildProjectService.LoadItem (monitor, fileName, MSBuildFileFormat.VS2012, typeGuid, itemGuid, ctx); + } - throw new NotSupportedException (); - }); + throw new NotSupportedException (); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs index 8dce5f1327..2a9c0c96e3 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/SolutionItem.cs @@ -460,7 +460,7 @@ namespace MonoDevelop.Projects if (ItemExtension.OnCheckHasSolutionData () && !SavingSolution && ParentSolution != null) { // The project has data that has to be saved in the solution, but the solution is not being saved. Do it now. - await SolutionFormat.SlnFileFormat.WriteFile (ParentSolution.FileName, ParentSolution, false, monitor); + await Task.Run (() => SolutionFormat.SlnFileFormat.WriteFile (ParentSolution.FileName, ParentSolution, false, monitor)); ParentSolution.NeedsReload = false; } } @@ -1232,12 +1232,12 @@ namespace MonoDevelop.Projects protected virtual Task OnLoad (ProgressMonitor monitor) { - return Task.FromResult (0); + return Task.CompletedTask; } protected internal virtual Task OnSave (ProgressMonitor monitor) { - return Task.FromResult (0); + return Task.CompletedTask; } public FilePath GetAbsoluteChildPath (FilePath relPath) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs index 639c96b249..7b4dec5b75 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceItem.cs @@ -276,7 +276,7 @@ namespace MonoDevelop.Projects FileService.RequestFileEdit (f); try { fileStatusTracker.BeginSave (); - await ItemExtension.Save (monitor); + await Task.Run (() => ItemExtension.Save (monitor)); await OnSaveUserProperties (); // Call the virtual to avoid the lock OnSaved (new WorkspaceItemEventArgs (this)); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObjectReader.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObjectReader.cs index 9c0a453544..b205558a76 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObjectReader.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceObjectReader.cs @@ -41,12 +41,12 @@ namespace MonoDevelop.Projects public virtual Task<SolutionItem> LoadSolutionItem (ProgressMonitor monitor, SolutionLoadContext ctx, string fileName, MSBuildFileFormat expectedFormat, string typeGuid, string itemGuid) { - throw new NotSupportedException (); + return Task.FromException<SolutionItem> (new NotSupportedException ()); } public virtual Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) { - throw new NotSupportedException (); + return Task.FromException<WorkspaceItem> (new NotSupportedException ()); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceSerializationExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceSerializationExtension.cs index f96243af37..7e4c748ab5 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceSerializationExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/WorkspaceSerializationExtension.cs @@ -45,13 +45,11 @@ namespace MonoDevelop.Projects return false; } - public override Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) + public override async Task<WorkspaceItem> LoadWorkspaceItem (ProgressMonitor monitor, string fileName) { - return Task.Run (async () => { - var workspaceItem = ReadWorkspaceItemFile (fileName, monitor); - await workspaceItem.LoadUserProperties ().ConfigureAwait (false); - return workspaceItem; - }); + var workspaceItem = ReadWorkspaceItemFile (fileName, monitor); + await workspaceItem.LoadUserProperties ().ConfigureAwait (false); + return workspaceItem; } WorkspaceItem ReadWorkspaceItemFile (FilePath fileName, ProgressMonitor monitor) diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml index 70c27e8047..a08baa0058 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml @@ -543,6 +543,16 @@ defaultHandler = "MonoDevelop.Ide.Commands.ReopenClosedTabHandler" _label = "Reopen Closed Tab" _description = "Opens the last tab that has been closed"/> + <Command id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllExceptPinned" + defaultHandler = "MonoDevelop.Ide.Commands.CloseAllExceptPinnedHandler" + _label = "Close All Except _Pinned" + _description = "Close all files except pinned" + macShortcut = "Meta|Shift|P" /> + <Command id = "MonoDevelop.Ide.Commands.FileTabCommands.PinTab" + defaultHandler = "MonoDevelop.Ide.Commands.PinTabHandler" + _label = "_Pin Tab" + _description = "Pin/Unpin current Tab selected" + macShortcut = "Meta|Alt|P" /> </Category> <!-- ViewCommands --> @@ -935,7 +945,7 @@ _label = "Find Next Like Selection" _description = "Search forwards for the selected text" shortcut = "Control|F3" - macShortcut = "Meta|E Meta|F3" /> + macShortcut = "Meta|F3" /> <Command id = "MonoDevelop.Ide.Commands.SearchCommands.FindPreviousSelection" _label = "Find Previous Like Selection" _description = "Search backwards for the selected text" @@ -1012,10 +1022,11 @@ shortcut = "Control|I" macShortcut = "Meta|L" /> <Command id = "MonoDevelop.Ide.Commands.SearchCommands.UseSelectionForFind" - _label = "Find Like Selection" - _description = "Uses the current selection as find string"/> + _label = "Use Selection for Find" + _description = "Uses the current selection as find string" + macShortcut = "Meta|E" /> <Command id = "MonoDevelop.Ide.Commands.SearchCommands.UseSelectionForReplace" - _label = "Replace Like Selection" + _label = "Use Selection for Replace" _description = "Uses the current selection as replace string"/> </Category> diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml index feb4dea335..e9d314c67c 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MainMenu.addin.xml @@ -157,7 +157,7 @@ <CommandItem id = "MonoDevelop.Ide.Commands.SearchCommands.FindPrevious" /> <CommandItem id = "MonoDevelop.Ide.Commands.SearchCommands.FindNext" /> <CommandItem id = "MonoDevelop.Ide.Commands.SearchCommands.FindNextSelection" /> - + <CommandItem id = "MonoDevelop.Ide.Commands.SearchCommands.UseSelectionForFind" /> <SeparatorItem id = "SearchSeparator2" /> <CommandItem id = "MonoDevelop.Ide.Commands.SearchCommands.FindInFiles" /> <CommandItem id = "MonoDevelop.Ide.Commands.SearchCommands.ReplaceInFiles" /> diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml index 70690194fb..c25b4e1b7c 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml @@ -348,8 +348,10 @@ <Extension path = "/MonoDevelop/Ide/ContextMenu/DocumentTab"> <CommandItem id = "MonoDevelop.Ide.Commands.FileCommands.CloseFile" /> <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAll" /> + <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllExceptPinned" /> <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllButThis" /> <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.CloseAllToTheRight" /> + <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.PinTab" /> <CommandItem id = "MonoDevelop.Ide.Commands.FileTabCommands.ReopenClosedTab" /> <SeparatorItem id = "CloseSeparator" /> <CommandItem id = "MonoDevelop.Ide.Commands.FileCommands.Save" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs index fd92edcc49..4f7753ace4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/ActionCommand.cs @@ -109,15 +109,16 @@ namespace MonoDevelop.Components.Commands info.Visible = false; return; } - defaultHandler = (CommandHandler) Activator.CreateInstance (DefaultHandlerType); + defaultHandler = (CommandHandler)Activator.CreateInstance (DefaultHandlerType); } if (commandArray) { info.ArrayInfo = new CommandArrayInfo (info); defaultHandler.InternalUpdate (info.ArrayInfo); - } - else + } else defaultHandler.InternalUpdate (info); } + + internal RuntimeAddin RuntimeAddin => defaultHandlerAddin; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs index 326e5a8299..67aea3f5a5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs @@ -478,8 +478,14 @@ namespace MonoDevelop.Components.Commands } #endif // Handle the GDK key via MD commanding - if (ProcessKeyEventCore (ev)) - return true; + try { + if (ProcessKeyEventCore (ev)) { + return true; + } + } catch (Exception ex) { + LoggingService.LogInternalError ("Exception while parsing command", ex); + return false; + } #if MAC // Otherwise if we have a native first responder that is not the GdkQuartzView @@ -547,6 +553,10 @@ namespace MonoDevelop.Components.Commands return false; } + if (commands == null) { + return false; + } + var toplevelFocus = IdeApp.Workbench.HasToplevelFocus; var conflict = new List<Command> (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs index b12ecad3fb..1697cba57a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebook.cs @@ -36,6 +36,7 @@ using MonoDevelop.Ide; using MonoDevelop.Components.AtkCocoaHelper; using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Shell; +using System.Linq; namespace MonoDevelop.Components.DockNotebook { @@ -136,6 +137,7 @@ namespace MonoDevelop.Components.DockNotebook public event TabsReorderedHandler TabsReordered; public event EventHandler<TabEventArgs> TabClosed; + public event EventHandler<TabEventArgs> TabPinned; public event EventHandler<TabEventArgs> TabActivated; public event EventHandler<TabEventArgs> PageAdded; @@ -349,6 +351,8 @@ namespace MonoDevelop.Components.DockNotebook PageAdded?.Invoke (this, new TabEventArgs { Tab = tab, }); + tab.OnChangingPinned = OnTabPinned; + NotebookChanged?.Invoke (this, EventArgs.Empty); return tab; @@ -360,6 +364,24 @@ namespace MonoDevelop.Components.DockNotebook ((DockNotebookTab)pages [n]).Index = n; } + void OnTabPinned (DockNotebookTab sender, bool value) + { + if (pages.Count == 1) + return; + + var stickedPages = pages.Where (p => p.IsPinned); + var normalPages = pages.Where (p => !p.IsPinned); + + if (value) { + if (stickedPages.Any ()) + ReorderTab (sender, normalPages.MinValueOrDefault (s => s.Index) ?? stickedPages.MaxValueOrDefault (s => s.Index), false); + else + ReorderTab (sender, pages.FirstOrDefault (), false); + } else { + ReorderTab (sender, stickedPages.MaxValueOrDefault (s => s.Index) ?? normalPages.MinValueOrDefault (s => s.Index), false); + } + } + public DockNotebookTab GetTab (int n) { if (n < 0 || n >= pages.Count) @@ -388,8 +410,11 @@ namespace MonoDevelop.Components.DockNotebook NotebookChanged?.Invoke (this, EventArgs.Empty); } - internal void ReorderTab (DockNotebookTab tab, DockNotebookTab targetTab) + internal void ReorderTab (DockNotebookTab tab, DockNotebookTab targetTab, bool pinCheck = true) { + if (pinCheck && tab.IsPinned != targetTab.IsPinned) + return; + if (tab == targetTab) return; int targetPos = targetTab.Index; @@ -412,6 +437,12 @@ namespace MonoDevelop.Components.DockNotebook TabClosed (this, new TabEventArgs () { Tab = tab }); } + internal void OnPinTab (DockNotebookTab tab) + { + if (TabPinned != null) + TabPinned (this, new TabEventArgs () { Tab = tab }); + } + internal void OnActivateTab (DockNotebookTab tab) { if (TabActivated != null) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs index fbd6a1ed22..53c1b62205 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs @@ -35,6 +35,9 @@ namespace MonoDevelop.Components.DockNotebook { class DockNotebookTab: IAnimatable, IDisposable { + public System.Action<DockNotebookTab, bool> OnChangingPinned; + public System.Action<DockNotebookTab, bool> OnChangedPinned; + DockNotebook notebook; readonly TabStrip strip; @@ -47,6 +50,8 @@ namespace MonoDevelop.Components.DockNotebook Xwt.Drawing.Image icon; Widget content; + internal Cairo.Rectangle PinButtonActiveArea; + Gdk.Rectangle allocation; internal Gdk.Rectangle Allocation { get { @@ -113,6 +118,19 @@ namespace MonoDevelop.Components.DockNotebook public double DirtyStrength { get; set; } + bool isPinned; + public bool IsPinned { + get { return isPinned; } + set { + if (isPinned == value) + return; + OnChangingPinned?.Invoke (this, value); + isPinned = value; + strip.Update (); + OnChangedPinned?.Invoke (this, value); + } + } + void IAnimatable.BatchBegin () { } void IAnimatable.BatchCommit () { QueueDraw (); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs index 7f7743b443..6f32941ec4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs @@ -37,6 +37,7 @@ using MonoDevelop.Components.Docking; using MonoDevelop.Ide.Gui; using MonoDevelop.Ide; using System.Runtime.InteropServices; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Components.DockNotebook { @@ -49,6 +50,8 @@ namespace MonoDevelop.Components.DockNotebook static Xwt.Drawing.Image tabbarBackImage = Xwt.Drawing.Image.FromResource ("tabbar-back.9.png"); static Xwt.Drawing.Image tabCloseImage = Xwt.Drawing.Image.FromResource ("tab-close-9.png"); static Xwt.Drawing.Image tabDirtyImage = Xwt.Drawing.Image.FromResource ("tab-dirty-9.png"); + static Xwt.Drawing.Image tabPinnedImage = Xwt.Drawing.Image.FromResource ("tab-pinned-9.png"); + static Xwt.Drawing.Image tabUnPinnedImage = Xwt.Drawing.Image.FromResource ("tab-unpinned-9.png"); HBox innerBox; @@ -84,8 +87,11 @@ namespace MonoDevelop.Components.DockNotebook static readonly int VerticalTextSize = 11; const int TabSpacing = 0; const int LeanWidth = 12; + const int ButtonSize = 14; const double CloseButtonMarginRight = 0; const double CloseButtonMarginBottom = -1.0; + const double PinButtonMarginRight = 0; + const double PinButtonMarginBottom = -1.0; const int TextOffset = 1; @@ -235,7 +241,7 @@ namespace MonoDevelop.Components.DockNotebook tab.AccessibilityPressCloseButton += OnAccessibilityPressCloseButton; tab.AccessibilityShowMenu += OnAccessibilityShowMenu; } - }
+ } UpdateAccessibilityTabs (); notebook.PageAdded += PageAddedHandler; notebook.PageRemoved += PageRemovedHandler; @@ -254,14 +260,14 @@ namespace MonoDevelop.Components.DockNotebook void PageAddedHandler (object sender, TabEventArgs args) { - var tab = args.Tab;
+ var tab = args.Tab; - if (tab.Accessible != null) {
+ if (tab.Accessible != null) { Accessible.AddAccessibleElement (tab.Accessible); - Accessible.AddAccessibleElement (tab.CloseButtonAccessible);
-
- tab.AccessibilityPressTab += OnAccessibilityPressTab;
- tab.AccessibilityPressCloseButton += OnAccessibilityPressCloseButton;
+ Accessible.AddAccessibleElement (tab.CloseButtonAccessible); + + tab.AccessibilityPressTab += OnAccessibilityPressTab; + tab.AccessibilityPressCloseButton += OnAccessibilityPressCloseButton; tab.AccessibilityShowMenu += OnAccessibilityShowMenu; } @@ -272,13 +278,13 @@ namespace MonoDevelop.Components.DockNotebook void PageRemovedHandler (object sender, TabEventArgs args) { - var tab = args.Tab;
+ var tab = args.Tab; + + if (tab.Accessible != null) { + tab.AccessibilityPressTab -= OnAccessibilityPressTab; + tab.AccessibilityPressCloseButton -= OnAccessibilityPressCloseButton; + tab.AccessibilityShowMenu -= OnAccessibilityShowMenu; - if (tab.Accessible != null) {
- tab.AccessibilityPressTab -= OnAccessibilityPressTab;
- tab.AccessibilityPressCloseButton -= OnAccessibilityPressCloseButton;
- tab.AccessibilityShowMenu -= OnAccessibilityShowMenu;
-
Accessible.RemoveAccessibleElement (tab.Accessible); Accessible.RemoveAccessibleElement (tab.CloseButtonAccessible); } @@ -290,11 +296,11 @@ namespace MonoDevelop.Components.DockNotebook UpdateAccessibilityTabs (); } - void PageReorderedHandler (DockNotebookTab tab, int oldPlacement, int newPlacement)
+ void PageReorderedHandler (DockNotebookTab tab, int oldPlacement, int newPlacement) { QueueResize (); - UpdateAccessibilityTabs ();
+ UpdateAccessibilityTabs (); } void UpdateAccessibilityTabs () @@ -553,6 +559,7 @@ namespace MonoDevelop.Components.DockNotebook return base.OnMotionNotifyEvent (evnt); } + bool overPinOnPress; bool overCloseOnPress; bool allowDoubleClick; @@ -573,6 +580,13 @@ namespace MonoDevelop.Components.DockNotebook } overCloseOnPress = false; + // Don't select the tab if we are clicking the pin button + if (IsOverPinButton (t, (int)evnt.X, (int)evnt.Y)) { + overPinOnPress = true; + return true; + } + overPinOnPress = false; + if (evnt.Type == EventType.TwoButtonPress) { if (allowDoubleClick) { notebook.OnActivateTab (t); @@ -604,15 +618,22 @@ namespace MonoDevelop.Components.DockNotebook return base.OnButtonReleaseEvent (evnt); } - if (!draggingTab && overCloseOnPress) { + if (!draggingTab) { var t = FindTab ((int)evnt.X, (int)evnt.Y); - if (t != null && IsOverCloseButton (t, (int)evnt.X, (int)evnt.Y)) { + if (t != null && overCloseOnPress && IsOverCloseButton (t, (int)evnt.X, (int)evnt.Y)) { notebook.OnCloseTab (t); allowDoubleClick = false; return true; + } else if (t != null && overPinOnPress && IsOverPinButton (t, (int)evnt.X, (int)evnt.Y)) { + t.IsPinned = !t.IsPinned; + notebook.OnPinTab (t); + allowDoubleClick = false; + QueueDraw (); + return true; } } overCloseOnPress = false; + overPinOnPress = false; allowDoubleClick = true; if (dragX != 0) this.Animate ("EndDrag", @@ -935,6 +956,7 @@ namespace MonoDevelop.Components.DockNotebook // Cancel drag operations and animations buttonPressedOnTab = false; overCloseOnPress = false; + overPinOnPress = false; allowDoubleClick = true; draggingTab = false; dragX = 0; @@ -966,6 +988,11 @@ namespace MonoDevelop.Components.DockNotebook return tab != null && tab.CloseButtonActiveArea.Contains (x, y); } + static bool IsOverPinButton (DockNotebookTab tab, int x, int y) + { + return tab != null && tab.PinButtonActiveArea.Contains (x, y); + } + public void Update () { if (!tracker.Hovered) { @@ -1145,7 +1172,7 @@ namespace MonoDevelop.Components.DockNotebook leftPadding = (leftPadding * Math.Min (1.0, Math.Max (0.5, (tabBounds.Width - 30) / 70.0))); double bottomPadding = active ? TabActivePadding.Bottom : TabPadding.Bottom; - DrawTabBackground (this, ctx, allocation, tabBounds.Width, tabBounds.X, active); + DrawTabBackground (this, ctx, allocation, tabBounds.Width, tabBounds.X, active, tab.IsPinned); ctx.LineWidth = 1; ctx.NewPath (); @@ -1158,9 +1185,17 @@ namespace MonoDevelop.Components.DockNotebook tab.CloseButtonActiveArea = closeButtonAlloation.Inflate (2, 2); + var spinButtonAllocation = new Cairo.Rectangle (closeButtonAlloation.X - rightPadding - PinButtonMarginRight, + closeButtonAlloation.Y, + tabPinnedImage.Width, tabPinnedImage.Height); + + tab.PinButtonActiveArea = spinButtonAllocation.Inflate (2, 2); + bool closeButtonHovered = tracker.Hovered && tab.CloseButtonActiveArea.Contains (tracker.MousePosition); + bool pinButtonHovered = tracker.Hovered && tab.PinButtonActiveArea.Contains (tracker.MousePosition); bool tabHovered = tracker.Hovered && tab.Allocation.Contains (tracker.MousePosition); - bool drawCloseButton = active || tabHovered || focused; + bool drawCloseButton = tab.IsPinned || (active || tabHovered || focused); + bool drawPinButton = tab.IsPinned || tabHovered; if (!closeButtonHovered && tab.DirtyStrength > 0.5) { ctx.DrawImage (this, tabDirtyImage, closeButtonAlloation.X, closeButtonAlloation.Y); @@ -1170,11 +1205,17 @@ namespace MonoDevelop.Components.DockNotebook if (drawCloseButton) ctx.DrawImage (this, tabCloseImage.WithAlpha ((closeButtonHovered ? 1.0 : 0.5) * tab.Opacity), closeButtonAlloation.X, closeButtonAlloation.Y); + if (drawPinButton) + ctx.DrawImage (this, (tab.IsPinned ? tabPinnedImage : tabUnPinnedImage).WithAlpha ((pinButtonHovered ? 1.0 : 0.5) * tab.Opacity), spinButtonAllocation.X, spinButtonAllocation.Y); + // Render Text double tw = tabBounds.Width - (leftPadding + rightPadding); if (drawCloseButton || tab.DirtyStrength > 0.5) tw -= closeButtonAlloation.Width / 2; + if (drawPinButton || tab.DirtyStrength > 0.5) + tw -= spinButtonAllocation.Width / 2 + rightPadding; + double tx = tabBounds.X + leftPadding; var baseline = la.GetLine (0).Layout.GetPixelBaseline (); double ty = tabBounds.Height - bottomPadding - baseline; @@ -1199,11 +1240,11 @@ namespace MonoDevelop.Components.DockNotebook ctx.SetSource (lg); Pango.CairoHelper.ShowLayout (ctx, la.GetLine (0).Layout); } - }
+ } la.Dispose (); } - static void DrawTabBackground (Widget widget, Context ctx, Gdk.Rectangle allocation, int contentWidth, int px, bool active = true) + static void DrawTabBackground (Widget widget, Context ctx, Gdk.Rectangle allocation, int contentWidth, int px, bool active = true, bool isPinned = false) { int lean = Math.Min (LeanWidth, contentWidth / 2); int halfLean = lean / 2; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs index 1a54f5e800..9556f15db2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs @@ -39,6 +39,7 @@ using Animations = Xwt.Motion.AnimationExtensions; using MonoDevelop.Core; using MonoDevelop.Ide.Fonts; using MonoDevelop.Ide; +using Gdk; namespace MonoDevelop.Components.Docking { @@ -369,6 +370,12 @@ namespace MonoDevelop.Components.Docking if (autoShowTimeout == uint.MaxValue) { autoShowTimeout = GLib.Timeout.Add (bar.Frame.AutoShowDelay, delegate { autoShowTimeout = uint.MaxValue; + + // Check is mouse over - it may be that the LeaveEvent wasn't fired becaues of a gtk bug. So we double check as a work around. + GetPointer (out int x, out int y); + bool isMouseOver = x >= 0 && y >= 0 && x <= Allocation.Width && y <= Allocation.Height; + if (!isMouseOver) + return false; AutoShow (); return false; }); @@ -430,11 +437,23 @@ namespace MonoDevelop.Components.Docking protected override bool OnEnterNotifyEvent (Gdk.EventCrossing evnt) { + ScheduleAutoShowOnHover(); + return base.OnEnterNotifyEvent (evnt); + } + + protected override bool OnMotionNotifyEvent (EventMotion evnt) + { + ScheduleAutoHide (true); + ScheduleAutoShowOnHover (); + return base.OnMotionNotifyEvent (evnt); + } + + void ScheduleAutoShowOnHover () + { if (bar.HoverActivationEnabled && autoShowFrame == null) { ScheduleAutoShow (); QueueDraw (); } - return base.OnEnterNotifyEvent (evnt); } void OnPerformPress (object sender, EventArgs args) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/CommandSearchCategory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/CommandSearchCategory.cs index 9084175639..ab8d215712 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/CommandSearchCategory.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/CommandSearchCategory.cs @@ -26,27 +26,27 @@ using System; using System.Threading; using System.Threading.Tasks; -using MonoDevelop.Core; +using System.Linq; using System.Collections.Generic; -using MonoDevelop.Core.Instrumentation; -using MonoDevelop.Projects; -using MonoDevelop.Ide.Gui; -using MonoDevelop.Ide; -using ICSharpCode.NRefactory.TypeSystem; -using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Core; using MonoDevelop.Core.Text; -using Gtk; -using System.Linq; using MonoDevelop.Components.Commands; +using MonoDevelop.Ide; namespace MonoDevelop.Components.MainToolbar { class CommandSearchCategory : SearchCategory { static readonly List<Tuple<Command, string>> allCommands; + static readonly Mono.Addins.RuntimeAddin currentRuntimeAddin; static CommandSearchCategory () { + // The AddinManager is not thread safe, so we need to make sure we're + // calling from the main thread. + Runtime.AssertMainThread (); + currentRuntimeAddin = Mono.Addins.AddinManager.CurrentAddin; + var hiddenCategory = GettextCatalog.GetString ("Hidden"); allCommands = IdeApp.CommandService.GetCommands () .Where (cmd => (cmd as ActionCommand)?.CommandArray != true && cmd.Category != hiddenCategory) @@ -58,7 +58,7 @@ namespace MonoDevelop.Components.MainToolbar { } - string[] validTags = new [] { "cmd", "command", "c" }; + readonly string[] validTags = { "cmd", "command", "c" }; public override string [] Tags { get { @@ -86,10 +86,12 @@ namespace MonoDevelop.Components.MainToolbar break; var cmd = cmdTuple.Item1; var matchString = cmdTuple.Item2; - int rank; - if (matcher.CalcMatchRank (matchString, out rank)) + if (matcher.CalcMatchRank (matchString, out var rank)) { + if ((cmd as ActionCommand)?.RuntimeAddin == currentRuntimeAddin) + rank += 1; // we prefer commands comming from the addin searchResultCallback.ReportResult (new CommandResult (cmd, null, route, pattern.Pattern, matchString, rank)); + } } } catch (OperationCanceledException) { } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs index 0a25ce6227..0312ddd9bf 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs @@ -45,20 +45,10 @@ using System.Windows.Input; namespace MonoDevelop.Components { - public static class GtkWorkarounds + public static partial class GtkWorkarounds { - const string LIBOBJC ="/usr/lib/libobjc.dylib"; const string USER32DLL = "User32.dll"; - [DllImport (LIBOBJC, EntryPoint = "sel_registerName")] - static extern IntPtr sel_registerName (string selector); - - [DllImport (LIBOBJC, EntryPoint = "objc_getClass")] - static extern IntPtr objc_getClass (string klass); - - [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] - static extern IntPtr objc_msgSend_IntPtr (IntPtr klass, IntPtr selector); - [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] static extern void objc_msgSend_void_bool (IntPtr klass, IntPtr selector, bool arg); @@ -83,9 +73,6 @@ namespace MonoDevelop.Components [DllImport (LIBOBJC, EntryPoint = "objc_msgSend_stret")] static extern void objc_msgSend_CGRect64 (out CGRect64 rect, IntPtr klass, IntPtr selector); - [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] - static extern void objc_msgSend_NSInt64_NSInt32 (IntPtr klass, IntPtr selector, int arg); - [DllImport (PangoUtil.LIBQUARTZ)] static extern IntPtr gdk_quartz_window_get_nswindow (IntPtr window); @@ -109,7 +96,7 @@ namespace MonoDevelop.Components static IntPtr cls_NSScreen; static IntPtr sel_screens, sel_objectEnumerator, sel_nextObject, sel_frame, sel_visibleFrame, - sel_requestUserAttention, sel_setHasShadow, sel_invalidateShadow, sel_terminate; + sel_requestUserAttention, sel_setHasShadow, sel_invalidateShadow; static IntPtr sharedApp; static IntPtr cls_NSEvent; static IntPtr sel_modifierFlags; @@ -174,21 +161,9 @@ namespace MonoDevelop.Components sel_modifierFlags = sel_registerName ("modifierFlags"); sel_setHasShadow = sel_registerName ("setHasShadow:"); sel_invalidateShadow = sel_registerName ("invalidateShadow"); - sel_terminate = sel_registerName ("terminate:"); sharedApp = objc_msgSend_IntPtr (objc_getClass ("NSApplication"), sel_registerName ("sharedApplication")); } - static void MacTerminate () - { - objc_msgSend_NSInt64_NSInt32 (sharedApp, sel_terminate, 0); - } - - public static void Terminate () - { - if (Platform.IsMac) - MacTerminate (); - } - static Gdk.Rectangle MacGetUsableMonitorGeometry (Gdk.Screen screen, int monitor) { IntPtr array = objc_msgSend_IntPtr (cls_NSScreen, sel_screens); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NSViewExtensions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NSViewExtensions.cs index 9d7e89b4cc..be3192f786 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NSViewExtensions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NSViewExtensions.cs @@ -22,6 +22,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#if MAC using System; using System.Runtime.InteropServices; using Foundation; @@ -71,10 +72,12 @@ namespace AppKit } } - static readonly IntPtr sel_sortSubviewsUsingFunction_context_ = Selector.GetHandle ("sortSubviewsUsingFunction:context:"); + static readonly IntPtr sel_sortSubviewsUsingFunction_context_ = + Selector.GetHandle ("sortSubviewsUsingFunction:context:"); static void SortSubviews (NSView view, IntPtr function_pointer, IntPtr context) { MonoDevelop.Components.Mac.Messaging.void_objc_msgSend_IntPtr_IntPtr (view.Handle, sel_sortSubviewsUsingFunction_context_, function_pointer, context); } } } +#endif diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Shared/GtkWorkarounds.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Shared/GtkWorkarounds.cs new file mode 100644 index 0000000000..9ae2811d82 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Shared/GtkWorkarounds.cs @@ -0,0 +1,65 @@ +// +// GtkWorkarounds.cs +// +// Author: +// iain <iaholmes@microsoft.com> +// +// Copyright (c) 2019 +// +// 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.Runtime.InteropServices; + +using MonoDevelop.Core; + +// This partial of the GtkWorkarounds class is shared between MonoDevelop.Ide.dll and md-tool.exe +// Anything that only needs to be in MonoDevelop.Ide should go into the other part. +namespace MonoDevelop.Components +{ + public static partial class GtkWorkarounds + { + const string LIBOBJC = "/usr/lib/libobjc.dylib"; + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern IntPtr objc_msgSend_IntPtr (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "sel_registerName")] + static extern IntPtr sel_registerName (string selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_getClass")] + static extern IntPtr objc_getClass (string klass); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern void objc_msgSend_NSInt64_NSInt32 (IntPtr klass, IntPtr selector, int arg); + + static void MacTerminate () + { + var sel_terminate = sel_registerName ("terminate:"); + var app = objc_msgSend_IntPtr (objc_getClass ("NSApplication"), sel_registerName ("sharedApplication")); + + objc_msgSend_NSInt64_NSInt32 (app, sel_terminate, 0); + } + + public static void Terminate () + { + if (Platform.IsMac) + MacTerminate (); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs index c2b43f1433..bc209f4124 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileCommands.cs @@ -444,4 +444,5 @@ namespace MonoDevelop.Ide.Commands // MonoDevelop.Ide.Commands.CopyPathNameHandler Implemented in FileTabCommands.cs // MonoDevelop.Ide.Commands.FileTabCommands.ToggleMaximize Implemented in FileTabCommands.cs // MonoDevelop.Ide.Commands.FileTabCommands.ReopenClosedTab Implemented in FileTabCommands.cs + // MonoDevelop.Ide.Commands.FileTabCommands.CloseAllExceptPinned Implemented in FileTabCommands.cs } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs index 65ade3f49b..295000ea11 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Commands/FileTabCommands.cs @@ -39,6 +39,10 @@ using MonoDevelop.Ide.Gui.Dialogs; using MonoDevelop.Ide.Gui.Documents; using MonoDevelop.Ide.Gui.Shell; +using MonoDevelop.Components.DockNotebook; +using System.Collections.Immutable; +using MonoDevelop.Core; + namespace MonoDevelop.Ide.Commands { public enum FileTabCommands @@ -48,14 +52,26 @@ namespace MonoDevelop.Ide.Commands CopyPathName, ToggleMaximize, ReopenClosedTab, - CloseAllToTheRight + CloseAllToTheRight, + CloseAllExceptPinned, + PinTab, } - class CloseAllHandler : CommandHandler + class CloseAllHandler : TabCommandHandler { - protected virtual Document GetDocumentException () + protected virtual ImmutableArray<Document> GetDocumentExceptions () + { + return ImmutableArray<Document>.Empty; + } + + bool HasDistinctViewContent (ImmutableArray<Document> viewContents, Document document) { - return null; + for (int i = 0; i < viewContents.Length; i++) { + if (document.Window.Document == viewContents[i]) { + return false; + } + } + return true; } protected virtual bool StartAfterException => false; @@ -67,15 +83,15 @@ namespace MonoDevelop.Ide.Commands return; var activeNotebook = ((SdiWorkspaceWindow)active.Window).TabControl; - var except = GetDocumentException (); + var except = GetDocumentExceptions (); var docs = new List<Document> (); var dirtyDialogShown = false; - var startRemoving = except == null || !StartAfterException; + var startRemoving = !except.Any () || !StartAfterException; foreach (var doc in IdeApp.Workbench.Documents) { if (((SdiWorkspaceWindow)doc.Window).TabControl == activeNotebook) { - if (except != null && doc == except) { + if (except.Any () && !HasDistinctViewContent (except, doc)) { startRemoving = true; continue; } @@ -102,34 +118,72 @@ namespace MonoDevelop.Ide.Commands } } - class CloseAllButThisHandler : CloseAllHandler + abstract class TabCommandHandler : CommandHandler { - protected override Document GetDocumentException () - { - return IdeApp.Workbench.ActiveDocument; + protected DockNotebookTab GetTabFromActiveDocument () { + var document = IdeApp.Workbench.ActiveDocument; + if (document == null) + return null; + return ((SdiWorkspaceWindow)document.Window).DockNotebookTab; } + } + class CloseAllExceptPinnedHandler : CloseAllHandler + { protected override void Update (CommandInfo info) { - var documents = IdeApp.Workbench.Documents; - var activeDoc = IdeApp.Workbench.ActiveDocument; + info.Visible = info.Enabled = IdeApp.Workbench.Documents.Count != 0; + } - if (activeDoc == null) { - info.Enabled = false; + protected override ImmutableArray<Document> GetDocumentExceptions () + { + var active = IdeApp.Workbench.ActiveDocument; + if (active == null) + return ImmutableArray<Document>.Empty; + + var activeNotebook = ((SdiWorkspaceWindow)active.Window).TabControl; + + var contents = Microsoft.CodeAnalysis + .ImmutableArrayExtensions + .WhereAsArray ( + IdeApp.Workbench.Documents.ToImmutableArray (), + doc => ((SdiWorkspaceWindow)doc.Window).TabControl == activeNotebook && (((SdiWorkspaceWindow)doc.Window).DockNotebookTab?.IsPinned ?? false) + ); + return contents; + } + } + + class PinTabHandler : TabCommandHandler + { + protected override void Update (CommandInfo info) + { + info.Visible = info.Enabled = IdeApp.Workbench.ActiveDocument != null; + if (!info.Visible) return; + + var selectedTab = GetTabFromActiveDocument (); + if (selectedTab != null) { + info.Text = (selectedTab.IsPinned) ? GettextCatalog.GetString ("Un_pin Tab") : GettextCatalog.GetString ("_Pin Tab"); } + } - var activeNotebook = ((SdiWorkspaceWindow)activeDoc.Window).TabControl; + protected override void Run () + { + var selectedTab = GetTabFromActiveDocument (); + if (selectedTab != null) + selectedTab.IsPinned = !selectedTab.IsPinned; + } + } - // Disable if only document in tab strip - foreach (var doc in documents) { - if (doc != activeDoc && ((SdiWorkspaceWindow)doc.Window).TabControl == activeNotebook) { - info.Enabled = true; - return; - } + class CloseAllButThisHandler : CloseAllHandler + { + protected override ImmutableArray<Document> GetDocumentExceptions () + { + var active = IdeApp.Workbench.ActiveDocument; + if (active == null) { + return ImmutableArray<Document>.Empty; } - - info.Enabled = false; + return ImmutableArray.Create (active.Window.Document); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs index ef32d1a4ad..1721889cd2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CustomTools/CustomToolService.cs @@ -310,8 +310,6 @@ namespace MonoDevelop.Ide.CustomTools // Execute the generator - Exception error = null; - // Share the one pad for all Tool output. Pad pad = null; @@ -337,7 +335,6 @@ namespace MonoDevelop.Ide.CustomTools try { await tool.Generate (monitor, project, file, result); } catch (Exception ex) { - error = ex; result.UnhandledException = ex; } @@ -360,11 +357,7 @@ namespace MonoDevelop.Ide.CustomTools UpdateCompleted (monitor, file, genFile, result, false); } finally { FileService.ThawEvents (); - if (error == null) - newTask.SetResult (true); - else { - newTask.SetException (error); - } + newTask.TrySetResult (true); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs index 3d812dfa67..9a47f7e362 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs @@ -367,7 +367,7 @@ namespace MonoDevelop.Ide.Editor showRulerFromContext = false; } } - + this.FireChange (); return Task.FromResult (true); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs index 74bdc40b31..eef174104a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs @@ -850,6 +850,7 @@ namespace MonoDevelop.Ide.Gui if (!String.IsNullOrEmpty (document.FileName)) { var dp = CreateDocumentPrefs (args, document); dp.NotebookId = notebookId; + dp.IsPinned = tab.IsPinned; files.Add (dp); } } @@ -867,6 +868,16 @@ namespace MonoDevelop.Ide.Gui dp.Line = line.LineNumber + 1; dp.Column = pos.Position - line.Start + 1; } + + try { + var tab = ((SdiWorkspaceWindow)document.Window).DockNotebookTab; + if (tab != null) { + dp.IsPinned = tab.IsPinned; + } + } catch (Exception ex) { + LoggingService.LogInternalError (ex); + } + return dp; } @@ -953,6 +964,16 @@ namespace MonoDevelop.Ide.Gui var document = await documentManager.BatchOpenDocument (pm, fileName, null, doc.Line, doc.Column, nb); if (document != null) { + + try { + var tab = ((SdiWorkspaceWindow)document.Window).DockNotebookTab; + if (tab != null) { + tab.IsPinned = doc.IsPinned; + } + } catch (Exception ex) { + LoggingService.LogInternalError (ex); + } + var t = new Tuple<Document,string> (document, fileName); docViews.Add (t); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchMemento.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchMemento.cs index d70c432c54..a8a35f8d5c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchMemento.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/WorkbenchMemento.cs @@ -127,6 +127,9 @@ namespace MonoDevelop.Ide.Gui [ItemProperty (DefaultValue = 0)] public int NotebookId; + + [ItemProperty (DefaultValue = false)] + public bool IsPinned; } [DataItem ("Pad")] diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index 4b27c74d7a..6879aa96f6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -142,6 +142,14 @@ <EmbeddedResource Include="templates\EmptyStruct.xft.xml" /> <EmbeddedResource Include="templates\EmptyTextFile.xft.xml" /> <EmbeddedResource Include="templates\EmptyXMLFile.xft.xml" /> + <EmbeddedResource Include="icons\tab-pinned-9.png" /> + <EmbeddedResource Include="icons\tab-pinned-9%402x.png" /> + <EmbeddedResource Include="icons\tab-pinned-9~dark.png" /> + <EmbeddedResource Include="icons\tab-pinned-9~dark%402x.png" /> + <EmbeddedResource Include="icons\tab-unpinned-9.png" /> + <EmbeddedResource Include="icons\tab-unpinned-9%402x.png" /> + <EmbeddedResource Include="icons\tab-unpinned-9~dark.png" /> + <EmbeddedResource Include="icons\tab-unpinned-9~dark%402x.png" /> <EmbeddedResource Include="icons\reference-assembly-16.png" /> <EmbeddedResource Include="icons\reference-assembly-16%402x.png" /> <EmbeddedResource Include="icons\reference-assembly-16~dark.png" /> @@ -4252,6 +4260,7 @@ <Compile Include="MonoDevelop.Components\Mac\DragEventTrapView.cs" /> <Compile Include="MonoDevelop.Components\Mac\NativeToolkitHelper.cs" /> <Compile Include="MonoDevelop.Components\Mac\NSViewExtensions.cs" /> + <Compile Include="MonoDevelop.Components\Shared\GtkWorkarounds.cs" /> </ItemGroup> <ItemGroup Condition="'$(Configuration)' == 'DebugMac' OR '$(Configuration)' == 'ReleaseMac'"> <Compile Include="MonoDevelop.Components\Mac\KeyCodes.cs" /> @@ -4353,6 +4362,9 @@ <InternalsVisibleTo Include="Xamarin.OSXEditor" /> <InternalsVisibleTo Include="Xamarin.Sketches" /> </ItemGroup> + <ItemGroup> + <Folder Include="MonoDevelop.Components\Shared\" /> + </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Target Name="AfterBuild"> <Copy SourceFiles="@(Data)" DestinationFolder="..\..\..\build\data\%(Data.RelativeDir)" SkipUnchangedFiles="true" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs index da8982d467..1776fbeb3b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs @@ -541,9 +541,9 @@ namespace MonoDevelop.Ide // If the user interacted with the IDE just a moment ago, wait a bit more time before // running the action - var interactionSpan = (int)(DateTime.Now - commandService.LastUserInteraction).TotalMilliseconds; + var interactionSpan = Math.Max (0, (DateTime.Now - commandService.LastUserInteraction).TotalMilliseconds); if (interactionSpan < 500) { - DispatchIdleActions (500 - interactionSpan); + DispatchIdleActions (500 - (int) interactionSpan); return; } diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9.png Binary files differnew file mode 100644 index 0000000000..edc1f09dd6 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9.png diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9@2x.png Binary files differnew file mode 100644 index 0000000000..86acbbbc16 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9@2x.png diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark.png Binary files differnew file mode 100644 index 0000000000..9fa86cb595 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark.png diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark@2x.png Binary files differnew file mode 100644 index 0000000000..ee2e2262ff --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-pinned-9~dark@2x.png diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9.png Binary files differnew file mode 100644 index 0000000000..24f9f94650 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9.png diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9@2x.png Binary files differnew file mode 100644 index 0000000000..cab628cf34 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9@2x.png diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark.png Binary files differnew file mode 100644 index 0000000000..e240b6cc83 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark.png diff --git a/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark@2x.png b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark@2x.png Binary files differnew file mode 100644 index 0000000000..23838c89b2 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/icons/tab-unpinned-9~dark@2x.png diff --git a/main/src/tools/mdtool/mdtool.csproj b/main/src/tools/mdtool/mdtool.csproj index 0910f01da2..69d8d48902 100644 --- a/main/src/tools/mdtool/mdtool.csproj +++ b/main/src/tools/mdtool/mdtool.csproj @@ -36,6 +36,9 @@ <None Include="..\..\core\MonoDevelop.Startup\app.config"> <Link>app.config</Link> </None> + <Compile Include="..\..\core\MonoDevelop.Ide\MonoDevelop.Components\Shared\GtkWorkarounds.cs"> + <Link>src\GtkWorkarounds.cs</Link> + </Compile> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project> diff --git a/main/src/tools/mdtool/src/mdtool.cs b/main/src/tools/mdtool/src/mdtool.cs index 1ecaa580b7..778a3f5d7f 100644 --- a/main/src/tools/mdtool/src/mdtool.cs +++ b/main/src/tools/mdtool/src/mdtool.cs @@ -45,6 +45,7 @@ class MonoDevelopProcessHost [STAThread] public static int Main (string[] args) { + int exitCode = -1; try { var sc = new ConsoleSynchronizationContext (); SynchronizationContext.SetSynchronizationContext (sc); @@ -115,7 +116,8 @@ class MonoDevelopProcessHost //setup app needs to skip runtime initialization or we get addin engine races if (toolName == "setup") { - return RunSetup (toolArgs); + exitCode = RunSetup (toolArgs); + return exitCode; } // Only log fatal errors unless verbosity is specified. Command line tools should already @@ -127,40 +129,57 @@ class MonoDevelopProcessHost if (showHelp || badInput) { ShowHelp (badInput, exeName); - return badInput? 1 : 0; + exitCode = badInput? 1 : 0; + return exitCode; } - var tool = Runtime.ApplicationService.GetApplication (toolName); - if (tool == null) { - Console.Error.WriteLine ("Tool '{0}' not found.", toolName); - listTools = true; - badInput = true; - } - - if (listTools) { - ShowAvailableTools (); - return badInput? 1 : 0; - } + if (!showHelp && !badInput) { + var tool = Runtime.ApplicationService.GetApplication (toolName); + if (tool == null) { + Console.Error.WriteLine ("Tool '{0}' not found.", toolName); + listTools = true; + badInput = true; + } - var task = tool.Run (toolArgs); - task.ContinueWith ((t) => sc.ExitLoop ()); - sc.RunMainLoop (); - return task.Result; + if (listTools) { + ShowAvailableTools (); + exitCode = badInput ? 1 : 0; + return exitCode; + } + if (tool != null) { + var task = tool.Run (toolArgs); + task.ContinueWith ((t) => sc.ExitLoop ()); + sc.RunMainLoop (); + exitCode = task.Result; + } + } } catch (UserException ex) { Console.WriteLine (ex.Message); - return -1; + exitCode = -1; } catch (Exception ex) { LoggingService.LogFatalError (ex.ToString ()); - return -1; + exitCode = -1; } finally { try { - Runtime.Shutdown (); + var terminate = exitCode == 0; + Shutdown (terminate); } catch { // Ignore shutdown exceptions } LoggingService.Shutdown (); } + + return exitCode; + } + + static void Shutdown (bool terminate) + { + Runtime.Shutdown (); + + if (terminate) { + MonoDevelop.Components.GtkWorkarounds.Terminate (); + } } static void ShowHelp (bool shortHelp, string exeName) diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/FilePathTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/FilePathTests.cs index 6d3bad23c5..f43f41eaba 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/FilePathTests.cs +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/FilePathTests.cs @@ -24,6 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; +using System.Collections.Generic; using System.IO; using System.Linq; using NUnit.Framework; @@ -111,21 +112,58 @@ namespace MonoDevelop.Core Assert.IsFalse (child.IsChildPathOf (child)); } - [Test] - public void FileExtensionIsProper () + [TestCase("test.txt", "test")] + [TestCase(".gitignore", "")] + public void FileNameWithoutExtension (string fileName, string expected) + { + var path = FilePath.Build ("dir", fileName); + + Assert.AreEqual (expected, path.FileNameWithoutExtension); + } + + + [TestCase ("test.txt", ".txt", true)] + [TestCase ("test.txt", ".TxT", null)] + [TestCase ("test.txt", ".cs", false)] + [TestCase ("test.txt", ".longer", false)] + [TestCase ("test.txt", "", false)] + [TestCase (".gitignore", ".gitignore", true)] + [TestCase (".gitignore", ".git", false)] + [TestCase (".gitignore", "", false)] + [TestCase ("a", "", true)] + [TestCase ("a.", "", true)] + [TestCase ("", "", true)] + [TestCase ("", "a", false)] + public void HasExtensionChecks (string fileName, string assertExtension, bool? expected) { - var path = new FilePath ("asdf.txt"); - Assert.AreEqual ("asdf.txt", path.FileName); - Assert.IsTrue (path.HasExtension (".txt")); - Assert.AreEqual (FilePath.PathComparison == StringComparison.OrdinalIgnoreCase, path.HasExtension (".TXT")); - Assert.AreEqual (".txt", path.Extension); - Assert.AreEqual ("asdf", path.FileNameWithoutExtension); - - path = new FilePath (".gitignore"); - Assert.False (path.HasExtension (".gitignore")); - Assert.AreEqual (".gitignore", path.FileName); - Assert.AreEqual (".gitignore", path.Extension); - Assert.AreEqual ("", path.FileNameWithoutExtension); + IEqualityComparer<string> comparer = FilePath.PathComparer; + var expectedValue = expected ?? FilePath.PathComparison == StringComparison.OrdinalIgnoreCase; + + var path = FilePath.Build ("dir", fileName); + + Assert.AreEqual (expectedValue, path.HasExtension (assertExtension)); + + var expectedConstraint = expectedValue ? Is.EqualTo (assertExtension) : Is.Not.EqualTo (assertExtension); + Assert.That (path.Extension, expectedConstraint.Using (comparer)); + } + + [TestCase ("test.txt", "test.txt", true)] + [TestCase ("test.txt", "Test.txT", null)] + [TestCase ("test.txt", "abc.txt", false)] + [TestCase ("test.txt", "something", false)] + [TestCase (".gitignore", ".gitignore", true)] + [TestCase (".gitignore", ".git", false)] + public void HasFileNameChecks (string fileName, string assertFileName, bool? expected) + { + IEqualityComparer<string> comparer = FilePath.PathComparer; + var expectedValue = expected ?? FilePath.PathComparison == StringComparison.OrdinalIgnoreCase; + + var path = FilePath.Build ("dir", fileName); + + Assert.AreEqual (expectedValue, path.HasFileName (assertFileName)); + + var expectedConstraint = expectedValue ? Is.EqualTo (assertFileName) : Is.Not.EqualTo (assertFileName); + Assert.That (path.FileName, expectedConstraint.Using (comparer)); } [Test] diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/MSBuildProjectTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/MSBuildProjectTests.cs index 7b3738aa59..fbfe4e9454 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/MSBuildProjectTests.cs +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/MSBuildProjectTests.cs @@ -494,6 +494,20 @@ namespace MonoDevelop.Projects var specialFolder = Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData); Assert.AreEqual (specialFolder, p.EvaluatedProperties.GetValue ("EnumFolderPath")); + var path = p.EvaluatedProperties.GetValue ("Path"); + + Assert.AreEqual ( + path.Split (Path.DirectorySeparatorChar).Length, + p.EvaluatedProperties.GetValue<int> ("EnumFlagsSplitStringLength") + ); + Assert.AreEqual ( + path.Split (new [] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries).Length, + p.EvaluatedProperties.GetValue<int> ("EnumFlagsSplitStringRemoveEmptyLength") + ); + + Assert.AreEqual ("1", p.EvaluatedProperties.GetValue ("CorrectOverloadIndexOf")); + Assert.AreEqual ("a.cs;b.cs", p.EvaluatedProperties.GetValue ("CorrectOverloadIndexOfTransform")); + var basePath = Path.GetDirectoryName (p.FileName); var targets = Path.Combine (basePath, "false.targets"); diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectWithWildcardsTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectWithWildcardsTests.cs index 97cf87adf3..8bf1f13c7d 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectWithWildcardsTests.cs +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Projects/ProjectWithWildcardsTests.cs @@ -1,4 +1,4 @@ -// +// // ProjectWithWildcardsTests.cs // // Author: @@ -63,6 +63,31 @@ namespace MonoDevelop.Projects } [Test] + public async Task LoadProjectWithQuestionWildcards () + { + string projFile = Util.GetSampleProject ("console-project-with-wildcards", "ConsoleProject-with-question-wildcard.csproj"); + + var p = await Services.ProjectService.ReadSolutionItem (Util.GetMonitor (), projFile); + Assert.IsInstanceOf<Project> (p); + var mp = (Project)p; + var files = mp.Files.Select (f => f.FilePath.FileName).OrderBy (f => f).ToArray (); + Assert.That (files, Is.EquivalentTo (new string [] { + "Data1.cs", + "Data2.cs", + "Data3.cs", + "maybe.js", + "maybe1.js", + "Program.cs", + "text1-1.txt", + "text1-2.txt", + "text2-1.txt", + "text2-2.txt", + })); + + p.Dispose (); + } + + [Test] public async Task LoadProjectWithWildcardsAndExcludes () { string projFile = Util.GetSampleProject ("console-project-with-wildcards", "ConsoleProject-with-excludes.csproj"); @@ -338,7 +363,7 @@ namespace MonoDevelop.Projects } /// <summary> - /// If an MSBuild item has a property on loading then if all the properties are removed the + /// If an MSBuild item has a property on loading then if all the properties are removed the /// project file when saved will still have an end element. So this test uses a different /// .saved5 file compared with the previous test and includes the extra end tag for the /// EmbeddedResource. diff --git a/main/tests/test-projects/console-project-with-wildcards/ConsoleProject-with-question-wildcard.csproj b/main/tests/test-projects/console-project-with-wildcards/ConsoleProject-with-question-wildcard.csproj new file mode 100755 index 0000000000..c94386b696 --- /dev/null +++ b/main/tests/test-projects/console-project-with-wildcards/ConsoleProject-with-question-wildcard.csproj @@ -0,0 +1,51 @@ +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.50727</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{4A9E3523-48F0-4BDF-A0F4-49DAD4431FAB}</ProjectGuid> + <OutputType>Exe</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>ConsoleProject</RootNamespace> + <AssemblyName>ConsoleProject</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>True</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>False</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>True</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Program.cs" /> + <Compile Include="Content\**\*.cs" /> + <Content Include="Content\*.txt" /> + <Content Include="Content\Data\*.txt" /> + <Content Include="maybe?.js" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/main/tests/test-projects/console-project-with-wildcards/maybe.js b/main/tests/test-projects/console-project-with-wildcards/maybe.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/main/tests/test-projects/console-project-with-wildcards/maybe.js diff --git a/main/tests/test-projects/console-project-with-wildcards/maybe1.js b/main/tests/test-projects/console-project-with-wildcards/maybe1.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/main/tests/test-projects/console-project-with-wildcards/maybe1.js diff --git a/main/tests/test-projects/msbuild-tests/functions.csproj b/main/tests/test-projects/msbuild-tests/functions.csproj index 5a96bebf3f..217f855153 100755 --- a/main/tests/test-projects/msbuild-tests/functions.csproj +++ b/main/tests/test-projects/msbuild-tests/functions.csproj @@ -1,10 +1,20 @@ <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> - <ItemGroup> + <ItemGroup> <File Include="$(P1).$(Prefix)Test1" /> <Result Include="$(Files)" /> - </ItemGroup> - <PropertyGroup> + <DesignerCS Include="a.designer.cs;b.designer.cs"/> + <DesignerCSDependentFiles Include="@(DesignerCS)"> + <DependentUpon>$([System.String]::Copy('%(Identity)').Substring( + 0, + $([System.String]::Copy('%(Identity)').LastIndexOf( + '.designer.cs', + System.StringComparison.OrdinalIgnoreCase)) + )).cs + </DependentUpon> + </DesignerCSDependentFiles> + </ItemGroup> + <PropertyGroup> <Prop>abcdefgh</Prop> <SomeChars>1.0</SomeChars> <TestFile>files\test.txt</TestFile> @@ -37,7 +47,7 @@ <CharTrim>$(Path.Trim (\b))</CharTrim> <OutDir>foo</OutDir> <FullPath>$([System.IO.Path]::GetFullPath(`$([System.IO.Path]::Combine(`$(MSBuildProjectDirectory)`, `$(OutDir)`))`))</FullPath> - <FullPathWithSpaces>$([System.IO.Path]::GetFullPath(`$([System.IO.Path]::Combine(`$(MSBuildProjectDirectory)`, `(a b)`))`))</FullPathWithSpaces> + <FullPathWithSpaces>$([System.IO.Path]::GetFullPath(`$([System.IO.Path]::Combine(`$(MSBuildProjectDirectory)`, `(a b)`))`))</FullPathWithSpaces> <P1>AAA</P1> <Files>$(P1);@(File)</Files> <Prefix>Post</Prefix> @@ -47,14 +57,18 @@ <DoubleNumberComplex>$([MSBuild]::Subtract($(DoubleNumber.Split('.')[0].Substring(3).Trim()), 8800))</DoubleNumberComplex> <ParamsPathCombine>$([System.IO.Path]::Combine('a', 'b', 'c', 'd', 'e', 'f'))</ParamsPathCombine> <EnumFolderPath>$([System.Environment]::GetFolderPath(SpecialFolder.LocalApplicationData))</EnumFolderPath> + <EnumFlagsSplitStringLength>$(Path.Split('\', StringSplitOptions.None).Length)</EnumFlagsSplitStringLength> + <EnumFlagsSplitStringRemoveEmptyLength>$(Path.Split('\', System.StringSplitOptions.None|RemoveEmptyEntries).Length)</EnumFlagsSplitStringRemoveEmptyLength> <DirectoryNameOfFileAbove>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), false.targets))false.targets</DirectoryNameOfFileAbove> <PathOfFileAbove>$([MSBuild]::GetPathOfFileAbove('false.targets'))</PathOfFileAbove> <EnsureTrailingSlash>$([MSBuild]::EnsureTrailingSlash('$(CharTrim)'))</EnsureTrailingSlash> <NormalizeDirectory>$([MSBuild]::NormalizeDirectory('$(CharTrim)'))</NormalizeDirectory> <NormalizePath>$([MSBuild]::NormalizePath('$(CharTrim)'))</NormalizePath> - </PropertyGroup> - <PropertyGroup> - </PropertyGroup> + <CorrectOverloadIndexOf>$([System.String]::Copy('a.designer.cs').LastIndexOf('.designer.cs', System.StringComparison.OrdinalIgnoreCase))</CorrectOverloadIndexOf> + <CorrectOverloadIndexOfTransform>@(DesignerCSDependentFiles->'%(DependentUpon)')</CorrectOverloadIndexOfTransform> + </PropertyGroup> + <PropertyGroup> + </PropertyGroup> <Target Name="Test"> <Message Text="$(FullPath)" /> </Target> diff --git a/version-checks b/version-checks index b6284130b8..052d27b050 100644 --- a/version-checks +++ b/version-checks @@ -17,7 +17,7 @@ DEP[0]=md-addins DEP_NAME[0]=MDADDINS DEP_PATH[0]=${top_srcdir}/../md-addins DEP_MODULE[0]=git@github.com:xamarin/md-addins.git -DEP_NEEDED_VERSION[0]=f69d5f968f1b0d549818e286daafcf6416fbba9c +DEP_NEEDED_VERSION[0]=cb7f1ec820c2396dea8cd21079f6ab3d4a5eb09f DEP_BRANCH_AND_REMOTE[0]="master origin/master" # heap-shot |