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();
}
}
}
}