/* Copyright (c) Citrix Systems Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using XenAdmin.Actions; using XenAPI; using XenAdmin.Dialogs; namespace XenAdmin.Commands { /// /// An interface used to hide the SetSelection and SetMainWindow methods on the class. /// These methods are used only by the Command framework and are potentially confusing if seen by Command consumers. /// internal interface ICommand { void SetSelection(IEnumerable selection); void SetMainWindow(IMainWindow mainWindow); } /// /// A class which represents a user's task in XenCenter. Classes derived from can be easily /// assigned to menu items and toolbar buttons. /// [TypeConverter(typeof(CommandConverter))] internal abstract class Command : ICommand { private SelectedItemCollection _selection = new SelectedItemCollection(); private IMainWindow _mainWindow; private Control _parent; /// /// Initializes a new instance of this Command. The parameter-less constructor is required in the derived /// class if it is to be attached to a ToolStrip menu item or button. It should not be used in any other scenario. /// protected Command() { } /// /// Initializes a new instance of the class. /// /// The application main window. protected Command(IMainWindow mainWindow) { Util.ThrowIfParameterNull(mainWindow, "mainWindow"); _mainWindow = mainWindow; } /// /// Initializes a new instance of the class. /// /// The application main window. /// The selection context for the Command. protected Command(IMainWindow mainWindow, IEnumerable selection) : this(mainWindow) { Util.ThrowIfParameterNull(selection, "selection"); _selection = new SelectedItemCollection(selection); } /// /// Initializes a new instance of the class. /// /// The application main window. /// The selection context for the Command. protected Command(IMainWindow mainWindow, SelectedItem selection) : this(mainWindow, new [] { selection }) { } /// /// Initializes a new instance of the class. /// /// The application main window. /// The selection context for the Command. protected Command(IMainWindow mainWindow, IXenObject xenObject) : this(mainWindow, new SelectedItem(xenObject)) { } /// /// Gets a list of s from the specified s. /// protected static IEnumerable ConvertToSelection(IEnumerable xenObjects) where T : IXenObject { Util.ThrowIfParameterNull(xenObjects, "selection"); List selection = new List(); foreach (T xenObject in xenObjects) { selection.Add(new SelectedItem(xenObject)); } return selection; } /// /// Gets the current selection context for the Command. /// public SelectedItemCollection GetSelection() { return _selection; } /// /// Determines whether this instance can execute with the current selection context. /// /// /// true if this instance can execute; otherwise, false. /// public bool CanExecute() { return MainWindowCommandInterface != null && CanExecuteCore(GetSelection()); } /// /// Determines whether this instance can execute with the current selection context. /// /// The selection context. /// /// true if this instance can execute with the specified selection; otherwise, false. /// protected virtual bool CanExecuteCore(SelectedItemCollection selection) { return true; } /// /// Executes this Command on the current selection. /// public void Execute() { if (Confirm()) { CommandErrorDialog errorDialog = GetErrorDialog(); ExecuteCore(GetSelection()); if (errorDialog != null) { errorDialog.ShowDialog(Parent); } } } /// /// Executes this Command. /// /// The selection the Command should operate on. protected virtual void ExecuteCore(SelectedItemCollection selection) { } /// /// Gets the text for a menu item which launches this Command. /// public virtual string MenuText { get { return null; } set { throw new NotImplementedException(); } } /// /// Gets the text for a context menu item which launches this Command. /// public virtual string ContextMenuText { get { return null; } } /// /// Gets the image for a menu item which launches this Command. /// public virtual Image MenuImage { get { return null; } } /// /// Gets the image for a context menu item which launches this Command. /// public virtual Image ContextMenuImage { get { return null; } } /// /// Gets the text for the toolbar button which launches this Command. /// public virtual string ToolBarText { get { return null; } } /// /// Gets the image for a toolbar button which launches this Command. /// public virtual Image ToolBarImage { get { return null; } } /// /// Gets the text for a button which launches this Command. /// public virtual string ButtonText { get { return null; } } /// /// Gets the tool tip text. By default this is the can't execute reason if execution is not possible and /// blank if it can. Override EnabledToolTipText to provide a descriptive tooltip when the command is enabled. /// public virtual string ToolTipText { get { if (CanExecute()) return EnabledToolTipText; return DisabledToolTipText; } } /// /// Gets the tool tip text when the command is not able to run. CantExectuteReason for single items, /// null for multiple. /// protected virtual string DisabledToolTipText { get { Dictionary reasons = GetCantExecuteReasons(); // It's necessary to double check that we have one reason which matches up with a single selection // as CanExecuteCore and GetCantExecuteReasons aren't required to match up. if (reasons.Count == 1 && GetSelection().Count == 1) { foreach (string s in reasons.Values) { if (s.Equals(Messages.UNKNOWN)) { //This is the default, and not a useful tooltip return null; } return s; } } return null; } } /// /// Gets the tool tip text when the command is able to run. Null by default. /// protected virtual string EnabledToolTipText { get { return null; } } /// /// Gets the shortcut key display string. This is only used if this Command is used on the main menu. /// public virtual string ShortcutKeyDisplayString { get { return null; } } /// /// Gets the shortcut keys. This is only used if this Command is used on the main menu. /// public virtual Keys ShortcutKeys { get { return Keys.None; } } /// /// Gets a value indicating whether a confirmation dialog should be shown. /// protected virtual bool ConfirmationRequired { get { return false; } } /// /// Gets the confirmation dialog title. The default for this is Messages.MESSAGEBOX_CONFIRM. /// protected virtual string ConfirmationDialogTitle { get { return null; } } /// /// Gets the confirmation dialog text. /// protected virtual string ConfirmationDialogText { get { return null; } } /// /// Gets the help id for the confirmatin dialog. /// protected virtual string ConfirmationDialogHelpId { get { return null; } } /// /// Gets the confirmation dialog's "Yes" button label. /// protected virtual string ConfirmationDialogYesButtonLabel { get { return null; } } /// /// Gets the confirmation dialog's "No" button label. /// protected virtual string ConfirmationDialogNoButtonLabel { get { return null; } } /// /// Gets a value indicating whether the "No" button should be selected when a confirmation dialog is displayed. /// protected virtual bool ConfirmationDialogNoButtonSelected { get { return false; } } /// /// Shows a confirmation dialog. /// /// true if the user clicked Yes. protected bool ShowConfirmationDialog() { ThreeButtonDialog.TBDButton buttonYes = ThreeButtonDialog.ButtonYes; if (!string.IsNullOrEmpty(ConfirmationDialogYesButtonLabel)) buttonYes.label = ConfirmationDialogYesButtonLabel; ThreeButtonDialog.TBDButton buttonNo = ThreeButtonDialog.ButtonNo; if (!string.IsNullOrEmpty(ConfirmationDialogNoButtonLabel)) buttonNo.label = ConfirmationDialogNoButtonLabel; if (ConfirmationDialogNoButtonSelected) buttonNo.selected = true; return MainWindow.Confirm(null, Parent, ConfirmationDialogTitle ?? Messages.XENCENTER, ConfirmationDialogHelpId, buttonYes, buttonNo, ConfirmationDialogText); } /// /// Shows a confirmation dialog if required. /// /// True if the operation should proceed. protected virtual bool Confirm() { return !ConfirmationRequired || ShowConfirmationDialog(); } /// /// Gets all of the reasons that items in the selection can't execute. /// /// A dictionary of reasons keyed by the item name. public Dictionary GetCantExecuteReasons() { Dictionary cantExecuteReasons = new Dictionary(); foreach (SelectedItem item in GetSelection()) { if (item == null || item.XenObject == null) continue; if (MainWindowCommandInterface != null && CanExecuteCore(new SelectedItemCollection(item))) continue; string reason = GetCantExecuteReasonCore(item); if (reason != null) cantExecuteReasons.Add(item, reason); } return cantExecuteReasons; } /// /// Gets the reason that the specified item from the selection cant execute. This is displayed in the error dialog. /// The default is "Unknown". /// protected virtual string GetCantExecuteReasonCore(SelectedItem item) { return Messages.UNKNOWN; } private CommandErrorDialog GetErrorDialog() { Dictionary cantExecuteReasons = GetCantExecuteReasons(); if (cantExecuteReasons.Count > 0) { return GetErrorDialogCore(cantExecuteReasons); } return null; } /// /// Gets the error dialog to be displayed if one or more items in the selection couldn't be executed. Returns null by /// default i.e. An error dialog isn't displayed by default. /// /// The reasons for why the items couldn't execute. protected virtual CommandErrorDialog GetErrorDialogCore(IDictionary cantExecuteReasons) { return null; } /// /// Gets the main window to be used by the Command. /// public IMainWindow MainWindowCommandInterface { get { return _mainWindow; } } /// /// Sets the parent for any dialogs. If not called, then the main window is used. /// /// The parent. public void SetParent(Control parent) { _parent = parent; } /// /// Gets the parent for any dialogs. If SetParent() hasn't been called then the MainWindow is returned. /// public Control Parent { get { return _parent ?? _mainWindow.Form; } } /// /// Runs the specified s such that they are synchronous per connection but asynchronous across connections. /// /// Whether or not the actions should be executed symultaneously public void RunMultipleActions(IEnumerable actions, string title, string startDescription, string endDescription, bool runActionsInParallel) { MultipleActionLauncher launcher = new MultipleActionLauncher(actions, title, startDescription, endDescription, runActionsInParallel); launcher.Run(); } #region ICommand Members /// /// Sets the selection context for the Command. This is hidden as it is only for use by the Command framework. /// void ICommand.SetSelection(IEnumerable selection) { Util.ThrowIfParameterNull(selection, "selection"); _selection = new SelectedItemCollection(selection); } /// /// Sets the main window for the Command. This is hidden as it is only for use by the Command framework. /// /// The main window. void ICommand.SetMainWindow(IMainWindow mainWindow) { Util.ThrowIfParameterNull(mainWindow, "mainWindow"); _mainWindow = mainWindow; } #endregion } }