/* * Copyright (c) 2009 Craig Sutherland * * 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 FastForward.Monitor { using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Linq; using System.Threading; using System.Windows.Forms; using FastForward.Monitor.Properties; using FastForward.WinCore; using FastForward.WinCore.Configuration; using ThoughtWorks.CruiseControl.Remote; using ThoughtWorks.CruiseControl.Remote.Monitor; using ThoughtWorks.CruiseControl.Remote.Parameters; /// /// The main UI for the application. /// public partial class MainForm : Form { #region Private fields private ApplicationConfig configuration; private ServerList serverList; private object selectedControl; private Dictionary servers = new Dictionary(); private Dictionary queues = new Dictionary(); private Dictionary requests = new Dictionary(); private int allQueuesSort = 0; private int allQueueRequestsSort = 0; private WindowManager webPages = new WindowManager(); private WindowManager currentStatuses = new WindowManager(); private WindowManager buildHistories = new WindowManager(); private WindowManager packages = new WindowManager(); private WindowManager properties = new WindowManager(); private PluginHost pluginHost; private Dictionary plugins = new Dictionary(); private Dictionary pluginTabs = new Dictionary(); private Dictionary allErrors = new Dictionary(); #endregion #region Constructors /// /// Initialise a new . /// /// /// public MainForm(ServerList servers, ApplicationConfig configuration) { InitializeComponent(); this.configuration = configuration; this.serverList = servers; } #endregion #region Public methods #region InitialiseForm() /// /// Initialise the main form. /// public void InitialiseForm() { using (Trace.Start("Initialising main form")) { pluginHost = new MainFormPluginHost(this); serverList.ServerAdded += servers_ServerAdded; serverList.ServerRemoved += servers_ServerRemoved; foreach (var server in serverList.Servers) { AddServer(server); } RefreshServers(); selectedControl = allProjectsView; InitialiseFromConfiguration(); DisplayStatus("Application loaded"); } } #endregion #region Terminate() /// /// Shut down the application. /// public void Terminate() { foreach (var plugin in plugins.Values) { try { plugin.Terminate(); } catch { // Ignore any errors here - this could be due to the UI being unstable } } // Just to make sure we don't re-terminate and shutdown again plugins.Clear(); } #endregion #region LogErrorMessage() /// /// Logs an error message. /// /// /// public void LogErrorMessage(string source, string message) { allErrors.Add(DateTime.Now, message); var count = allErrors.Count; errorReport.Text = string.Format("{0} error{1}", count, count == 1 ? string.Empty : "s"); statusLabel.Text = message; statusLabel.Image = applicationImages.Images["Failure"]; statusLabel.Owner.Refresh(); } #endregion #region DisplayStatus() /// /// Display a success message. /// /// The message to display. public void DisplayStatus(string message) { statusLabel.Text = message; statusLabel.Image = applicationImages.Images["Success"]; statusLabel.Owner.Refresh(); } /// /// Display a success message. /// /// The message to display. public void DisplayStatus(string message, int count, string singleItem, string pluralItem) { statusLabel.Text = string.Format(message, count, count == 1 ? singleItem : pluralItem); statusLabel.Image = applicationImages.Images["Success"]; statusLabel.Owner.Refresh(); } #endregion #endregion #region Private methods #region InitialiseFromConfiguration() /// /// Initialise this form from the configuration. /// private void InitialiseFromConfiguration() { using (Trace.Start("Loading configuration settings")) { // Load the window position if (configuration.Monitor.Left != int.MinValue) Left = configuration.Monitor.Left; if (configuration.Monitor.Top != int.MinValue) Top = configuration.Monitor.Top; if (configuration.Monitor.Width != int.MinValue) Width = configuration.Monitor.Width; if (configuration.Monitor.Height != int.MinValue) Height = configuration.Monitor.Height; WindowState = configuration.Monitor.State; // Set the windows properties ChangeWindowsProperties(); webPages.ConfiguredWindows.AddRange(from record in configuration.ConfiguredWindows where record.Name.StartsWith("WebPage") select record); currentStatuses.ConfiguredWindows.AddRange(from record in configuration.ConfiguredWindows where record.Name.StartsWith("Status") select record); buildHistories.ConfiguredWindows.AddRange(from record in configuration.ConfiguredWindows where record.Name.StartsWith("History") select record); packages.ConfiguredWindows.AddRange(from record in configuration.ConfiguredWindows where record.Name.StartsWith("Packages") select record); properties.ConfiguredWindows.AddRange(from record in configuration.ConfiguredWindows where record.Name.StartsWith("Properties") select record); // Load the visibility settings monitorExplorerDisplay.Checked = !configuration.Monitor.ShowExplorer; monitorExplorerDisplay_Click(null, null); groupByServerMenu.Checked = !configuration.Monitor.GroupByServer; groupByServerMenu_Click(null, null); // Load the list views allProjectsView.LoadFromConfig(configuration.Monitor.ListViews); allQueuesSort = ListViewHelper.LoadListView(allQueuesList, configuration.Monitor.ListViews); allQueueRequestsSort = ListViewHelper.LoadListView(allQueueRequestsList, configuration.Monitor.ListViews); // Load the plugins InitialisePlugins(); // Load the tabs DisplayTabs(); } } #endregion #region DisplayTabs() /// /// Display the tabs in the correct order. /// private void DisplayTabs() { using (Trace.Start("Displaying tabs")) { // The tabs have not been loaded - make sure they are all loaded. if (configuration.Monitor.Tabs.Count == 0) { configuration.Monitor.Tabs.AddRange(new TabConfig[] { new TabConfig { Key = "AllProjects", Text = "All Projects", Order = 0, Type = TabConfigType.System }, new TabConfig { Key = "AllQueues", Text = "All Queues", Order = 1, Type = TabConfigType.System }, new TabConfig { Key = "AllQueueRequests", Text = "All Queue Requests", Order = 2, Type = TabConfigType.System } }); } // Add all the tabs monitorTabs.TabPages.Clear(); foreach (var tab in from record in configuration.Monitor.Tabs orderby record.Order select record) { // Initialise the tab switch (tab.Type) { case TabConfigType.System: // Add the system tabs switch (tab.Key) { case "AllProjects": monitorTabs.TabPages.Add(allProjectsPage); break; case "AllQueues": monitorTabs.TabPages.Add(allQueuesPage); break; case "AllQueueRequests": monitorTabs.TabPages.Add(allQueueRequestsPage); break; } break; case TabConfigType.Plugin: if (pluginTabs.ContainsKey(tab.Key)) { monitorTabs.TabPages.Add(pluginTabs[tab.Key]); } break; } } } } #endregion #region InitialisePlugins() /// /// Initialise the plugins. /// private void InitialisePlugins() { using (Trace.Start("Initialising plug-ins")) { // Make sure the plug-ins location exists try { if (!Directory.Exists(configuration.DefaultPluginLocation)) Directory.CreateDirectory(configuration.DefaultPluginLocation); } catch { // This may fail because of security } // Initialise all the plugins var invalidPlugins = new List(); foreach (var plugin in configuration.LoadedPlugins) { try { using (Trace.Start("Starting plug-in: " + plugin.Type)) { var instance = Plugin.StartNew(plugin); instance.Initialise(pluginHost, serverList); plugins.Add(plugin.Key, instance); } } catch (Exception error) { MessageBox.Show( "Unable to load plug-in " + plugin.Type + ", plug-in will be removed" + Environment.NewLine + error.Message, "Plug-in error", MessageBoxButtons.OK, MessageBoxIcon.Warning); invalidPlugins.Add(plugin); } } // Remove the invalid plugins foreach (var plugin in invalidPlugins) { configuration.LoadedPlugins.Remove(plugin); } } } #endregion #region ChangeWindowsProperties /// /// Change the windows settings to use the settings from the configuration. /// private void ChangeWindowsProperties() { ShowInTaskbar = configuration.Monitor.ShowInTaskBar; TopMost = configuration.Monitor.AlwaysOnTop; } #endregion #region AddServer() /// /// Add a new server. /// /// private void AddServer(Server server) { var serverConfig = server.Data.Get(); // Add the groups server.DisplayName = serverConfig.Name; var serverName = server.GetDisplayName(true); allProjectsView.AddServer(server, serverConfig); allQueuesList.Groups.Add(server.TargetAddress, serverName); allQueueRequestsList.Groups.Add(server.TargetAddress, serverName); var node = new ServerTreeNode(server, serverConfig); monitorExplorer.Nodes.Add(node); servers.Add(server, node); // Add the queues server.BuildQueueAdded += server_BuildQueueAdded; server.BuildQueueRemoved += server_BuildQueueRemoved; foreach (var queue in server.BuildQueues) { AddBuildQueue(queue); } } #endregion #region RemoveServer() /// /// Remove an existing server. /// /// private void RemoveServer(Server server) { if (servers.ContainsKey(server)) { servers[server].Remove(); } // Remove the queues server.BuildQueueAdded -= server_BuildQueueAdded; server.BuildQueueRemoved -= server_BuildQueueRemoved; foreach (var queue in server.BuildQueues) { RemoveBuildQueue(queue); } // Remove the groups allProjectsView.RemoveServer(server); allQueuesList.Groups.Add(allQueuesList.Groups[server.TargetAddress]); allQueueRequestsList.Groups.Add(allQueueRequestsList.Groups[server.TargetAddress]); } #endregion #region AddBuildQueue() /// /// Add a new buid queue. /// /// private void AddBuildQueue(BuildQueue queue) { queue.BuildQueueRequestAdded += queue_BuildQueueRequestAdded; queue.BuildQueueRequestRemoved += queue_BuildQueueRequestRemoved; var item = new BuildQueueListViewItem(queue); allQueuesList.Items.Add(item); queues.Add(queue, item); item.Group = allQueuesList.Groups[queue.Server.TargetAddress]; } #endregion #region RemoveBuildQueue /// /// Remove an existing build queue. /// /// private void RemoveBuildQueue(BuildQueue queue) { queue.BuildQueueRequestAdded -= queue_BuildQueueRequestAdded; queue.BuildQueueRequestRemoved -= queue_BuildQueueRequestRemoved; if (queues.ContainsKey(queue)) { queues[queue].Remove(); } foreach (var request in queue.Requests) { if (requests.ContainsKey(request)) { requests[request].Remove(); } } } #endregion #region AddBuildQueueRequest() /// /// Add a new build queue request. /// /// private void AddBuildQueueRequest(BuildQueueRequest value) { if (!requests.ContainsKey(value)) { var item = new BuildQueueRequestListViewItem(value); allQueueRequestsList.Items.Add(item); requests.Add(value, item); item.Group = allQueueRequestsList.Groups[value.BuildQueue.Server.TargetAddress]; } } #endregion #region ForceBuild() /// /// Force a build for the currently selected projects. /// private void ForceBuild() { var projects = GetSelectedProjects(); if (projects.Count() == 0) { MessageBox.Show("There were no projects selected"); } else { using (Trace.Start("Sending force build commands")) { // Check for any parameters var parameters = new Dictionary>(); foreach (var project in projects) { try { parameters.Add(project, project.RetrieveParameters()); } catch (NotImplementedException) { // The underlying server does not have any parameters functionality parameters.Add(project, new List()); } } // Count the number of parameters var parameterCount = (from record in parameters.Values select record.Count()).Sum(); if (parameterCount > 0) { // Prompt the user for the parameters first var form = new BuildParametersForm(parameters, this); form.ShowDialog(this); } else { // No parameters - just send the requests foreach (var project in projects) { using (Trace.Start("Sending force build to " + project.Name)) { SendRequest(p => p.ForceBuild(), project, project.Name); } } DisplayStatus("Force build sent to {0} {1}", projects.Count(), "project", "projects"); } } } } #endregion #region AbortBuild() /// /// Abort a build for the currently selected projects. /// private void AbortBuild() { var projects = GetSelectedProjects(); if (projects.Count() == 0) { MessageBox.Show("There were no projects selected"); } else { using (Trace.Start("Sending abort build commands")) { foreach (var project in projects) { using (Trace.Start("Sending abort build to " + project.Name)) { SendRequest(p => p.AbortBuild(), project, project.Name); } } DisplayStatus("Abort build sent to {0} {1}", projects.Count(), "project", "projects"); } } } #endregion #region StartProject() /// /// Start the currently selected projects /// private void StartProject() { var projects = GetSelectedProjects(); if (projects.Count() == 0) { MessageBox.Show("There were no projects selected"); } else { using (Trace.Start("Sending start project commands")) { foreach (var project in projects) { using (Trace.Start("Sending start project to " + project.Name)) { SendRequest(p => p.Start(), project, project.Name); } } DisplayStatus("Start project sent to {0} {1}", projects.Count(), "project", "projects"); } } } #endregion #region StopProject() /// /// Stop the currently selected projects. /// private void StopProject() { var projects = GetSelectedProjects(); if (projects.Count() == 0) { MessageBox.Show("There were no projects selected"); } else { using (Trace.Start("Sending stop project commands")) { foreach (var project in projects) { using (Trace.Start("Sending stop project to " + project.Name)) { SendRequest(p => p.Stop(), project, project.Name); } } DisplayStatus("Stop project sent to {0} {1}", projects.Count(), "project", "projects"); } } } #endregion #region RefreshServers() /// /// Refresh all the servers. /// private void RefreshServers() { foreach (var server in serverList.Servers) { using (Trace.Start("Refreshing server: " + server.GetDisplayName(true))) { SendRequest(s => s.Refresh(), server, server.GetDisplayName()); } } } #endregion #region ChangeSettings() /// /// Allow the user to change the configuration settings. /// private void ChangeSettings() { // Need to change this otherwise the settings form will be underneath! var topMost = TopMost; TopMost = false; var settingsForm = new SettingsForm(configuration); settingsForm.OkClicked += (o, a) => { this.SafeInvoke(() => { topMost = ReloadAfterSettingsChange(settingsForm); }); }; settingsForm.ShowDialog(); TopMost = topMost; } #endregion #region ReloadAfterSettingsChange() /// /// Reload the settings after they have been changed. /// /// /// private bool ReloadAfterSettingsChange(SettingsForm settingsForm) { using (Trace.Start("Reloading configuration settings")) { var oldServers = configuration.Servers; configuration.Servers = settingsForm.Configuration.Servers; UpdateServers(oldServers); RefreshServers(); // Set the windows properties configuration.Monitor = settingsForm.Configuration.Monitor; ChangeWindowsProperties(); var topMost = configuration.Monitor.AlwaysOnTop; // Reload all the projects allProjectsView.Clear(); foreach (var server in serverList.Servers) { var serverConfig = server.Data.Get(); serverConfig.Server.DisplayName = serverConfig.Name; allProjectsView.AddServer(server, serverConfig); } // Copy the templates and plugins over configuration.BuildTemplates = settingsForm.Configuration.BuildTemplates; UpdatePlugins(settingsForm); // Copy the tabs over configuration.Monitor.Tabs = settingsForm.Configuration.Monitor.Tabs; DisplayTabs(); // Save the configuration configuration.Save(); BringToFront(); return topMost; } } #endregion #region UpdatePlugins() /// /// Update all the plugins. /// /// private void UpdatePlugins(SettingsForm settingsForm) { using (Trace.Start("Updating plugins")) { // Store the current list of plugins var currentPlugins = (from key in plugins select key.Key).ToList(); foreach (var plugin in settingsForm.Configuration.LoadedPlugins) { // Check if the plugin was previously loaded if (currentPlugins.Contains(plugin.Key)) { // If so, just update it var current = configuration.FindPlugin(plugin.Key); if (current.Data != plugin.Data) { current.Data = plugin.Data; plugins[current.Key].UpdateConfiguration(current); } currentPlugins.Remove(current.Key); } else { // Otherwise the new plugin needs to be initialised try { var newPlugin = Plugin.StartNew(plugin); newPlugin.Initialise(pluginHost, serverList); plugins.Add(plugin.Key, newPlugin); configuration.LoadedPlugins.Add(plugin); } catch (Exception error) { MessageBox.Show( "Unable to load plugin " + plugin.Type + ", plugin will be removed" + Environment.NewLine + error.Message, "Plug-in error", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } // Terminate any old plugins foreach (var plugin in currentPlugins) { plugins[plugin].Terminate(); plugins.Remove(plugin); var current = configuration.FindPlugin(plugin); configuration.LoadedPlugins.Remove(current); } } } #endregion #region UpdateServers() /// /// Update all the servers. /// /// private void UpdateServers(List previousServers) { using (Trace.Start("Updating servers")) { var oldServers = new List(); oldServers.AddRange(from record in serverList.Servers select record.Data.Get()); foreach (var server in configuration.Servers) { var oldServer = (from record in oldServers where (record.Address == server.Address) && (record.TargetServer == server.TargetServer) select record).SingleOrDefault(); if (oldServer != null) { var hasChanged = (oldServer.PollingInterval != server.PollingInterval) || (oldServer.UseEncryption != server.UseEncryption); if (hasChanged) { serverList.UpdateServer(server); } if (oldServer.Server != null) { oldServer.Server.Data.Set(server); oldServer.Server.DisplayName = server.Name; } oldServers.Remove(oldServer); } else { serverList.AddServer(server); } } foreach (var oldServer in oldServers) { serverList.RemoveServer(oldServer); } } } #endregion #region GetSelectedProjects() /// /// Get all the currently selected projects. /// /// private IEnumerable GetSelectedProjects() { var list = new List(); if (selectedControl is IProjectSelector) { list.AddRange((selectedControl as IProjectSelector).GetSelectedProjects()); } else if (selectedControl is ListView) { foreach (var item in (selectedControl as ListView).SelectedItems) { var projectItem = item as ProjectListViewItem; if (projectItem != null) list.Add(projectItem.Project); } } else if (selectedControl is TreeView) { var projectNode = (selectedControl as TreeView).SelectedNode as ProjectTreeNode; if (projectNode != null) list.Add(projectNode.Project); } return list; } #endregion #region GetSelectedQueues() /// /// Get all the currently selected build queues. /// /// private IEnumerable GetSelectedQueues() { var list = new List(); if (selectedControl is ListView) { foreach (var item in (selectedControl as ListView).SelectedItems) { var queueItem = item as BuildQueueListViewItem; if (queueItem != null) list.Add(queueItem.BuildQueue); } } else if (selectedControl is TreeView) { var queueNode = (selectedControl as TreeView).SelectedNode as BuildQueueTreeNode; if (queueNode != null) list.Add(queueNode.BuildQueue); } return list; } #endregion #region GetSelectedServers() /// /// Get all the currently selected servers. /// /// private IEnumerable GetSelectedServers() { var list = new List(); if (selectedControl is TreeView) { var serverNode = (selectedControl as TreeView).SelectedNode as ServerTreeNode; if (serverNode != null) list.Add(serverNode.Server); } return list; } #endregion #region SendRequest() /// /// Pool a request for a remote server to process. /// /// /// /// private void SendRequest(Action request, T value, string source) { ThreadPool.QueueUserWorkItem(o => { try { request((T)o); } catch (NotImplementedException) { LogErrorMessage(source, "This functionality is not allowed on the underlying client"); } catch (Exception error) { error.SaveLog(); LogErrorMessage(source, error.Message); } }, value); } #endregion #region ShowWebPage() /// /// Display the web page for the selected projects. /// private void ShowWebPage() { var projects = GetSelectedProjects(); ShowWebPage(projects); } /// /// Display the web page for some projects. /// private void ShowWebPage(IEnumerable projects) { if (projects.Count() == 0) { MessageBox.Show(this, "There are no projects selected", "View web page", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { foreach (var project in projects) { using (Trace.Start("Displaying web page for " + project.Name)) { webPages.Show(project, p => new WebPageForm(p, this), string.Format("WebPage:{0}:{1}", project.Server.TargetAddress, project.Name)); } } } } #endregion #region ShowPackages /// /// Display the packages for the selected projects. /// private void ShowPackages() { var projects = GetSelectedProjects(); ShowPackages(projects); } /// /// Display the packages for the selected projects. /// private void ShowPackages(IEnumerable projects) { if (projects.Count() == 0) { MessageBox.Show(this, "There are no projects selected", "View packages", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { foreach (var project in projects) { using (Trace.Start("Displaying packages for " + project.Name)) { packages.Show(project, p => new PackagesForm(p, this), string.Format("Packages:{0}:{1}", project.Server.TargetAddress, project.Name)); } } } } #endregion #region ShowCurrentStatus() /// /// Display the current status for the selected projects. /// private void ShowCurrentStatus() { var projects = GetSelectedProjects(); ShowCurrentStatus(projects); } /// /// Display the current status for the selected projects. /// private void ShowCurrentStatus(IEnumerable projects) { if (projects.Count() == 0) { MessageBox.Show(this, "There are no projects selected", "View current project status", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { foreach (var project in projects) { using (Trace.Start("Displaying current status for " + project.Name)) { currentStatuses.Show(project, p => new CurrentStatusForm(p, this), string.Format("Status:{0}:{1}", project.Server.TargetAddress, project.Name)); } } } } #endregion #region ShowBuildHistory() /// /// Display the build history for the selected projects. /// private void ShowBuildHistory() { var projects = GetSelectedProjects(); ShowBuildHistory(projects); } /// /// Display the build history for some projects. /// /// The projects to show the build history for. private void ShowBuildHistory(IEnumerable projects) { if (projects.Count() == 0) { MessageBox.Show(this, "There are no projects selected", "View build history", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { foreach (var project in projects) { using (Trace.Start("Displaying build history for " + project.Name)) { buildHistories.Show(project, p => new BuildHistoryForm(p, configuration, this), string.Format("History:{0}:{1}", project.Server.TargetAddress, project.Name)); } } } } #endregion #region Login() /// /// Login to a remote server. /// private void Login() { if (serverList.Servers.Count() == 0) { MessageBox.Show(this, "At least one server must be defined first", "Unable to login", MessageBoxButtons.OK, MessageBoxIcon.Warning); } else { var login = new LoginForm(serverList); login.ShowDialog(this); } } #endregion #region Logout() /// /// Logout from a remote server. /// private void Logout() { if (serverList.Servers.Count() == 0) { MessageBox.Show(this, "At least one server must be defined first", "Unable to login", MessageBoxButtons.OK, MessageBoxIcon.Warning); } else { var logoutForm = new LogoutForm(serverList); logoutForm.ShowDialog(this); } } #endregion #region ShowProperties() /// /// View the properties of the currently selected items. /// private void ShowProperties() { var selectedItems = new List(); selectedItems.AddRange(from record in GetSelectedServers() select new ServerProperties(record) as INotifyPropertyChanged); selectedItems.AddRange(from record in GetSelectedProjects() select new ProjectProperties(record) as INotifyPropertyChanged); selectedItems.AddRange(from record in GetSelectedQueues() select new BuildQueueProperties(record) as INotifyPropertyChanged); if (selectedItems.Count() == 0) { MessageBox.Show("There is nothing selected"); } else { foreach (INotifyPropertyChanged item in selectedItems) { using (Trace.Start("Displaying web page for " + item.GetType().Name)) { properties.Show(item, o => new PropertiesForm(o, this), string.Format("Properties:{0}", item)); } } } } #endregion #region PerformProjectDoubleClick() /// /// Performs a double click action on a project. /// /// private void PerformProjectDoubleClick(IEnumerable projects) { if (projects.Count() > 0) { switch (this.configuration.Monitor.AllProjectsDoubleClickAction) { case "View Build History": ShowBuildHistory(projects); break; case "View Current Status": ShowCurrentStatus(projects); break; case "View Packages": ShowPackages(projects); break; case "View Properties": ShowProperties(); break; case "View Web Page": ShowWebPage(projects); break; } } } #endregion #endregion #region Event handlers #region servers_ServerAdded() /// /// A new server has been added. /// /// /// private void servers_ServerAdded(object sender, ServerChangedArgs e) { this.SafeInvoke(() => { AddServer(e.Server); }); } #endregion #region servers_ServerRemoved() /// /// An existing server has been removed. /// /// /// private void servers_ServerRemoved(object sender, ServerChangedArgs e) { this.SafeInvoke(() => { RemoveServer(e.Server); }); } #endregion #region server_BuildQueueAdded() /// /// A build queue has been added to a server. /// /// /// private void server_BuildQueueAdded(object sender, BuildQueueChangedArgs e) { this.SafeInvoke(() => { AddBuildQueue(e.BuildQueue); }); } #endregion #region server_BuildQueueRemoved() /// /// A build queue has been removed from a server. /// /// /// private void server_BuildQueueRemoved(object sender, BuildQueueChangedArgs e) { this.SafeInvoke(() => { RemoveBuildQueue(e.BuildQueue); }); } #endregion #region queue_BuildQueueRequestAdded() /// /// A request has been added to a build queue. /// /// /// private void queue_BuildQueueRequestAdded(object sender, BuildQueueRequestChangedArgs e) { this.SafeInvoke(() => { AddBuildQueueRequest(e.BuildQueueRequest); }); } #endregion #region queue_BuildQueueRequestRemoved() /// /// A request has been removed from a build queue. /// /// /// private void queue_BuildQueueRequestRemoved(object sender, BuildQueueRequestChangedArgs e) { this.SafeInvoke(() => { if (requests.ContainsKey(e.BuildQueueRequest)) { requests[e.BuildQueueRequest].Remove(); } }); } #endregion #region exitMenu_Click() /// /// The user has clicked on the exit menu button. /// /// /// private void exitMenu_Click(object sender, EventArgs e) { Application.Exit(); } #endregion #region aboutMenu_Click() /// /// The user has clicked on the about menu button. /// /// /// private void aboutMenu_Click(object sender, EventArgs e) { var about = new AboutForm(); about.ShowDialog(this); } #endregion #region forceBuildButton_Click() /// /// The user has clicked on the force build button. /// /// /// private void forceBuildButton_Click(object sender, EventArgs e) { ForceBuild(); } #endregion #region abortBuildButton_Click() /// /// The user has clicked on the abort build button. /// /// /// private void abortBuildButton_Click(object sender, EventArgs e) { AbortBuild(); } #endregion #region startProjectButton_Click() /// /// The user has clicked on the start project button. /// /// /// private void startProjectButton_Click(object sender, EventArgs e) { StartProject(); } #endregion #region stopProjectButton_Click() /// /// The user has clicked on the stop project button. /// /// /// private void stopProjectButton_Click(object sender, EventArgs e) { StopProject(); } #endregion #region refreshButton_Click() /// /// The user has clicked on the refresh all button. /// /// /// private void refreshButton_Click(object sender, EventArgs e) { RefreshServers(); } #endregion #region forceBuildMenu_Click() /// /// The user has clicked on the force build menu button. /// /// /// private void forceBuildMenu_Click(object sender, EventArgs e) { ForceBuild(); } #endregion #region abortBuildMenu_Click() /// /// The user has clicked on the abort build menu button. /// /// /// private void abortBuildMenu_Click(object sender, EventArgs e) { AbortBuild(); } #endregion #region startProjectMenu_Click() /// /// The user has clicked on the start project menu button. /// /// /// private void startProjectMenu_Click(object sender, EventArgs e) { StartProject(); } #endregion #region stopProjectMenu_Click() /// /// The user has clicked on the stop project menu button. /// /// /// private void stopProjectMenu_Click(object sender, EventArgs e) { StopProject(); } #endregion #region settingsMenu_Click() /// /// The user has clicked on the settings menu button. /// /// /// private void settingsMenu_Click(object sender, EventArgs e) { ChangeSettings(); } #endregion #region refreshAllServersMenu_Click() /// /// The user has clicked on the refresh all servers menu button. /// /// /// private void refreshAllServersMenu_Click(object sender, EventArgs e) { RefreshServers(); } #endregion #region monitorExplorerDisplay_Click() /// /// The user has clicked on the show/hide monitor explorer menu button. /// /// /// private void monitorExplorerDisplay_Click(object sender, EventArgs e) { monitorExplorerDisplay.Checked = !monitorExplorerDisplay.Checked; monitorContainer.Panel1Collapsed = !monitorExplorerDisplay.Checked; } #endregion #region OnSelectionChanged() /// /// The user has changed between project list controls. /// /// /// private void OnSelectionChanged(object sender, EventArgs e) { selectedControl = sender; } #endregion #region groupByServerMenu_Click() /// /// The user has clicked on the show/hide server groups button. /// /// /// private void groupByServerMenu_Click(object sender, EventArgs e) { groupByServerMenu.Checked = !groupByServerMenu.Checked; allProjectsView.ShowGroups = groupByServerMenu.Checked; allQueuesList.ShowGroups = groupByServerMenu.Checked; allQueueRequestsList.ShowGroups = groupByServerMenu.Checked; } #endregion #region MainForm_FormClosing() /// /// This form is closing. /// /// /// private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { // First check if the form can be closed var canClose = true; if (e.CloseReason == CloseReason.UserClosing) { // Check each plug-in foreach (var plugin in this.plugins.Values) { if (plugin.CloseAction == HostCloseAction.Hide) { canClose = false; break; } } e.Cancel = !canClose; } if (canClose) { // Store the current positions configuration.Monitor.State = WindowState; if (WindowState == FormWindowState.Normal) { configuration.Monitor.Left = Left; configuration.Monitor.Top = Top; configuration.Monitor.Width = Width; configuration.Monitor.Height = Height; } // Save all the window positions configuration.ConfiguredWindows.Clear(); configuration.ConfiguredWindows.AddRange(webPages.ConfiguredWindows); configuration.ConfiguredWindows.AddRange(currentStatuses.ConfiguredWindows); configuration.ConfiguredWindows.AddRange(buildHistories.ConfiguredWindows); configuration.ConfiguredWindows.AddRange(packages.ConfiguredWindows); configuration.ConfiguredWindows.AddRange(properties.ConfiguredWindows); // Store the visibility options configuration.Monitor.ShowExplorer = monitorExplorerDisplay.Checked; configuration.Monitor.GroupByServer = groupByServerMenu.Checked; // Save the list views allProjectsView.SaveToConfig(configuration.Monitor.ListViews); ListViewHelper.SaveListView(allQueuesList, allQueuesSort, configuration.Monitor.ListViews); ListViewHelper.SaveListView(allQueueRequestsList, allQueueRequestsSort, configuration.Monitor.ListViews); // Terminate all the plugins foreach (var plugin in plugins.Values) { plugin.Terminate(); } } else { Hide(); } } #endregion #region allQueueRequestsList_ColumnClick() /// /// Sort by a column for the queue requests list view. /// /// /// private void allQueueRequestsList_ColumnClick(object sender, ColumnClickEventArgs e) { allQueueRequestsSort = ListViewHelper.SortListView(allQueueRequestsList, e.Column, allQueueRequestsSort); } #endregion #region allQueuesList_ColumnClick() /// /// Sort by a column for the queues list view. /// /// /// private void allQueuesList_ColumnClick(object sender, ColumnClickEventArgs e) { allQueuesSort = ListViewHelper.SortListView(allQueuesList, e.Column, allQueuesSort); } #endregion #region webpageMenu_Click() /// /// The user has clicked on the view web page menu button. /// /// /// private void webpageMenu_Click(object sender, EventArgs e) { ShowWebPage(); } #endregion #region packagesMenu_Click() /// /// The user has clicked on the package menu button. /// /// /// private void packagesMenu_Click(object sender, EventArgs e) { ShowPackages(); } #endregion #region currentStatusMenu_Click() /// /// The user has clicked on the current status menu button. /// /// /// private void currentStatusMenu_Click(object sender, EventArgs e) { ShowCurrentStatus(); } #endregion #region displayWebPageContextMenu_Click() /// /// The user has clicked on the display web page menu button in the context menu. /// /// /// private void displayWebPageContextMenu_Click(object sender, EventArgs e) { ShowWebPage(); } #endregion #region projectContextMenu_Opening() /// /// The context menu us being displayed. /// /// /// private void projectContextMenu_Opening(object sender, CancelEventArgs e) { var projects = GetSelectedProjects(); var canForce = false; var canAbort = false; var canStart = false; var canStop = false; foreach (var project in projects) { canForce |= project.Activity.IsSleeping(); canAbort |= !project.Activity.IsSleeping(); canStart |= project.Status == ProjectIntegratorState.Stopped; canStop |= project.Status == ProjectIntegratorState.Running; } forceBuildContextMenu.Visible = canForce; abortBuildContextMenu.Visible = canAbort; startprojectContextMenu.Visible = canStart; stopProjectContextMenu.Visible = canStop; } #endregion #region forceBuildContextMenu_Click() /// /// The user has clicked on force build in the context menu. /// /// /// private void forceBuildContextMenu_Click(object sender, EventArgs e) { ForceBuild(); } #endregion #region abortBuildContextMenu_Click() /// /// The user has clicked on abort build in the context menu. /// /// /// private void abortBuildContextMenu_Click(object sender, EventArgs e) { AbortBuild(); } #endregion #region startprojectContextMenu_Click() /// /// The user has clicked on start project in the context menu. /// /// /// private void startprojectContextMenu_Click(object sender, EventArgs e) { StartProject(); } #endregion #region stopProjectContextMenu_Click() /// /// The user has clicked on stop project in the context menu. /// /// /// private void stopProjectContextMenu_Click(object sender, EventArgs e) { StopProject(); } #endregion #region currentStatusContextMenu_Click() /// /// The user has clicked on current status in the context menu. /// /// /// private void currentStatusContextMenu_Click(object sender, EventArgs e) { ShowCurrentStatus(); } #endregion #region packagesContextMenu_Click() /// /// The user has clicked on packages in context menu. /// /// /// private void packagesContextMenu_Click(object sender, EventArgs e) { ShowPackages(); } #endregion #region viewWebPageButton_Click() /// /// The user has clicked on the view web page button. /// /// /// private void viewWebPageButton_Click(object sender, EventArgs e) { ShowWebPage(); } #endregion #region viewCurrentStatusButton_Click() /// /// The user has clicked on the current status button. /// /// /// private void viewCurrentStatusButton_Click(object sender, EventArgs e) { ShowCurrentStatus(); } #endregion #region viewPackagesButton_Click() /// /// The user has clicked on the packages button. /// /// /// private void viewPackagesButton_Click(object sender, EventArgs e) { ShowPackages(); } #endregion #region buildHistoryButton_Click() /// /// The user has clicked on the build history button. /// /// /// private void buildHistoryButton_Click(object sender, EventArgs e) { ShowBuildHistory(); } #endregion #region buildHistoryMenu_Click() /// /// The user has clicked on build history in the menu. /// /// /// private void buildHistoryMenu_Click(object sender, EventArgs e) { ShowBuildHistory(); } #endregion #region buildHistoryContextMenu_Click /// /// The user has clicked on build history in the context menu. /// /// /// private void buildHistoryContextMenu_Click(object sender, EventArgs e) { ShowBuildHistory(); } #endregion #region loginMenu_Click() /// /// The user has clicked on login in the menu. /// /// /// private void loginMenu_Click(object sender, EventArgs e) { Login(); } #endregion #region logoutMenu_Click() /// /// The user has clicked on logout in the menu. /// /// /// private void logoutMenu_Click(object sender, EventArgs e) { Logout(); } #endregion #region monitorExplorer_BeforeSelect() /// /// A node is being selected in the monitor explorer. /// /// /// private void monitorExplorer_BeforeSelect(object sender, TreeViewCancelEventArgs e) { var pluginNode = monitorExplorer.SelectedNode as PluginServerNode; if (pluginNode != null) { pluginNode.FireUnselected(); } } #endregion #region monitorExplorer_AfterSelect() /// /// A node has been selected from the monitor explorer. /// /// /// private void monitorExplorer_AfterSelect(object sender, TreeViewEventArgs e) { var pluginNode = e.Node as PluginServerNode; if (pluginNode != null) { pluginNode.FireSelected(); } } #endregion #region monitorExplorer_NodeMouseClick() /// /// Select the clicked node. /// /// /// private void monitorExplorer_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { if (e.Button == MouseButtons.Right) { monitorExplorer.SelectedNode = e.Node; explorerContextMenu.Show(monitorExplorer, e.X, e.Y); } } #endregion #region explorerContextMenu_Opening() /// /// The explorer menu is opening. /// /// /// private void explorerContextMenu_Opening(object sender, CancelEventArgs e) { explorerContextMenu.Items.Clear(); var currentNode = monitorExplorer.SelectedNode; if (currentNode is PluginServerNode) { e.Cancel = !(currentNode as PluginServerNode).ContextMenuOpened(explorerContextMenu); } else if (currentNode is ProjectTreeNode) { var project = (currentNode as ProjectTreeNode).Project; if (project.Activity.IsSleeping()) { explorerContextMenu.Items.Add("Force Build", Resources.play_green, forceBuildContextMenu_Click); } else { explorerContextMenu.Items.Add("Abort Build", Resources.stop_red, abortBuildContextMenu_Click); } if (project.Status == ProjectIntegratorState.Running) { explorerContextMenu.Items.Add("Stop Project", Resources.cog_stop, stopProjectContextMenu_Click); } else { explorerContextMenu.Items.Add("Start project", Resources.cog_start, startprojectContextMenu_Click); } explorerContextMenu.Items.Add(new ToolStripSeparator()); explorerContextMenu.Items.Add("P&roperties", Resources.magnifier, OnPropertiesClick); e.Cancel = false; } else if (currentNode is ServerTreeNode) { explorerContextMenu.Items.Add("P&roperties", Resources.magnifier, OnPropertiesClick); e.Cancel = false; } else if (currentNode is BuildQueueTreeNode) { explorerContextMenu.Items.Add("P&roperties", Resources.magnifier, OnPropertiesClick); e.Cancel = false; } else { e.Cancel = true; } } #endregion #region monitorTabs_SelectedIndexChanged() /// /// The select tab has changed. /// /// /// private void monitorTabs_SelectedIndexChanged(object sender, EventArgs e) { if (monitorTabs.SelectedTab is PluginTab) { (monitorTabs.SelectedTab as PluginTab).Activate(); } } #endregion #region errorReport_DoubleClick() /// /// The user has double clicked on the errors list. /// /// /// private void errorReport_DoubleClick(object sender, EventArgs e) { if (allErrors.Count > 0) { var lastError = (from record in allErrors orderby record.Key descending select record.Value).First(); MessageBox.Show(this, "The last error was " + lastError, "Last Error", MessageBoxButtons.OK, MessageBoxIcon.Information); } } #endregion #region OnPropertiesClick() /// /// A properties button or menu has been clicked. /// /// /// private void OnPropertiesClick(object sender, EventArgs e) { ShowProperties(); } #endregion #region allProjectsView_DoubleClick() /// /// The all projects grid has been double-clicked. /// /// The grid that fired the event. /// The arguments for the event. private void allProjectsView_DoubleClick(object sender, EventArgs e) { if (!string.IsNullOrEmpty(this.configuration.Monitor.AllProjectsDoubleClickAction)) { var projects = allProjectsView.GetSelectedProjects(); this.PerformProjectDoubleClick(projects); } } #endregion #endregion #region Private classes #region MainFormPluginHost /// /// Expose plugin host functionality for the main form. /// private class MainFormPluginHost : PluginHost { #region Private fields private MainForm form; #endregion #region Constructors /// /// Initialise a new . /// /// public MainFormPluginHost(MainForm form) { this.form = form; } #endregion #region Public methods #region Show() /// /// Show the host. /// public override void Show() { if (form.Visible) { if (form.WindowState == FormWindowState.Minimized) form.WindowState = FormWindowState.Normal; form.BringToFront(); } else { form.Show(); } } #endregion #region ShutDown() /// /// Shut down the host. /// public override void ShutDown() { Application.Exit(); } #endregion #region AddServerNode() /// /// Adds a new node for a server. /// /// The server the node is for. /// The new node. public override PluginServerNode AddServerNode(Server server) { var serverNode = form.servers[server]; var newNode = new PluginServerNode(); serverNode.Nodes.Add(newNode); return newNode; } #endregion #region AddTab() /// /// Adds a new tab page. /// /// The new tab. public override PluginTab AddTab(Plugin plugin, string text) { var key = string.Format("{0}:{1}", plugin.Configuration.Key, text); var newTab = new PluginTab(); newTab.Text = text; form.monitorTabs.TabPages.Add(newTab); newTab.Removed += (o, e) => { form.monitorTabs.TabPages.Remove(newTab); form.pluginTabs.Remove(key); }; form.pluginTabs.Add(key, newTab); return newTab; } #endregion #region AddImage() /// /// Adds a new image. /// /// The image to add. /// The identifier of the image. public override string AddImage(Image image) { var key = Guid.NewGuid().ToString(); form.applicationImages.Images.Add(key, image); return key; } #endregion #region ChangeSelectedControl() /// /// Change the currently selected control. /// /// public override void ChangeSelectedControl(Control control) { form.selectedControl = control; } #endregion #region ShowProjectContextMenu() /// /// Display the project context menu. /// /// /// /// public override void ShowProjectContextMenu(Control container, int x, int y) { form.projectContextMenu.Show(container, x, y); } #endregion #region AddCommand() /// /// Adds a new command. /// /// The text of the command. /// The icon of the command. /// The type of the command. /// The command to run. /// The new instance. public override PluginCommand AddCommand(string text, Image icon, PluginCommandType type, EventHandler command) { // Add the command to the main menu var menuCommand = new ToolStripMenuItem(text, icon, command); switch (type) { case PluginCommandType.Project: form.projectMenu.DropDownItems.Add(menuCommand); break; } // Add the command to the toolbar var toolButton = new ToolStripButton(text, icon, command); toolButton.DisplayStyle = ToolStripItemDisplayStyle.Image; form.mainToolbar.Items.Add(toolButton); // Add the command to the context menu var contextCommand = new ToolStripMenuItem(text, icon, command); form.projectContextMenu.Items.Add(contextCommand); // Generate the plug-in command var result = new PluginCommand(text, icon, type, command); result.Removed += (o, e) => { form.projectMenu.DropDownItems.Remove(menuCommand); form.mainToolbar.Items.Remove(toolButton); form.projectContextMenu.Items.Remove(contextCommand); }; result.PropertyChanged += (o, e) => { switch (e.PropertyName) { case "Icon": menuCommand.Image = result.Icon; toolButton.Image = result.Icon; contextCommand.Image = result.Icon; break; case "Text": menuCommand.Text = result.Text; toolButton.Text = result.Text; contextCommand.Text = result.Text; break; case "Tooltip": menuCommand.ToolTipText = result.Tooltip; toolButton.ToolTipText = result.Tooltip; contextCommand.ToolTipText = result.Tooltip; break; } }; // Return the command return result; } #endregion #region GetSelectedProjects() /// /// Gets the currently selected projects. /// /// The selected projects. public override IEnumerable GetSelectedProjects() { return form.GetSelectedProjects(); } #endregion #region PerformDefaultProjectAction() /// /// Performs the default project action on a project. /// /// The project to perform the action on. public override void PerformDefaultProjectAction(Project value) { var projects = new List(){ value }; form.PerformProjectDoubleClick(projects); } #endregion #endregion } #endregion #endregion } }