using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Inet.Viewer.Data; using Inet.Viewer.Resources; namespace Inet.Viewer.WinForms.Prompt { /// /// DialogForm class for displaying the prompt dialog responsible for requesting any parameters for a given report. /// public partial class PromptDialogForm : Form, ValidationDelegate { private PromptPanelBuilder builder; private List prompts,hiddenPrompts; private int previousListIndex = -1; private bool changingIndex; private bool showMessageDialogs; private List allNodes; private bool firstTime; private Action, Boolean> callback; private string refreshingCascadingPrompt; private Dictionary lastValueMap = new Dictionary(); private bool validateNextSelection; /// /// Constructor with the necessary dependencies /// /// prompts which are to be shown to the user for him to fill out /// builder class for creating panel components per prompt public PromptDialogForm(List prompts, PromptPanelBuilder builder) : this(prompts, builder, null, true) { } /// /// Constructor with the necessary dependencies /// /// prompts which are to be shown to the user for him to fill out /// builder class for creating panel components per prompt /// callback for returning the chosen values public PromptDialogForm(List prompts, PromptPanelBuilder builder, Action, Boolean> callback) : this(prompts, builder, callback, true) { } /// /// Constructor with the necessary dependencies /// /// prompts which are to be shown to the user for him to fill out /// builder class for creating panel components per prompt /// whether to show error messages in a popup public PromptDialogForm(List prompts, PromptPanelBuilder builder, bool showMessageDialogs) : this(prompts, builder, null, showMessageDialogs) { } /// /// Constructor with all necessary dependencies (called by the other constructors) /// /// list of the prompts /// factory of the necessary prompt panels /// for returning the chosen prompt values /// whether to show error messages in a popup internal PromptDialogForm(List prompts, PromptPanelBuilder builder, Action, Boolean> callback, bool showMessageDialogs) { prompts.RemoveAll(p => p.DisplayName == null); this.callback = callback; this.showMessageDialogs = showMessageDialogs; this.builder = builder; this.previousListIndex = 0; this.firstTime = true; InitializeComponent(); Prompts = prompts; btnLeft.Enabled = false; bool hasMorePrompts = this.AllNodes.Count > 1; this.btnRight.Enabled = hasMorePrompts; AcceptButton = hasMorePrompts ? btnRight : btnOK; this.promptTree.SelectedNode = this.AllNodes[0]; ShowPrompt(prompts[0]); panel.Select(); } /// /// the image icon index for the given prompt type /// /// prompt type we need the icon index for /// the index of the icon private static int ImageIndexFor(int promptType) { if (promptType == PromptData.Datetime) { return 6; } else if (promptType == PromptData.Unknown) { return 7; } else { return promptType - 6; } } /// /// called when the OK button is clicked /// /// ok button responsible for the action /// eventargs of the action private void btnOK_Click(object sender, EventArgs e) { if (UpdateValueFromUI() && ValidateAllNodes()) { DialogResult = DialogResult.OK; if (callback != null) { List allPrompts = new List(); allPrompts.AddRange(prompts); allPrompts.AddRange(hiddenPrompts.Where(p=>p.Name != null)); callback(allPrompts, false); } } else if (this.showMessageDialogs) { string errMsg = CurrentPromptControl.ErrorMessage; MessageBox.Show(strings.Prompt_Error_InvalidValue+(errMsg==null?"":"\n\n"+errMsg)); } } /// /// called when the cancel button is clicked /// /// cancel button responsible for the action /// eventargs of the action private void btnCancel_Click(object sender, EventArgs e) { DialogResult = DialogResult.Cancel; } /// /// called when the right arrow button (next prompt) is clicked /// /// button responsible for the action /// eventargs of the action private void btnRight_Click(object sender, EventArgs e) { this.promptTree.SelectedNode = this.AllNodes[this.AllNodes.IndexOf(promptTree.SelectedNode) + 1]; //this.promptTree.Focus(); } /// /// called when the left arrow button (previous prompt) is clicked /// /// button responsible for the action /// eventargs of the action private void btnLeft_Click(object sender, EventArgs e) { this.promptTree.SelectedNode = this.AllNodes[this.AllNodes.IndexOf(promptTree.SelectedNode) - 1]; //this.promptTree.Focus(); } /// /// called when a different prompt is chosen in the prompt tree /// /// object responsible for this event /// eventargs of the action private void promptTree_AfterSelect(object sender, EventArgs e) { if (this.changingIndex || this.firstTime) { this.firstTime = false; return; } int selectedIndex = AllNodes.IndexOf(promptTree.SelectedNode); if (!UpdateValueFromUI()) { this.changingIndex = true; try { this.promptTree.SelectedNode = this.AllNodes[this.previousListIndex]; string errMsg = CurrentPromptControl.ErrorMessage; if (!showMessageDialogs || new ValidationFailureDialog(errMsg).ShowDialog() == System.Windows.Forms.DialogResult.OK) { return; } this.promptTree.SelectedNode = this.AllNodes[selectedIndex]; validateNextSelection = true; } finally { this.changingIndex = false; } } this.btnLeft.Enabled = this.AllNodes.IndexOf(promptTree.SelectedNode) > 0; bool hasMorePrompts = this.AllNodes.IndexOf(promptTree.SelectedNode) < this.AllNodes.Count - 1; this.btnRight.Enabled = hasMorePrompts; AcceptButton = hasMorePrompts ? btnRight : btnOK; ShowPrompt(prompts[this.AllNodes.IndexOf(promptTree.SelectedNode)]); this.previousListIndex = this.AllNodes.IndexOf(promptTree.SelectedNode); if (validateNextSelection) { CurrentPromptControl.ShowError = true; CurrentPromptControl.ValidatePrompt(); validateNextSelection = false; } } /// /// Shows a prompt on the right side of the split container. /// /// the prompt to show private void ShowPrompt(PromptData promptData) { Control control = builder.CreateControlFor(promptData, this); control.Dock = DockStyle.Fill; this.panel.Controls.Clear(); this.panel.Controls.Add(control); this.labelCaption.Text = promptData.Name; if (promptData.Description == null || promptData.Description.Length == 0) { labelDescription.Visible = false; } else { labelDescription.Visible = true; labelDescription.Text = promptData.Description; } } /// /// Validates the input of the currently visible UI control and updates the prompt data value /// according to the input. If validation fails an error message is shown and updating does not /// take place. /// /// true if validation succeeds private bool UpdateValueFromUI() { if (previousListIndex == -1 || panel == null || panel.Controls.Count == 0) { return true; } PromptControl promptControl = CurrentPromptControl; if (!promptControl.ValidatePrompt()) { return false; } PromptData promptData = this.prompts[this.previousListIndex]; PromptValue newValue,oldValue; try { newValue = promptControl.Value; } catch (Exception) { newValue = null; } if (newValue == null) { return false; } if (!lastValueMap.TryGetValue(promptData.Name, out oldValue) || newValue.StringRepresentation != oldValue.StringRepresentation) { promptData.Values = newValue; lastValueMap[promptData.Name] = newValue; // cascading? => refresh data from server if (AllNodes[this.previousListIndex].Nodes.Count != 0) { RefreshPromptData(promptData); } } return true; } /// /// Validates all nodes. If any node has invalid input, the failed /// node will be selected in the tree view and false is returned. /// /// true if validation succeeds private bool ValidateAllNodes() { List allNodes = AllNodes; for (int i = 0; i < allNodes.Count; i++) { TreeNode treeNode = allNodes[i]; PromptData promptData = prompts[i]; PromptValue value = promptData.Values; string err; if (!promptData.WithinLimits(value) || value is RangePromptValue && (err=((RangePromptValue)value).CheckRange()) != null) { validateNextSelection = true; promptTree.SelectedNode = treeNode; return false; } } return true; } /// /// Called when a panel is done with validation /// /// public void DoneWithValidation(bool wasValid) { // this.btnOK.Enabled = wasValid; } /// /// a list of all treenodes in the prompt tree /// internal List AllNodes { get { if (this.allNodes == null) { List result = new List(); this.allNodes = _AllNodes(null); } return this.allNodes; } } /// /// internal method called recursively for generating a list of the tree nodes /// /// parent node to fetch nodes for /// a list of tree nodes including the passed in node and its children private List _AllNodes(TreeNode parentNode) { List result = new List(); TreeNodeCollection children; if (parentNode == null) { children = this.promptTree.Nodes; } else { children = parentNode.Nodes; } foreach (TreeNode n in children) { List subChildren = _AllNodes(n); result.Add(n); result.AddRange(subChildren.ToArray()); } return result; } /// /// Refreshes the prompt data from the server for the specified changed /// cascading entry. /// /// the changed cascading entry private void RefreshPromptData(PromptData changedCascadingPromptData) { refreshingCascadingPrompt = changedCascadingPromptData.Name; List promptDataList = prompts.Select(e => changedCascadingPromptData.Name == e.CascadingParent ? null : e).ToList(); promptDataList.AddRange(hiddenPrompts.Where(p => p.Name != null)); callback(promptDataList, true); // disable the UI until we got the updated prompt data Enabled = false; } /// /// Updates the prompt data with the values from the specified array. /// /// array to update the prompt data with internal void MergePromptData(PromptData[] promptDataArray) { if (refreshingCascadingPrompt == null && Visible) { // ignore any incomming prompt data as long as no refresh was triggered return; } bool structuralChange = false; int newCount = 0; HashSet cascadingParents = new HashSet(promptDataArray.Cast().Where(p => p.CascadingParent != null).Select(p => p.CascadingParent)); hiddenPrompts = new List(); for (int i = 0; i < promptDataArray.Length; i++) { PromptData promptData = promptDataArray[i]; if (promptData.Hidden && !cascadingParents.Contains(promptData.Name)) { hiddenPrompts.Add(promptData); continue; } newCount++; int index = prompts.FindIndex(e => e.Name == promptData.Name); if (index == -1) { structuralChange = true; } else { if (refreshingCascadingPrompt == null || refreshingCascadingPrompt != promptData.CascadingParent) { promptData.Values = prompts[index].Values; } prompts[index] = promptData; } } if (structuralChange || newCount != prompts.Count) { string selectedName = prompts[AllNodes.IndexOf(promptTree.SelectedNode)].Name; Prompts = promptDataArray.ToList(); int idx = prompts.FindIndex(e => e.Name == selectedName); previousListIndex = -1; promptTree.SelectedNode = AllNodes[idx==-1?0:idx]; } bool errorShown = CurrentPromptControl != null && CurrentPromptControl.ShowError; // update the shown fields ShowPrompt(prompts[AllNodes.IndexOf(promptTree.SelectedNode)]); if (errorShown) { CurrentPromptControl.ShowError = true; CurrentPromptControl.ValidatePrompt(); } // enable the UI (disabled since RefreshPromptData) Enabled = true; refreshingCascadingPrompt = null; } /// /// Gets the control of the currently shown prompt. /// private PromptControl CurrentPromptControl { get { return ((PromptControl)this.panel.Controls[0]); } } /// /// Resets the state of the dialog. The prompt tree is expanded and /// the first element is selected. The elements itselfs /// internal void ResetDialog() { promptTree.ExpandAll(); firstTime = true; promptTree.SelectedNode = this.AllNodes[0]; ShowPrompt(prompts[0]); previousListIndex = -1; } /// /// Sets the list of prompts. /// private List Prompts { set { this.allNodes = null; Func> createNodes = null; HashSet cascadingParents = new HashSet(value.Where(p=>p.CascadingParent != null).Select(p => p.CascadingParent)); createNodes = (parent) => from p in value where p.CascadingParent == parent && (!p.Hidden || cascadingParents.Contains(p.Name)) select new TreeNode(p.DisplayName, ImageIndexFor(p.Type), ImageIndexFor(p.Type), createNodes(p.Name).ToArray()); IEnumerable nodes = createNodes(null); Dictionary displayNameToPrompts = value.Where(p => p.DisplayName != null).ToDictionary(p => p.DisplayName, p => p); promptTree.Nodes.Clear(); promptTree.Nodes.AddRange(nodes.ToArray()); promptTree.ExpandAll(); prompts = this.AllNodes.Select(n => displayNameToPrompts[n.Text]).ToList(); hiddenPrompts = value.Where(p => p.Hidden && !cascadingParents.Contains(p.Name)).ToList(); } } } }