diff --git a/CKAN/CKAN/ModuleInstaller.cs b/CKAN/CKAN/ModuleInstaller.cs index 665abba7da..ac7f238c6d 100644 --- a/CKAN/CKAN/ModuleInstaller.cs +++ b/CKAN/CKAN/ModuleInstaller.cs @@ -24,6 +24,8 @@ public class ModuleInstaller public ModuleInstallerReportModInstalled onReportModInstalled = null; public ModuleInstallerReportProgress onReportProgress = null; + private bool m_LastDownloadSuccessful = false; + /// /// Download the given mod. Returns the filename it was saved to. /// If no filename is provided, the standard_name() will be used. @@ -105,6 +107,18 @@ public string CachedOrDownload(string identifier, Version version, Uri url, stri return Download(url, filename); } + public static bool IsCached(CkanModule module) + { + var filename = CkanModule.StandardName(module.identifier, module.version); + var path = CachePath(filename); + if (File.Exists(path)) + { + return true; + } + + return false; + } + public bool IsCached(string filename, out string fullPath) { fullPath = CachePath(filename); @@ -199,6 +213,16 @@ public void InstallList(List modules, RelationshipResolverOptions option { Monitor.Wait(downloader); } + + if (m_LastDownloadSuccessful) + { + for (int i = 0; i < modulesToDownload.Length; i++) + { + Install(modulesToDownload[i], modulesToDownloadPaths[i]); + } + } + + currentTransaction.Commit(); } private void OnDownloadsComplete(Uri[] urls, string[] filenames, CkanModule[] modules, Exception[] errors) @@ -214,15 +238,7 @@ private void OnDownloadsComplete(Uri[] urls, string[] filenames, CkanModule[] mo } } - if (noErrors) - { - for (int i = 0; i < urls.Length; i++) - { - Install(modules[i], filenames[i]); - } - - currentTransaction.Commit(); - } + m_LastDownloadSuccessful = noErrors; lock (downloader) { diff --git a/CKAN/CKAN/Net.cs b/CKAN/CKAN/Net.cs index d706effe79..9c8a77f1a5 100644 --- a/CKAN/CKAN/Net.cs +++ b/CKAN/CKAN/Net.cs @@ -16,6 +16,7 @@ namespace CKAN public struct NetAsyncDownloaderDownloadPart { public WebClient agent; + public long bytesDownloaded; public long bytesLeft; public int bytesPerSecond; public Exception error; @@ -97,7 +98,7 @@ public string[] StartDownload() return filePaths; } - private void FileProgressReport(int index, int percent, long bytesDownloaded, long _bytesLeft) + private void FileProgressReport(int index, int percent, long bytesDownloaded, long bytesLeft) { NetAsyncDownloaderDownloadPart download = downloads[index]; @@ -113,7 +114,8 @@ private void FileProgressReport(int index, int percent, long bytesDownloaded, lo download.bytesPerSecond = (int) bytesChange/timeSpan.Seconds; } - download.bytesLeft = _bytesLeft; + download.bytesLeft = bytesLeft; + download.bytesDownloaded = bytesDownloaded; downloads[index] = download; if (onProgressReport != null) @@ -121,15 +123,17 @@ private void FileProgressReport(int index, int percent, long bytesDownloaded, lo int totalPercentage = 0; int totalBytesPerSecond = 0; long totalBytesLeft = 0; + long totalBytesDownloaded = 0; for (int i = 0; i < downloads.Length; i++) { totalBytesPerSecond += downloads[i].bytesPerSecond; totalBytesLeft += downloads[i].bytesLeft; - totalPercentage += downloads[i].percentComplete; + totalBytesDownloaded += downloads[i].bytesDownloaded; + totalBytesLeft += downloads[i].bytesLeft; } - totalPercentage /= downloads.Length; + totalPercentage = (int)((totalBytesDownloaded * 100) / (totalBytesLeft + totalBytesDownloaded + 1)); onProgressReport(totalPercentage, totalBytesPerSecond, totalBytesLeft); } diff --git a/CKAN/CKAN/Registry.cs b/CKAN/CKAN/Registry.cs index 9a507d7baa..f1afb4dec0 100644 --- a/CKAN/CKAN/Registry.cs +++ b/CKAN/CKAN/Registry.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using log4net; @@ -113,7 +114,7 @@ public List Available(KSPVersion ksp_version = null) { try { - if (LatestAvailable(dependency.name.Value, ksp_version) == null) + if (LatestAvailableWithProvides(dependency.name.Value, ksp_version).Count == 0) { failedDepedency = true; break; @@ -191,6 +192,54 @@ public CkanModule LatestAvailable(string module, KSPVersion ksp_version = null) } } + /// + /// Returns the latest available version of a module that + /// satisifes the specified version. Takes into account module 'provides' + /// Throws a ModuleNotFoundException if asked for a non-existant module. + /// Returns null if there's simply no compatible version for this system. + /// + public List LatestAvailableWithProvides(string module, KSPVersion ksp_version = null) + { + log.DebugFormat("Finding latest available for {0}", module); + + // TODO: Check user's stability tolerance (stable, unstable, testing, etc) + + List modules = new List(); + + try + { + var mod = LatestAvailable(module, ksp_version); + if (mod != null) + { + modules.Add(mod); + } + } + catch (ModuleNotFoundException) + { + foreach (var pair in available_modules) + { + if (pair.Value.Latest(ksp_version) == null) + { + continue; + } + + var provides = pair.Value.Latest(ksp_version).provides; + if (provides != null) + { + foreach (var provided in provides) + { + if (provided == module) + { + modules.Add(pair.Value.Latest(ksp_version)); + } + } + } + } + } + + return modules; + } + /// /// Register the supplied module as having been installed, thereby keeping /// track of its metadata and files. diff --git a/CKAN/GUI/ApplyChangesDialog.cs b/CKAN/GUI/ApplyChangesDialog.cs index c36a71c3c7..310feb2c97 100644 --- a/CKAN/GUI/ApplyChangesDialog.cs +++ b/CKAN/GUI/ApplyChangesDialog.cs @@ -17,10 +17,16 @@ public ApplyChangesDialog() public void ShowApplyChangesDialog(List> changeset, BackgroundWorker installWorker) + { + Util.Invoke(ChangesListView, () => _ShowApplyChangesDialog(changeset, installWorker)); + Util.Invoke(this, () => ShowDialog()); + } + + private void _ShowApplyChangesDialog(List> changeset, + BackgroundWorker installWorker) { m_Changeset = changeset; m_InstallWorker = installWorker; - ChangesListView.Items.Clear(); foreach (var change in changeset) @@ -39,8 +45,6 @@ public void ShowApplyChangesDialog(List>, RelationshipResolverOptions>( m_Changeset, install_ops)); diff --git a/CKAN/GUI/CKAN-GUI.csproj b/CKAN/GUI/CKAN-GUI.csproj index 6dbe646ff4..fa132ed757 100644 --- a/CKAN/GUI/CKAN-GUI.csproj +++ b/CKAN/GUI/CKAN-GUI.csproj @@ -55,12 +55,14 @@ ApplyChangesDialog.cs + Form ErrorDialog.cs + Form diff --git a/CKAN/GUI/Configuration.cs b/CKAN/GUI/Configuration.cs index e1804937db..037fdadce0 100644 --- a/CKAN/GUI/Configuration.cs +++ b/CKAN/GUI/Configuration.cs @@ -38,7 +38,9 @@ public static Configuration LoadConfiguration(string path) public static void SaveConfiguration(Configuration configuration, string path) { var serializer = new XmlSerializer(typeof (Configuration)); - serializer.Serialize(new StreamWriter(path), configuration); + var writer = new StreamWriter(path); + serializer.Serialize(writer, configuration); + writer.Close(); } } } \ No newline at end of file diff --git a/CKAN/GUI/ControlFactory.cs b/CKAN/GUI/ControlFactory.cs new file mode 100644 index 0000000000..7c34b4b150 --- /dev/null +++ b/CKAN/GUI/ControlFactory.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; + +namespace CKAN +{ + + // this class ensures that all controls are created from the same thread + // this is a mono limitation described here - http://www.mono-project.com/docs/faq/winforms/ + + + public class ControlFactory + { + + private int m_MainThreadID = 0; + + public ControlFactory() + { + m_MainThreadID = Thread.CurrentThread.ManagedThreadId; + } + + public T CreateControl() where T : new() + { + if (Thread.CurrentThread.ManagedThreadId != m_MainThreadID) + { + Debugger.Break(); + } + + return new T(); + } + + + } + +} diff --git a/CKAN/GUI/ErrorDialog.cs b/CKAN/GUI/ErrorDialog.cs index 6b76fc8b64..a0c1cb4126 100644 --- a/CKAN/GUI/ErrorDialog.cs +++ b/CKAN/GUI/ErrorDialog.cs @@ -8,26 +8,18 @@ public partial class ErrorDialog : Form public ErrorDialog() { InitializeComponent(); + StartPosition = FormStartPosition.CenterScreen; } public void ShowErrorDialog(string message) { - if (ErrorMessage.InvokeRequired) - { - ErrorMessage.Invoke(new MethodInvoker(delegate { ErrorMessage.Text = message; })); - } - else - { - ErrorMessage.Text = message; - } - - StartPosition = FormStartPosition.CenterScreen; - ShowDialog(); + Util.Invoke(ErrorMessage, () => ErrorMessage.Text = message); + Util.Invoke(this, () => ShowDialog()); } public void HideErrorDialog() { - Close(); + Util.Invoke(this, () => Close()); } private void DismissButton_Click(object sender, EventArgs e) diff --git a/CKAN/GUI/Main.Designer.cs b/CKAN/GUI/Main.Designer.cs index 5a466f2dae..2a4926cefc 100644 --- a/CKAN/GUI/Main.Designer.cs +++ b/CKAN/GUI/Main.Designer.cs @@ -42,15 +42,14 @@ private void InitializeComponent() this.RefreshToolButton = new System.Windows.Forms.ToolStripMenuItem(); this.UpdateAllToolButton = new System.Windows.Forms.ToolStripMenuItem(); this.ApplyToolButton = new System.Windows.Forms.ToolStripMenuItem(); + this.FilterToolButton = new System.Windows.Forms.ToolStripMenuItem(); + this.FilterAllButton = new System.Windows.Forms.ToolStripMenuItem(); + this.FilterInstalledButton = new System.Windows.Forms.ToolStripMenuItem(); + this.FilterInstalledUpdateButton = new System.Windows.Forms.ToolStripMenuItem(); + this.FilterNewButton = new System.Windows.Forms.ToolStripMenuItem(); + this.FilterNotInstalledButton = new System.Windows.Forms.ToolStripMenuItem(); + this.FilterIncompatibleButton = new System.Windows.Forms.ToolStripMenuItem(); this.ModList = new System.Windows.Forms.DataGridView(); - this.ModFilter = new System.Windows.Forms.ListBox(); - this.FilterByNameTextBox = new System.Windows.Forms.TextBox(); - this.FilterByNameLabel = new System.Windows.Forms.Label(); - this.FilterByNamePanel = new System.Windows.Forms.Panel(); - this.splitContainer1 = new System.Windows.Forms.SplitContainer(); - this.ModInfo = new System.Windows.Forms.TextBox(); - this.StatusPanel = new System.Windows.Forms.Panel(); - this.StatusLabel = new System.Windows.Forms.Label(); this.Installed = new System.Windows.Forms.DataGridViewCheckBoxColumn(); this.Update = new System.Windows.Forms.DataGridViewCheckBoxColumn(); this.ModName = new System.Windows.Forms.DataGridViewTextBoxColumn(); @@ -60,6 +59,21 @@ private void InitializeComponent() this.KSPVersion = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Description = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Homepage = new System.Windows.Forms.DataGridViewLinkColumn(); + this.FilterByNameTextBox = new System.Windows.Forms.TextBox(); + this.FilterByNameLabel = new System.Windows.Forms.Label(); + this.FilterByNamePanel = new System.Windows.Forms.Panel(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.ModInfo = new System.Windows.Forms.TextBox(); + this.DependsTabPage = new System.Windows.Forms.TabPage(); + this.DependsGraphTree = new System.Windows.Forms.TreeView(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.StatusPanel = new System.Windows.Forms.Panel(); + this.StatusLabel = new System.Windows.Forms.Label(); + this.NotCachedLabel = new System.Windows.Forms.Label(); + this.ContentsDownloadButton = new System.Windows.Forms.Button(); + this.ContentsPreviewTree = new System.Windows.Forms.TreeView(); this.menuStrip1.SuspendLayout(); this.menuStrip2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.ModList)).BeginInit(); @@ -68,6 +82,10 @@ private void InitializeComponent() this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel2.SuspendLayout(); this.splitContainer1.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.DependsTabPage.SuspendLayout(); + this.tabPage2.SuspendLayout(); this.StatusPanel.SuspendLayout(); this.SuspendLayout(); // @@ -133,10 +151,11 @@ private void InitializeComponent() this.menuStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.RefreshToolButton, this.UpdateAllToolButton, - this.ApplyToolButton}); + this.ApplyToolButton, + this.FilterToolButton}); this.menuStrip2.Location = new System.Drawing.Point(0, 24); this.menuStrip2.Name = "menuStrip2"; - this.menuStrip2.Size = new System.Drawing.Size(348, 40); + this.menuStrip2.Size = new System.Drawing.Size(445, 40); this.menuStrip2.TabIndex = 2; this.menuStrip2.Text = "menuStrip2"; // @@ -167,6 +186,63 @@ private void InitializeComponent() this.ApplyToolButton.Text = "Apply changes"; this.ApplyToolButton.Click += new System.EventHandler(this.ApplyToolButton_Click); // + // FilterToolButton + // + this.FilterToolButton.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.FilterAllButton, + this.FilterInstalledButton, + this.FilterInstalledUpdateButton, + this.FilterNewButton, + this.FilterNotInstalledButton, + this.FilterIncompatibleButton}); + this.FilterToolButton.Image = global::CKAN.Properties.Resources.search; + this.FilterToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.FilterToolButton.Name = "FilterToolButton"; + this.FilterToolButton.Size = new System.Drawing.Size(97, 36); + this.FilterToolButton.Text = "Filter (All)"; + // + // FilterAllButton + // + this.FilterAllButton.Name = "FilterAllButton"; + this.FilterAllButton.Size = new System.Drawing.Size(205, 22); + this.FilterAllButton.Text = "All"; + this.FilterAllButton.Click += new System.EventHandler(this.FilterAllButton_Click); + // + // FilterInstalledButton + // + this.FilterInstalledButton.Name = "FilterInstalledButton"; + this.FilterInstalledButton.Size = new System.Drawing.Size(205, 22); + this.FilterInstalledButton.Text = "Installed"; + this.FilterInstalledButton.Click += new System.EventHandler(this.FilterInstalledButton_Click); + // + // FilterInstalledUpdateButton + // + this.FilterInstalledUpdateButton.Name = "FilterInstalledUpdateButton"; + this.FilterInstalledUpdateButton.Size = new System.Drawing.Size(205, 22); + this.FilterInstalledUpdateButton.Text = "Installed (update available)"; + this.FilterInstalledUpdateButton.Click += new System.EventHandler(this.FilterInstalledUpdateButton_Click); + // + // FilterNewButton + // + this.FilterNewButton.Name = "FilterNewButton"; + this.FilterNewButton.Size = new System.Drawing.Size(205, 22); + this.FilterNewButton.Text = "New in repository"; + this.FilterNewButton.Click += new System.EventHandler(this.FilterNewButton_Click); + // + // FilterNotInstalledButton + // + this.FilterNotInstalledButton.Name = "FilterNotInstalledButton"; + this.FilterNotInstalledButton.Size = new System.Drawing.Size(205, 22); + this.FilterNotInstalledButton.Text = "Not installed"; + this.FilterNotInstalledButton.Click += new System.EventHandler(this.FilterNotInstalledButton_Click); + // + // FilterIncompatibleButton + // + this.FilterIncompatibleButton.Name = "FilterIncompatibleButton"; + this.FilterIncompatibleButton.Size = new System.Drawing.Size(205, 22); + this.FilterIncompatibleButton.Text = "Incompatible"; + this.FilterIncompatibleButton.Click += new System.EventHandler(this.FilterIncompatibleButton_Click); + // // ModList // this.ModList.AllowUserToAddRows = false; @@ -194,30 +270,72 @@ private void InitializeComponent() this.ModList.Name = "ModList"; this.ModList.RowHeadersVisible = false; this.ModList.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.ModList.Size = new System.Drawing.Size(663, 355); + this.ModList.Size = new System.Drawing.Size(626, 544); this.ModList.TabIndex = 3; this.ModList.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.ModList_CellContentClick); this.ModList.SelectionChanged += new System.EventHandler(this.ModList_SelectedIndexChanged); // - // ModFilter - // - this.ModFilter.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left))); - this.ModFilter.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.ModFilter.FormattingEnabled = true; - this.ModFilter.ItemHeight = 20; - this.ModFilter.Items.AddRange(new object[] { - "All", - "Installed", - "Installed (update available)", - "New in repository", - "Not installed", - "Incompatible"}); - this.ModFilter.Location = new System.Drawing.Point(0, 67); - this.ModFilter.Name = "ModFilter"; - this.ModFilter.Size = new System.Drawing.Size(197, 544); - this.ModFilter.TabIndex = 0; - this.ModFilter.SelectedIndexChanged += new System.EventHandler(this.ModFilter_SelectedIndexChanged); + // Installed + // + this.Installed.HeaderText = "Installed"; + this.Installed.Name = "Installed"; + this.Installed.Width = 50; + // + // Update + // + this.Update.HeaderText = "Update"; + this.Update.Name = "Update"; + this.Update.Width = 46; + // + // ModName + // + this.ModName.HeaderText = "Name"; + this.ModName.Name = "ModName"; + this.ModName.ReadOnly = true; + this.ModName.Width = 58; + // + // Author + // + this.Author.HeaderText = "Author"; + this.Author.Name = "Author"; + this.Author.ReadOnly = true; + this.Author.Width = 61; + // + // InstalledVersion + // + this.InstalledVersion.HeaderText = "Installed version"; + this.InstalledVersion.Name = "InstalledVersion"; + this.InstalledVersion.ReadOnly = true; + this.InstalledVersion.Width = 97; + // + // LatestVersion + // + this.LatestVersion.HeaderText = "Latest version"; + this.LatestVersion.Name = "LatestVersion"; + this.LatestVersion.ReadOnly = true; + this.LatestVersion.Width = 88; + // + // KSPVersion + // + this.KSPVersion.HeaderText = "KSP version"; + this.KSPVersion.Name = "KSPVersion"; + this.KSPVersion.Width = 81; + // + // Description + // + this.Description.HeaderText = "Description"; + this.Description.Name = "Description"; + this.Description.ReadOnly = true; + this.Description.Width = 83; + // + // Homepage + // + this.Homepage.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; + this.Homepage.HeaderText = "Homepage"; + this.Homepage.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; + this.Homepage.Name = "Homepage"; + this.Homepage.ReadOnly = true; + this.Homepage.Width = 63; // // FilterByNameTextBox // @@ -232,7 +350,7 @@ private void InitializeComponent() // this.FilterByNameLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.FilterByNameLabel.AutoSize = true; - this.FilterByNameLabel.Location = new System.Drawing.Point(8, 21); + this.FilterByNameLabel.Location = new System.Drawing.Point(3, 21); this.FilterByNameLabel.Name = "FilterByNameLabel"; this.FilterByNameLabel.Size = new System.Drawing.Size(75, 13); this.FilterByNameLabel.TabIndex = 5; @@ -243,7 +361,7 @@ private void InitializeComponent() this.FilterByNamePanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.FilterByNamePanel.Controls.Add(this.FilterByNameLabel); this.FilterByNamePanel.Controls.Add(this.FilterByNameTextBox); - this.FilterByNamePanel.Location = new System.Drawing.Point(653, 27); + this.FilterByNamePanel.Location = new System.Drawing.Point(649, 24); this.FilterByNamePanel.Name = "FilterByNamePanel"; this.FilterByNamePanel.Size = new System.Drawing.Size(217, 39); this.FilterByNamePanel.TabIndex = 6; @@ -253,9 +371,8 @@ private void InitializeComponent() this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.splitContainer1.Location = new System.Drawing.Point(203, 67); + this.splitContainer1.Location = new System.Drawing.Point(3, 67); this.splitContainer1.Name = "splitContainer1"; - this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; // // splitContainer1.Panel1 // @@ -263,22 +380,78 @@ private void InitializeComponent() // // splitContainer1.Panel2 // - this.splitContainer1.Panel2.Controls.Add(this.ModInfo); - this.splitContainer1.Size = new System.Drawing.Size(663, 544); - this.splitContainer1.SplitterDistance = 355; + this.splitContainer1.Panel2.Controls.Add(this.tabControl1); + this.splitContainer1.Size = new System.Drawing.Size(863, 544); + this.splitContainer1.SplitterDistance = 626; this.splitContainer1.TabIndex = 7; // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.DependsTabPage); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.Location = new System.Drawing.Point(0, 0); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(233, 544); + this.tabControl1.TabIndex = 0; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.ModInfo); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(225, 518); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Metadata"; + this.tabPage1.UseVisualStyleBackColor = true; + // // ModInfo // this.ModInfo.Dock = System.Windows.Forms.DockStyle.Fill; - this.ModInfo.Location = new System.Drawing.Point(0, 0); + this.ModInfo.Location = new System.Drawing.Point(3, 3); this.ModInfo.Multiline = true; this.ModInfo.Name = "ModInfo"; this.ModInfo.ReadOnly = true; this.ModInfo.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.ModInfo.Size = new System.Drawing.Size(663, 185); + this.ModInfo.Size = new System.Drawing.Size(219, 512); this.ModInfo.TabIndex = 2; // + // DependsTabPage + // + this.DependsTabPage.Controls.Add(this.DependsGraphTree); + this.DependsTabPage.Location = new System.Drawing.Point(4, 22); + this.DependsTabPage.Name = "DependsTabPage"; + this.DependsTabPage.Padding = new System.Windows.Forms.Padding(3); + this.DependsTabPage.Size = new System.Drawing.Size(225, 518); + this.DependsTabPage.TabIndex = 1; + this.DependsTabPage.Text = "Depends"; + this.DependsTabPage.UseVisualStyleBackColor = true; + // + // DependsGraphTree + // + this.DependsGraphTree.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.DependsGraphTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.DependsGraphTree.Location = new System.Drawing.Point(3, 3); + this.DependsGraphTree.Name = "DependsGraphTree"; + this.DependsGraphTree.Size = new System.Drawing.Size(219, 512); + this.DependsGraphTree.TabIndex = 0; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.ContentsPreviewTree); + this.tabPage2.Controls.Add(this.ContentsDownloadButton); + this.tabPage2.Controls.Add(this.NotCachedLabel); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(225, 518); + this.tabPage2.TabIndex = 2; + this.tabPage2.Text = "Contents"; + this.tabPage2.UseVisualStyleBackColor = true; + // // StatusPanel // this.StatusPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); @@ -297,67 +470,34 @@ private void InitializeComponent() this.StatusLabel.TabIndex = 0; this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // Installed - // - this.Installed.HeaderText = "Installed"; - this.Installed.Name = "Installed"; - this.Installed.Width = 50; - // - // Update - // - this.Update.HeaderText = "Update"; - this.Update.Name = "Update"; - this.Update.Width = 46; - // - // ModName - // - this.ModName.HeaderText = "Name"; - this.ModName.Name = "ModName"; - this.ModName.ReadOnly = true; - this.ModName.Width = 58; - // - // Author - // - this.Author.HeaderText = "Author"; - this.Author.Name = "Author"; - this.Author.ReadOnly = true; - this.Author.Width = 61; + // NotCachedLabel // - // InstalledVersion + this.NotCachedLabel.Location = new System.Drawing.Point(3, 3); + this.NotCachedLabel.Name = "NotCachedLabel"; + this.NotCachedLabel.Size = new System.Drawing.Size(216, 30); + this.NotCachedLabel.TabIndex = 0; + this.NotCachedLabel.Text = "This mod is not in the cache, click \'Download\' to preview contents"; // - this.InstalledVersion.HeaderText = "Installed version"; - this.InstalledVersion.Name = "InstalledVersion"; - this.InstalledVersion.ReadOnly = true; - this.InstalledVersion.Width = 97; + // ContentsDownloadButton // - // LatestVersion + this.ContentsDownloadButton.Location = new System.Drawing.Point(6, 36); + this.ContentsDownloadButton.Name = "ContentsDownloadButton"; + this.ContentsDownloadButton.Size = new System.Drawing.Size(103, 23); + this.ContentsDownloadButton.TabIndex = 1; + this.ContentsDownloadButton.Text = "Download"; + this.ContentsDownloadButton.UseVisualStyleBackColor = true; // - this.LatestVersion.HeaderText = "Latest version"; - this.LatestVersion.Name = "LatestVersion"; - this.LatestVersion.ReadOnly = true; - this.LatestVersion.Width = 88; + // ContentsPreviewTree // - // KSPVersion - // - this.KSPVersion.HeaderText = "KSP version"; - this.KSPVersion.Name = "KSPVersion"; - this.KSPVersion.Width = 81; - // - // Description - // - this.Description.HeaderText = "Description"; - this.Description.Name = "Description"; - this.Description.ReadOnly = true; - this.Description.Width = 83; - // - // Homepage - // - this.Homepage.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.Homepage.HeaderText = "Homepage"; - this.Homepage.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; - this.Homepage.Name = "Homepage"; - this.Homepage.ReadOnly = true; - this.Homepage.Width = 63; + this.ContentsPreviewTree.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.ContentsPreviewTree.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.ContentsPreviewTree.Enabled = false; + this.ContentsPreviewTree.Location = new System.Drawing.Point(0, 65); + this.ContentsPreviewTree.Name = "ContentsPreviewTree"; + this.ContentsPreviewTree.Size = new System.Drawing.Size(222, 450); + this.ContentsPreviewTree.TabIndex = 2; // // Main // @@ -367,13 +507,12 @@ private void InitializeComponent() this.Controls.Add(this.StatusPanel); this.Controls.Add(this.splitContainer1); this.Controls.Add(this.FilterByNamePanel); - this.Controls.Add(this.ModFilter); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.menuStrip1); this.Controls.Add(this.menuStrip2); this.MainMenuStrip = this.menuStrip1; this.Name = "Main"; - this.Text = "CKAN-GUI v0.1"; + this.Text = "CKAN-GUI"; this.Load += new System.EventHandler(this.Main_Load); this.menuStrip1.ResumeLayout(false); this.menuStrip1.PerformLayout(); @@ -384,9 +523,13 @@ private void InitializeComponent() this.FilterByNamePanel.PerformLayout(); this.splitContainer1.Panel1.ResumeLayout(false); this.splitContainer1.Panel2.ResumeLayout(false); - this.splitContainer1.Panel2.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); this.splitContainer1.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.DependsTabPage.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); this.StatusPanel.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -405,7 +548,6 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem RefreshToolButton; private System.Windows.Forms.ToolStripMenuItem UpdateAllToolButton; private System.Windows.Forms.ToolStripMenuItem ApplyToolButton; - private System.Windows.Forms.ListBox ModFilter; private System.Windows.Forms.TextBox FilterByNameTextBox; private System.Windows.Forms.Label FilterByNameLabel; private System.Windows.Forms.DataGridView ModList; @@ -424,6 +566,21 @@ private void InitializeComponent() private DataGridViewTextBoxColumn KSPVersion; private DataGridViewTextBoxColumn Description; private DataGridViewLinkColumn Homepage; + private ToolStripMenuItem FilterToolButton; + private ToolStripMenuItem FilterAllButton; + private ToolStripMenuItem FilterInstalledButton; + private ToolStripMenuItem FilterInstalledUpdateButton; + private ToolStripMenuItem FilterNewButton; + private ToolStripMenuItem FilterNotInstalledButton; + private ToolStripMenuItem FilterIncompatibleButton; + private TabControl tabControl1; + private TabPage tabPage1; + private TabPage DependsTabPage; + private TreeView DependsGraphTree; + private TabPage tabPage2; + private Label NotCachedLabel; + private TreeView ContentsPreviewTree; + private Button ContentsDownloadButton; } } diff --git a/CKAN/GUI/Main.cs b/CKAN/GUI/Main.cs index abfac72799..8a0ddce734 100644 --- a/CKAN/GUI/Main.cs +++ b/CKAN/GUI/Main.cs @@ -31,10 +31,25 @@ public partial class Main : Form private static Main m_Instance; public Configuration m_Configuration = null; + public ControlFactory controlFactory = null; + public Main() { + controlFactory = new ControlFactory(); m_Instance = this; InitializeComponent(); + + RecreateDialogs (); + } + + public void RecreateDialogs() + { + m_ApplyChangesDialog = controlFactory.CreateControl(); + m_ErrorDialog = controlFactory.CreateControl(); + m_RecommendsDialog = controlFactory.CreateControl(); + m_SettingsDialog = controlFactory.CreateControl(); + m_WaitDialog = controlFactory.CreateControl(); + m_YesNoDialog = controlFactory.CreateControl(); } public static Main Instance @@ -82,12 +97,6 @@ private void RefreshToolButton_Click(object sender, EventArgs e) UpdateRepo(); } - private void ModFilter_SelectedIndexChanged(object sender, EventArgs e) - { - m_ModFilter = (GUIModFilter) ModFilter.SelectedIndex; - UpdateModsList(); - } - private void MarkAllUpdatesToolButton_Click(object sender, EventArgs e) { foreach (DataGridViewRow row in ModList.Rows) @@ -114,25 +123,13 @@ private void MarkAllUpdatesToolButton_Click(object sender, EventArgs e) ModList.Refresh(); } - private void ModList_SelectedIndexChanged(object sender, EventArgs e) + private void UpdateModInfo(CkanModule module) { - if (ModList.SelectedRows.Count == 0) - { - return; - } - - DataGridViewRow selectedItem = ModList.SelectedRows[0]; - if (selectedItem == null) - { - return; - } - - var module = (CkanModule) selectedItem.Tag; - if (module == null) - { - return; - } + Util.Invoke(ModInfo, () => _UpdateModInfo(module)); + } + private void _UpdateModInfo(CkanModule module) + { ModInfo.Text = ""; ModInfo.AppendText(String.Format("\"{0}\" - version {1}\r\n", module.name, module.version)); @@ -153,7 +150,12 @@ private void ModList_SelectedIndexChanged(object sender, EventArgs e) ModInfo.AppendText(String.Format("Comment: {0}\r\n", module.comment)); ModInfo.AppendText(String.Format("Download: {0}\r\n", module.download)); ModInfo.AppendText(String.Format("Identifier: {0}\r\n", module.identifier)); - ModInfo.AppendText(String.Format("KSP Version: {0}\r\n", module.ksp_version)); + + if (module.ksp_version != null) + { + ModInfo.AppendText (String.Format ("KSP Version: {0}\r\n", module.ksp_version)); + } + ModInfo.AppendText(String.Format("License: {0}\r\n", module.license.ToString())); ModInfo.AppendText(String.Format("Release status: {0}\r\n", module.release_status)); @@ -208,6 +210,100 @@ private void ModList_SelectedIndexChanged(object sender, EventArgs e) ModInfo.AppendText("\r\n"); } + private void UpdateModDependencyGraphRecursively(TreeNode node, CkanModule module) + { + int i = 0; + + node.Text = module.name; + node.Nodes.Clear(); + + if (module.depends != null) + { + foreach (dynamic dependency in module.depends) + { + Registry registry = RegistryManager.Instance().registry; + + try + { + dynamic dependencyModule = registry.LatestAvailable(dependency.name.ToString(), KSP.Version()); + + node.Nodes.Add(""); + UpdateModDependencyGraphRecursively(node.Nodes[i], dependencyModule); + i++; + } + catch (Exception) + { + } + } + } + } + + private void UpdateModDependencyGraph(CkanModule module) + { + Util.Invoke(DependsGraphTree, () => _UpdateModDependencyGraph(module)); + } + + private void UpdateModContentsGraphRecursively(TreeNode node, CkanModule module) + { + + } + + private void _UpdateModDependencyGraph(CkanModule module) + { + DependsGraphTree.Nodes.Clear(); + DependsGraphTree.Nodes.Add(""); + UpdateModDependencyGraphRecursively(DependsGraphTree.Nodes[0], module); + DependsGraphTree.Nodes[0].ExpandAll(); + } + + private void UpdateModContentsTree(CkanModule module) + { + Util.Invoke(ContentsPreviewTree, () => _UpdateModContentsTree(module)); + } + + private void _UpdateModContentsTree(CkanModule module) + { + if (ModuleInstaller.IsCached(module)) + { + NotCachedLabel.Text = "Module is cached, preview available"; + ContentsDownloadButton.Enabled = false; + } + else + { + NotCachedLabel.Text = "This mod is not in the cache, click 'Download' to preview contents"; + ContentsDownloadButton.Enabled = true; + } + + ContentsPreviewTree.Nodes.Clear(); + ContentsPreviewTree.Nodes.Add(module.name); + UpdateModContentsGraphRecursively(ContentsPreviewTree.Nodes[0], module); + ContentsPreviewTree.Nodes[0].ExpandAll(); + } + + private void ModList_SelectedIndexChanged(object sender, EventArgs e) + { + if (ModList.SelectedRows.Count == 0) + { + return; + } + + DataGridViewRow selectedItem = ModList.SelectedRows[0]; + if (selectedItem == null) + { + return; + } + + var module = (CkanModule) selectedItem.Tag; + if (module == null) + { + return; + } + + UpdateModInfo(module); + UpdateModDependencyGraph(module); + UpdateModContentsTree(module); + } + private void ApplyToolButton_Click(object sender, EventArgs e) { List> changeset = ComputeChangeSetFromModList(); @@ -295,7 +391,8 @@ private void ModList_CellContentClick(object sender, DataGridViewCellEventArgs e ModList.EndEdit(); - if (ComputeChangeSetFromModList().Any()) + var changeset = ComputeChangeSetFromModList(); + if (changeset != null && changeset.Any()) { ApplyToolButton.Enabled = true; } @@ -309,5 +406,47 @@ private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { m_SettingsDialog.ShowDialog(); } + + private void FilterAllButton_Click(object sender, EventArgs e) + { + m_ModFilter = GUIModFilter.All; + FilterToolButton.Text = "Filter (All)"; + UpdateModsList(); + } + + private void FilterInstalledButton_Click(object sender, EventArgs e) + { + m_ModFilter = GUIModFilter.Installed; + FilterToolButton.Text = "Filter (Installed)"; + UpdateModsList(); + } + + private void FilterInstalledUpdateButton_Click(object sender, EventArgs e) + { + m_ModFilter = GUIModFilter.InstalledUpdateAvailable; + FilterToolButton.Text = "Filter (Updated)"; + UpdateModsList(); + } + + private void FilterNewButton_Click(object sender, EventArgs e) + { + m_ModFilter = GUIModFilter.NewInRepository; + FilterToolButton.Text = "Filter (New)"; + UpdateModsList(); + } + + private void FilterNotInstalledButton_Click(object sender, EventArgs e) + { + m_ModFilter = GUIModFilter.NotInstalled; + FilterToolButton.Text = "Filter (Not installed)"; + UpdateModsList(); + } + + private void FilterIncompatibleButton_Click(object sender, EventArgs e) + { + m_ModFilter = GUIModFilter.Incompatible; + FilterToolButton.Text = "Filter (Incompatible)"; + UpdateModsList(); + } } } \ No newline at end of file diff --git a/CKAN/GUI/Main.resx b/CKAN/GUI/Main.resx index 80bf53befa..edf7fa35f3 100644 --- a/CKAN/GUI/Main.resx +++ b/CKAN/GUI/Main.resx @@ -153,4 +153,31 @@ True + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + \ No newline at end of file diff --git a/CKAN/GUI/MainDialogs.cs b/CKAN/GUI/MainDialogs.cs index 02b62c2f4f..0d65148906 100644 --- a/CKAN/GUI/MainDialogs.cs +++ b/CKAN/GUI/MainDialogs.cs @@ -5,24 +5,16 @@ namespace CKAN { public partial class Main : Form { - private readonly ApplyChangesDialog m_ApplyChangesDialog = new ApplyChangesDialog(); - private readonly ErrorDialog m_ErrorDialog = new ErrorDialog(); - private readonly RecommendsDialog m_RecommendsDialog = new RecommendsDialog(); - private readonly SettingsDialog m_SettingsDialog = new SettingsDialog(); - private readonly WaitDialog m_WaitDialog = new WaitDialog(); - private readonly YesNoDialog m_YesNoDialog = new YesNoDialog(); + private ApplyChangesDialog m_ApplyChangesDialog = null; + private ErrorDialog m_ErrorDialog = null; + private RecommendsDialog m_RecommendsDialog = null; + private SettingsDialog m_SettingsDialog = null; + private WaitDialog m_WaitDialog = null; + private YesNoDialog m_YesNoDialog = null; public void AddStatusMessage(string text, params object[] args) { - if (StatusLabel.InvokeRequired) - { - StatusLabel.Invoke(new MethodInvoker(delegate { StatusLabel.Text = String.Format(text, args); })); - } - else - { - StatusLabel.Text = String.Format(text, args); - } - + Util.Invoke(StatusLabel, () => StatusLabel.Text = String.Format(text, args)); m_WaitDialog.AddLogMessage(String.Format(text, args)); } @@ -38,14 +30,14 @@ public bool YesNoDialog(string text) public void ShowWaitDialog() { - Enabled = false; + Util.Invoke(this, () => Enabled = false); m_WaitDialog.ShowWaitDialog(); } public void HideWaitDialog() { m_WaitDialog.Close(); - Enabled = true; + Util.Invoke(this, () => Enabled = true); } } } \ No newline at end of file diff --git a/CKAN/GUI/MainInstall.cs b/CKAN/GUI/MainInstall.cs index a060a77539..4592e37a1e 100644 --- a/CKAN/GUI/MainInstall.cs +++ b/CKAN/GUI/MainInstall.cs @@ -167,7 +167,9 @@ private void PostInstallMods(object sender, RunWorkerCompletedEventArgs e) UpdateModFilterList(); AddStatusMessage(""); m_WaitDialog.Close(); - Enabled = true; + + Util.Invoke(this, () => RecreateDialogs()); + Util.Invoke(this, () => Enabled = true); } private List GetInstallDependencies(CkanModule module, RelationshipResolverOptions options) diff --git a/CKAN/GUI/MainModList.cs b/CKAN/GUI/MainModList.cs index 0fe4baad47..fa732e820f 100644 --- a/CKAN/GUI/MainModList.cs +++ b/CKAN/GUI/MainModList.cs @@ -43,6 +43,7 @@ private List> ComputeChangeSetFromMod } RelationshipResolverOptions options = RelationshipResolver.DefaultOpts(); + options.with_recommends = false; RelationshipResolver resolver = null; try @@ -169,27 +170,42 @@ private List GetModsByFilter(GUIModFilter filter) private void UpdateModFilterList() { - ModFilter.Items[0] = String.Format("All ({0})", CountModsByFilter(GUIModFilter.All)); - ModFilter.Items[1] = String.Format("Installed ({0})", CountModsByFilter(GUIModFilter.Installed)); - ModFilter.Items[2] = String.Format("Updated ({0})", CountModsByFilter(GUIModFilter.InstalledUpdateAvailable)); - ModFilter.Items[3] = String.Format("New in repository ({0})", - CountModsByFilter(GUIModFilter.NewInRepository)); - ModFilter.Items[4] = String.Format("Not installed ({0})", CountModsByFilter(GUIModFilter.NotInstalled)); - ModFilter.Items[5] = String.Format("Incompatible ({0})", CountModsByFilter(GUIModFilter.Incompatible)); - } - - public void UpdateModsList(bool markUpdates = false) - { - if (ModList.InvokeRequired) + if (menuStrip2.InvokeRequired) { - ModList.Invoke(new MethodInvoker(delegate { _UpdateModsList(markUpdates); })); + menuStrip2.Invoke(new MethodInvoker(delegate { _UpdateModFilterList(); })); } else { - _UpdateModsList(markUpdates); + _UpdateModFilterList(); } } + private void _UpdateModFilterList() + { + FilterToolButton.DropDownItems[0].Text = String.Format("All ({0})", + CountModsByFilter(GUIModFilter.All)); + + FilterToolButton.DropDownItems[1].Text = String.Format("Installed ({0})", + CountModsByFilter(GUIModFilter.Installed)); + + FilterToolButton.DropDownItems[2].Text = String.Format("Updated ({0})", + CountModsByFilter(GUIModFilter.InstalledUpdateAvailable)); + + FilterToolButton.DropDownItems[3].Text = String.Format("New in repository ({0})", + CountModsByFilter(GUIModFilter.NewInRepository)); + + FilterToolButton.DropDownItems[4].Text = String.Format("Not installed ({0})", + CountModsByFilter(GUIModFilter.NotInstalled)); + + FilterToolButton.DropDownItems[5].Text = String.Format("Incompatible ({0})", + CountModsByFilter(GUIModFilter.Incompatible)); + } + + public void UpdateModsList(bool markUpdates = false) + { + Util.Invoke(this, () => _UpdateModsList(markUpdates)); + } + private void _UpdateModsList(bool markUpdates) { ModList.Rows.Clear(); diff --git a/CKAN/GUI/MainRepo.cs b/CKAN/GUI/MainRepo.cs index 4c687f7606..3912df31d6 100644 --- a/CKAN/GUI/MainRepo.cs +++ b/CKAN/GUI/MainRepo.cs @@ -11,7 +11,9 @@ public partial class Main : Form public void UpdateRepo() { m_UpdateRepoWorker.RunWorkerAsync(); - Enabled = false; + + Util.Invoke(this, () => Enabled = false); + m_WaitDialog.SetDescription("Contacting repository.."); m_WaitDialog.ClearLog(); m_WaitDialog.ShowWaitDialog(); @@ -39,7 +41,9 @@ private void PostUpdateRepo(object sender, RunWorkerCompletedEventArgs e) m_WaitDialog.HideWaitDialog(); AddStatusMessage("Repository successfully updated"); - Enabled = true; + + Util.Invoke(this, () => Enabled = true); + Util.Invoke(this, () => RecreateDialogs()); } } } \ No newline at end of file diff --git a/CKAN/GUI/RecommendsDialog.cs b/CKAN/GUI/RecommendsDialog.cs index 3308a272ae..01fa9b1e6c 100644 --- a/CKAN/GUI/RecommendsDialog.cs +++ b/CKAN/GUI/RecommendsDialog.cs @@ -9,18 +9,12 @@ public partial class RecommendsDialog : Form public RecommendsDialog() { InitializeComponent(); + StartPosition = FormStartPosition.CenterScreen; } public List ShowRecommendsDialog(string message, List recommended) { - if (MessageLabel.InvokeRequired) - { - MessageLabel.Invoke(new MethodInvoker(delegate { MessageLabel.Text = message; })); - } - else - { - MessageLabel.Text = message; - } + Util.Invoke(MessageLabel, () => MessageLabel.Text = message); if (RecommendedListView.InvokeRequired) { @@ -50,7 +44,6 @@ public List ShowRecommendsDialog(string message, List recommende } } - StartPosition = FormStartPosition.CenterScreen; if (ShowDialog() == DialogResult.OK) { diff --git a/CKAN/GUI/Util.cs b/CKAN/GUI/Util.cs new file mode 100644 index 0000000000..079ab77aba --- /dev/null +++ b/CKAN/GUI/Util.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace CKAN +{ + + public class Util + { + + // utility helper to deal with multi-threading and UI + public static void Invoke(T obj, Action action) where T : System.Windows.Forms.Control + { + if (obj.InvokeRequired) // if we're not in the UI thread + { + // enqueue call on UI thread and wait for it to return + obj.Invoke(new MethodInvoker(action)); + } + else + { + // we're on the UI thread, execute directly + action(); + } + } + + // utility helper to deal with multi-threading and UI + // async version, doesn't wait for UI thread + // use with caution, when not sure use blocking Invoke() + public static void AsyncInvoke(T obj, Action action) where T : System.Windows.Forms.Control + { + if (obj.InvokeRequired) // if we're not in the UI thread + { + // enqueue call on UI thread and continue + obj.BeginInvoke(new MethodInvoker(action)); + } + else + { + // we're on the UI thread, execute directly + action(); + } + } + + } + +} diff --git a/CKAN/GUI/WaitDialog.cs b/CKAN/GUI/WaitDialog.cs index 29f45c0cd5..7d772b10ce 100644 --- a/CKAN/GUI/WaitDialog.cs +++ b/CKAN/GUI/WaitDialog.cs @@ -8,99 +8,47 @@ public partial class WaitDialog : Form public WaitDialog() { InitializeComponent(); + StartPosition = FormStartPosition.CenterScreen; DialogProgressBar.Minimum = 0; DialogProgressBar.Maximum = 100; } public void ShowWaitDialog() { - StartPosition = FormStartPosition.CenterScreen; - ShowDialog(); + Util.Invoke(this, () => ShowDialog()); } public void HideWaitDialog() { - if (MessageTextBox.InvokeRequired) - { - MessageTextBox.Invoke( - new MethodInvoker(delegate { MessageTextBox.Text = "Waiting for operation to complete"; })); - } - else - { - MessageTextBox.Text = "Waiting for operation to complete"; - } - - Close(); + Util.Invoke(MessageTextBox, () => MessageTextBox.Text = "Waiting for operation to complete"); + Util.Invoke(this, () => Close()); } public void SetProgress(int progress) { - if (DialogProgressBar.InvokeRequired) - { - DialogProgressBar.Invoke(new MethodInvoker(delegate - { - DialogProgressBar.Value = progress; - DialogProgressBar.Style = ProgressBarStyle.Continuous; - })); - } - else - { - DialogProgressBar.Value = progress; - DialogProgressBar.Style = ProgressBarStyle.Continuous; - } + Util.Invoke(DialogProgressBar, () => DialogProgressBar.Value = progress); + Util.Invoke(DialogProgressBar, () => DialogProgressBar.Style = ProgressBarStyle.Continuous); } public void ResetProgress() { - if (DialogProgressBar.InvokeRequired) - { - DialogProgressBar.Invoke( - new MethodInvoker(delegate { DialogProgressBar.Style = ProgressBarStyle.Marquee; })); - } - else - { - DialogProgressBar.Style = ProgressBarStyle.Marquee; - } + Util.Invoke(DialogProgressBar, () => DialogProgressBar.Style = ProgressBarStyle.Marquee); } public void SetDescription(string message) { - if (MessageTextBox.InvokeRequired) - { - MessageTextBox.Invoke(new MethodInvoker(delegate { MessageTextBox.Text = "(" + message + ")"; })); - } - else - { - MessageTextBox.Text = "(" + message + ")"; - } + Util.Invoke(MessageTextBox, () => MessageTextBox.Text = "(" + message + ")"); } public void ClearLog() { - if (LogTextBox.InvokeRequired) - { - LogTextBox.Invoke(new MethodInvoker(delegate { LogTextBox.Text = ""; })); - } - else - { - LogTextBox.Text = ""; - } + Util.Invoke(LogTextBox, () => LogTextBox.Text = ""); } public void AddLogMessage(string message) { - if (LogTextBox.InvokeRequired) - { - LogTextBox.Invoke(new MethodInvoker(delegate { LogTextBox.AppendText(message + "\r\n"); })); - } - else - { - LogTextBox.AppendText(message + "\r\n"); - } + Util.Invoke(LogTextBox, () => LogTextBox.AppendText(message + "\r\n")); } - private void ActionDescriptionLabel_Click(object sender, EventArgs e) - { - } } } \ No newline at end of file diff --git a/CKAN/GUI/YesNoDialog.cs b/CKAN/GUI/YesNoDialog.cs index 833654132d..f84e457ae9 100644 --- a/CKAN/GUI/YesNoDialog.cs +++ b/CKAN/GUI/YesNoDialog.cs @@ -7,26 +7,18 @@ public partial class YesNoDialog : Form public YesNoDialog() { InitializeComponent(); + StartPosition = FormStartPosition.CenterScreen; } public DialogResult ShowYesNoDialog(string text) { - if (DescriptionLabel.InvokeRequired) - { - DescriptionLabel.Invoke(new MethodInvoker(delegate { DescriptionLabel.Text = text; })); - } - else - { - DescriptionLabel.Text = text; - } - - StartPosition = FormStartPosition.CenterScreen; + Util.Invoke(DescriptionLabel, () => DescriptionLabel.Text = text); return ShowDialog(); } public void HideYesNoDialog() { - Close(); + Util.Invoke(this, () => Close()); } } } \ No newline at end of file