diff --git a/XenAdmin/Controls/MainWindowControls/NavigationPane.cs b/XenAdmin/Controls/MainWindowControls/NavigationPane.cs index 25cb7b5fe..367332b9c 100644 --- a/XenAdmin/Controls/MainWindowControls/NavigationPane.cs +++ b/XenAdmin/Controls/MainWindowControls/NavigationPane.cs @@ -53,6 +53,7 @@ namespace XenAdmin.Controls.MainWindowControls } private NavigationMode currentMode; + private NotificationsSubMode lastNotificationsMode = NotificationsSubMode.Alerts; #region Events @@ -157,10 +158,10 @@ namespace XenAdmin.Controls.MainWindowControls #endregion - public void UpdateNotificationsButton() + public void UpdateNotificationsButton(NotificationsSubMode mode, int entries) { - buttonNotifyBig.Text = string.Format("Notifications ({0})", Alert.NonDismissingAlertCount); - notificationsView.UpdateEntries(NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount); + notificationsView.UpdateEntries(mode, entries); + buttonNotifyBig.Text = string.Format("Notifications ({0})", notificationsView.GetTotalEntries()); } public void XenConnectionCollectionChanged(CollectionChangeEventArgs e) @@ -203,12 +204,20 @@ namespace XenAdmin.Controls.MainWindowControls navigationView.MajorChange(() => navigationView.SaveAndRestoreTreeViewFocus(f)); } - public void SwitchToNotificationsView(NotificationsSubMode subMode) + public void SwitchToInfrastructureMode() + { + if (!buttonInfraBig.Checked) + buttonInfraBig.Checked = true; + } + + private void SwitchToNotificationsView(NotificationsSubMode subMode) { //check the button if switching has been requested programmatically if (!buttonNotifyBig.Checked) buttonNotifyBig.Checked = true; + //show the notificationsView first and then hide the navigationView + //to avoid instantaneous appearance of empty panels notificationsView.Visible = true; notificationsView.SelectNotificationsSubMode(subMode); navigationView.Visible = false; @@ -263,19 +272,24 @@ namespace XenAdmin.Controls.MainWindowControls { if (currentMode == NavigationMode.Notifications) { - SwitchToNotificationsView(NotificationsSubMode.Alerts); + //restore the last selected view + SwitchToNotificationsView(lastNotificationsMode); } else { - notificationsView.Visible = false; + //show the navigationView first and then hide the notificationsView + //to avoid instantaneous appearance of empty panels navigationView.Visible = true; + notificationsView.Visible = false; navigationView.CurrentSearch = Search; navigationView.NavigationMode = currentMode; navigationView.ResetSeachBox(); navigationView.RequestRefreshTreeView(); navigationView.FocusTreeView(); - navigationView.SelectObject(null, false); + + if (navigationView.SelectionManager.Selection.Count < 1) + navigationView.SelectObject(null, false); } if (NavigationModeChanged != null) @@ -371,6 +385,8 @@ namespace XenAdmin.Controls.MainWindowControls private void notificationsView_NotificationsSubModeChanged(NotificationsSubModeItem subModeItem) { + lastNotificationsMode = subModeItem.SubMode; + if (NotificationsSubModeChanged != null) NotificationsSubModeChanged(subModeItem); } diff --git a/XenAdmin/Controls/MainWindowControls/NotificationsView.cs b/XenAdmin/Controls/MainWindowControls/NotificationsView.cs index 27f44c866..06a93a571 100644 --- a/XenAdmin/Controls/MainWindowControls/NotificationsView.cs +++ b/XenAdmin/Controls/MainWindowControls/NotificationsView.cs @@ -54,6 +54,19 @@ namespace XenAdmin.Controls.MainWindowControls Items.Add(new NotificationsSubModeItem(NotificationsSubMode.Events)); } + public int GetTotalEntries() + { + int total = 0; + foreach (var item in Items) + { + var subModeItem = item as NotificationsSubModeItem; + + if (subModeItem != null) + total += subModeItem.UnreadEntries; + } + return total; + } + public void UpdateEntries(NotificationsSubMode subMode, int entries) { foreach (var item in Items) @@ -78,7 +91,12 @@ namespace XenAdmin.Controls.MainWindowControls if (subModeItem != null && subModeItem.SubMode == subMode) { + var lastSelected = SelectedItem; SelectedItem = item; + + if (lastSelected == SelectedItem) + OnSelectedIndexChanged(EventArgs.Empty); + break; } } @@ -150,45 +168,52 @@ namespace XenAdmin.Controls.MainWindowControls public Image Image { - get - { - switch (SubMode) - { - case NotificationsSubMode.Alerts: - return Properties.Resources._000_Alert2_h32bit_16; - case NotificationsSubMode.Updates: - return Properties.Resources.tempUpdates; - case NotificationsSubMode.Events: - return UnreadEntries == 0 - ? Properties.Resources._000_date_h32bit_16 - : Properties.Resources.tempErrorEvents; - default: - return null; - } - } + get { return GetImage(SubMode, UnreadEntries); } } public string Text { - get + get { return GetText(SubMode, UnreadEntries); } + } + + public static Image GetImage(NotificationsSubMode submode, int unreadEntries) + { + switch (submode) { - switch (SubMode) - { - case NotificationsSubMode.Alerts: - return UnreadEntries == 0 - ? Messages.NOTIFICATIONS_SUBMODE_ALERTS_READ - : string.Format(Messages.NOTIFICATIONS_SUBMODE_ALERTS_UNREAD, UnreadEntries); - case NotificationsSubMode.Updates: - return UnreadEntries == 0 - ? Messages.NOTIFICATIONS_SUBMODE_UPDATES_READ - : string.Format(Messages.NOTIFICATIONS_SUBMODE_UPDATES_UNREAD, UnreadEntries); - case NotificationsSubMode.Events: - return UnreadEntries == 0 - ? Messages.NOTIFICATIONS_SUBMODE_EVENTS_READ - : string.Format(Messages.NOTIFICATIONS_SUBMODE_EVENTS_UNREAD, UnreadEntries); - default: - return ""; - } + case NotificationsSubMode.Alerts: + return Properties.Resources._000_Alert2_h32bit_16; + case NotificationsSubMode.Updates: + return Properties.Resources.tempUpdates; + case NotificationsSubMode.Events: + return unreadEntries == 0 + ? Properties.Resources._000_date_h32bit_16 + : Properties.Resources.tempErrorEvents; + default: + return null; + } + } + + public static string GetText(NotificationsSubMode submode, int unreadEntries) + { + switch (submode) + { + case NotificationsSubMode.Alerts: + return unreadEntries == 0 + ? Messages.NOTIFICATIONS_SUBMODE_ALERTS_READ + : string.Format(Messages.NOTIFICATIONS_SUBMODE_ALERTS_UNREAD, unreadEntries); + case NotificationsSubMode.Updates: + return unreadEntries == 0 + ? Messages.NOTIFICATIONS_SUBMODE_UPDATES_READ + : string.Format(Messages.NOTIFICATIONS_SUBMODE_UPDATES_UNREAD, unreadEntries); + case NotificationsSubMode.Events: + if (unreadEntries == 0) + return Messages.NOTIFICATIONS_SUBMODE_EVENTS_READ; + else if (unreadEntries == 1) + return Messages.NOTIFICATIONS_SUBMODE_EVENTS_UNREAD_ONE; + else + return string.Format(Messages.NOTIFICATIONS_SUBMODE_EVENTS_UNREAD_MANY, unreadEntries); + default: + return ""; } } } diff --git a/XenAdmin/MainWindow.cs b/XenAdmin/MainWindow.cs index 50e87b520..dbc452f1d 100644 --- a/XenAdmin/MainWindow.cs +++ b/XenAdmin/MainWindow.cs @@ -190,6 +190,8 @@ namespace XenAdmin pluginManager.LoadPlugins(); contextMenuBuilder = new ContextMenuBuilder(pluginManager, commandInterface); + updatesPage.UpdatesCollectionChanged += updatesPage_UpdatesCollectionChanged; + eventsPage.GoToXenObjectRequested += eventsPage_GoToXenObjectRequested; SearchPage.SearchChanged += SearchPanel_SearchChanged; Alert.XenCenterAlerts.CollectionChanged += XenCenterAlerts_CollectionChanged; @@ -365,26 +367,48 @@ namespace XenAdmin return; Program.BeginInvoke(Program.MainWindow, () => - { - ActionBase action = (ActionBase)e.Element; - if (action == null) - return; + { + ActionBase action = (ActionBase)e.Element; + if (action == null) + return; - var meddlingAction = action as MeddlingAction; - if (meddlingAction == null) - { - SetStatusBar(null, null); - if (statusBarAction != null) - { - statusBarAction.Changed -= actionChanged; - statusBarAction.Completed -= actionChanged; - } - statusBarAction = action; - } - action.Changed += actionChanged; - action.Completed += actionChanged; - actionChanged(action); - }); + switch (e.Action) + { + case CollectionChangeAction.Add: + { + var meddlingAction = action as MeddlingAction; + if (meddlingAction == null) + { + SetStatusBar(null, null); + if (statusBarAction != null) + { + statusBarAction.Changed -= actionChanged; + statusBarAction.Completed -= actionChanged; + } + statusBarAction = action; + } + action.Changed += actionChanged; + action.Completed += actionChanged; + actionChanged(action); + break; + } + case CollectionChangeAction.Remove: + { + action.Changed -= actionChanged; + action.Completed -= actionChanged; + + int errors = ConnectionsManager.History.Count(a => a.IsCompleted && !a.Succeeded); + navigationPane.UpdateNotificationsButton(NotificationsSubMode.Events, errors); + + if (eventsPage.Visible) + { + TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Events, errors); + TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Events, errors); + } + break; + } + } + }); } void actionChanged(ActionBase action) @@ -435,6 +459,15 @@ namespace XenAdmin !string.IsNullOrEmpty(action.Title) ? action.Title : null); } + + int errors = ConnectionsManager.History.Count(a => a.IsCompleted && !a.Succeeded); + navigationPane.UpdateNotificationsButton(NotificationsSubMode.Events, errors); + + if (eventsPage.Visible) + { + TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Events, errors); + TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Events, errors); + } } public void SetStatusBar(Image image, string message) @@ -2362,6 +2395,23 @@ namespace XenAdmin return navigationPane.SelectObject(o); } + private void eventsPage_GoToXenObjectRequested(IXenObject obj) + { + navigationPane.SwitchToInfrastructureMode(); + navigationPane.SelectObject(obj); + } + + private void updatesPage_UpdatesCollectionChanged(int updatesCount) + { + navigationPane.UpdateNotificationsButton(NotificationsSubMode.Updates, updatesCount); + + if (updatesPage.Visible) + { + TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Updates, updatesCount); + TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Updates, updatesCount); + } + } + private void CloseWhenActionsCanceled(object o) { int i = 0; @@ -2608,10 +2658,10 @@ namespace XenAdmin private void navigationPane_NotificationsSubModeChanged(NotificationsSubModeItem submodeItem) { - TheTabControl.Visible = false; alertPage.Visible = submodeItem.SubMode == NotificationsSubMode.Alerts; updatesPage.Visible = submodeItem.SubMode == NotificationsSubMode.Updates; eventsPage.Visible = submodeItem.SubMode == NotificationsSubMode.Events; + TheTabControl.Visible = false; if (alertPage.Visible) alertPage.RefreshAlertList(); @@ -2694,7 +2744,17 @@ namespace XenAdmin void XenCenterAlerts_CollectionChanged(object sender, CollectionChangeEventArgs e) { - Program.BeginInvoke(Program.MainWindow, navigationPane.UpdateNotificationsButton); + Program.BeginInvoke(Program.MainWindow, () => + { + navigationPane.UpdateNotificationsButton( + NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount); + + if (alertPage.Visible) + { + TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount); + TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount); + } + }); } private void backButton_Click(object sender, EventArgs e) diff --git a/XenAdmin/TabPages/AlertSummaryPage.cs b/XenAdmin/TabPages/AlertSummaryPage.cs index 4284b1deb..15604deda 100644 --- a/XenAdmin/TabPages/AlertSummaryPage.cs +++ b/XenAdmin/TabPages/AlertSummaryPage.cs @@ -449,11 +449,14 @@ namespace XenAdmin.TabPages private void ToolStripMenuItemDismiss_Click(object sender, EventArgs e) { - if (new ThreeButtonDialog( + using (var dlog = new ThreeButtonDialog( new ThreeButtonDialog.Details(null, Messages.ALERT_DISMISS_CONFIRM, Messages.XENCENTER), ThreeButtonDialog.ButtonYes, - ThreeButtonDialog.ButtonNo).ShowDialog(this) != DialogResult.Yes) - return; + ThreeButtonDialog.ButtonNo)) + { + if (dlog.ShowDialog(this) != DialogResult.Yes) + return; + } Alert[] selectedAlerts = new Alert[GridViewAlerts.SelectedRows.Count]; for (int i = 0; i < selectedAlerts.Length; i++) diff --git a/XenAdmin/TabPages/HistoryPage.cs b/XenAdmin/TabPages/HistoryPage.cs index 9e3f680ee..4807fab11 100644 --- a/XenAdmin/TabPages/HistoryPage.cs +++ b/XenAdmin/TabPages/HistoryPage.cs @@ -51,6 +51,8 @@ namespace XenAdmin.TabPages private const int MAX_HISTORY_ITEM = 1000; + internal event Action GoToXenObjectRequested; + public HistoryPage() { InitializeComponent(); @@ -292,7 +294,8 @@ namespace XenAdmin.TabPages private void row_GoToXenObjectRequested(IXenObject obj) { - throw new NotImplementedException(); + if (GoToXenObjectRequested != null) + GoToXenObjectRequested(obj); } @@ -338,7 +341,25 @@ namespace XenAdmin.TabPages private void tsmiDismissSelected_Click(object sender, EventArgs e) { + using (var dlog = new ThreeButtonDialog( + new ThreeButtonDialog.Details(null, Messages.MESSAGEBOX_LOGS_DELETE_SELECTED, Messages.MESSAGEBOX_LOGS_DELETE_TITLE), + ThreeButtonDialog.ButtonYes, + ThreeButtonDialog.ButtonNo)) + { + if (dlog.ShowDialog(this) != DialogResult.Yes) + return; + } + + var actions = new List(); + foreach (var row in dataGridView.SelectedRows) + { + var actionRow = row as DataGridViewActionRow; + if (actionRow != null) + actions.Add(actionRow.Action); + } + + ConnectionsManager.History.RemoveAll(actions.Contains); } diff --git a/XenAdmin/TabPages/ManageUpdatesPage.cs b/XenAdmin/TabPages/ManageUpdatesPage.cs index ad80d85f1..a6e3f0f8d 100644 --- a/XenAdmin/TabPages/ManageUpdatesPage.cs +++ b/XenAdmin/TabPages/ManageUpdatesPage.cs @@ -66,6 +66,8 @@ namespace XenAdmin.TabPages Dictionary expandedState = new Dictionary(); + public event Action UpdatesCollectionChanged; + private void CheckForUpdates_CheckForUpdatesCompleted(object sender, CheckForUpdatesCompletedEventArgs e) { Program.Invoke(this, delegate @@ -74,7 +76,12 @@ namespace XenAdmin.TabPages ShowProgress(false); checkForUpdatesSucceeded = e.Succeeded; if (checkForUpdatesSucceeded) + { Rebuild(); + + if (UpdatesCollectionChanged != null) + UpdatesCollectionChanged(updateAlerts.Count); + } else { pictureBox.Image = SystemIcons.Error.ToBitmap(); diff --git a/XenAdminTests/TabsAndMenus/TabsAndMenus.cs b/XenAdminTests/TabsAndMenus/TabsAndMenus.cs index 9f3bd3457..7717b6c2a 100644 --- a/XenAdminTests/TabsAndMenus/TabsAndMenus.cs +++ b/XenAdminTests/TabsAndMenus/TabsAndMenus.cs @@ -78,17 +78,12 @@ namespace XenAdminTests.TabsAndMenus protected void VerifyContextMenu(object ixmo, ExpectedMenuItem[] expectedItems) { string ixmoString = (ixmo == null ? "XenCenter node" : ixmo.ToString()); - - // The easiest way to generate the context menu is to call TreeView_NodeMouseClick_ ourselves. - // So construct fake click event data. Assert.IsTrue(SelectInTree(ixmo), "Couldn't find a node for " + ixmoString + " in the tree"); - - - // Generate the TreeContextMenu MW(() => { var tree = TestUtils.GetFlickerFreeTreeView(MainWindowWrapper.Item, "navigationPane.navigationView.treeView"); + // Generate the TreeContextMenu var e = new VirtualTreeNodeMouseClickEventArgs(tree.SelectedNode, MouseButtons.Right, 1, 0, 0); var view = TestUtils.GetNavigationView(MainWindowWrapper.Item, "navigationPane.navigationView"); TestUtils.ExecuteMethod(view, "HandleNodeRightClick", new object[] { e }); diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index 01b8fa123..ba9f579a7 100644 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -19666,7 +19666,7 @@ namespace XenAdmin { } /// - /// Looks up a localized string similar to Clear log entry. + /// Looks up a localized string similar to Clear entry. /// public static string MESSAGEBOX_LOG_DELETE_TITLE { get { @@ -19692,6 +19692,15 @@ namespace XenAdmin { } } + /// + /// Looks up a localized string similar to Do you wish to delete the selected entries?. + /// + public static string MESSAGEBOX_LOGS_DELETE_SELECTED { + get { + return ResourceManager.GetString("MESSAGEBOX_LOGS_DELETE_SELECTED", resourceCulture); + } + } + /// /// Looks up a localized string similar to Clear entries. /// @@ -23012,11 +23021,20 @@ namespace XenAdmin { } /// - /// Looks up a localized string similar to Events ({0}). + /// Looks up a localized string similar to Events ({0} errors). /// - public static string NOTIFICATIONS_SUBMODE_EVENTS_UNREAD { + public static string NOTIFICATIONS_SUBMODE_EVENTS_UNREAD_MANY { get { - return ResourceManager.GetString("NOTIFICATIONS_SUBMODE_EVENTS_UNREAD", resourceCulture); + return ResourceManager.GetString("NOTIFICATIONS_SUBMODE_EVENTS_UNREAD_MANY", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Events (1 error). + /// + public static string NOTIFICATIONS_SUBMODE_EVENTS_UNREAD_ONE { + get { + return ResourceManager.GetString("NOTIFICATIONS_SUBMODE_EVENTS_UNREAD_ONE", resourceCulture); } } diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index 66a9790e5..2603a0987 100644 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -6856,6 +6856,9 @@ This action is final and unrecoverable. This will clear all non-active entries in the current view. Do you wish to continue? + + Do you wish to delete the selected entries? + Clear entries @@ -6863,7 +6866,7 @@ This action is final and unrecoverable. Are you sure you want to delete this entry? - Clear log entry + Clear entry Moving Virtual Disk @@ -7943,8 +7946,11 @@ It is strongly recommended that you Cancel and apply the latest version of the p Events - - Events ({0}) + + Events ({0} errors) + + + Events (1 error) Updates @@ -10936,9 +10942,9 @@ To learn more about the XenServer Dynamic Workload Balancing feature or to start XenCenter version {0} (build {1}) {2}-bit - - - {0} vGPU ({1} per GPU, {2}, {3} displays) + + + {0} vGPU ({1} per GPU, {2}, {3} displays) Pass-through whole GPU diff --git a/mk/tests-checks.sh b/mk/tests-checks.sh index 5a5745491..09fecf69f 100644 --- a/mk/tests-checks.sh +++ b/mk/tests-checks.sh @@ -47,7 +47,7 @@ cp ${OUTPUT_DIR}/XenAdminTests.tgz ${TEST_DIR} cd ${TEST_DIR} && tar xzf XenAdminTests.tgz && chmod -R 777 Release set +e -"/cygdrive/c/Program Files/NUnit 2.5.10/bin/net-2.0/nunit-console.exe" /process=separate /noshadow /err="C:\cygwin\tmp\error.nunit.log" /timeout=40000 /output="C:\cygwin\tmp\output.nunit.log" /xml="C:\cygwin\tmp\XenAdminTests.xml" "C:\cygwin\tmp\Release\XenAdminTests.dll" & +"/cygdrive/c/Program Files/NUnit 2.5.10/bin/net-2.0/nunit-console.exe" /process=separate /noshadow /labels /err="C:\cygwin\tmp\error.nunit.log" /timeout=40000 /output="C:\cygwin\tmp\output.nunit.log" /xml="C:\cygwin\tmp\XenAdminTests.xml" "C:\cygwin\tmp\Release\XenAdminTests.dll" & pid=$! (sleep 2000 ; kill $pid 2>/dev/null ) & sleeperpid=$!