/* Copyright (c) Cloud Software Group, Inc.
*
* 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.Linq;
using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.CustomFields;
using XenAdmin.Dialogs;
using XenAPI;
namespace XenAdmin.SettingsPanels
{
public partial class CustomFieldsDisplayPage : UserControl, IEditPage
{
///
/// This is not ideal, but we need something in the last row of the tableLayoutPanel
/// that fills all the way to the bottom of the page; if we leave the controls in the
/// last row, they align vertically in its middle because they don't have vertical
/// anchoring (because they have different heights and it's a pain to get the vertical
/// alignment right by fiddling with the margins)
///
private readonly Panel _panelDummy;
private IXenObject _xenObject;
private readonly List _fieldRows = new List();
public CustomFieldsDisplayPage()
{
InitializeComponent();
_panelDummy = new Panel { Size = new Size(0, 0), Margin = new Padding(0) };
Text = Messages.CUSTOM_FIELDS;
}
#region IEditPage Members
public AsyncAction SaveSettings()
{
var customFields = new List();
foreach (var row in _fieldRows)
{
object currentValue = CustomFieldsManager.GetCustomFieldValue(_xenObject, row.CustomFieldDefinition);
object newValue = row.GetValue();
if (currentValue == null && newValue == null)
continue;
customFields.Add(new CustomField(row.CustomFieldDefinition, newValue));
}
return new SaveCustomFieldsAction(_xenObject, customFields, true);
}
public void SetXenObjects(IXenObject orig, IXenObject clone)
{
CustomFieldsManager.CustomFieldsChanged -= CustomFields_CustomFieldsChanged;
_xenObject = clone;
if (_xenObject != null)
{
CustomFieldsManager.CustomFieldsChanged += CustomFields_CustomFieldsChanged;
Rebuild(true);
}
}
public bool ValidToSave => true;
public void ShowLocalValidationMessages()
{
}
public void HideLocalValidationMessages()
{
}
public void Cleanup()
{
}
public bool HasChanged
{
get
{
foreach (var row in _fieldRows)
{
object currentValue = CustomFieldsManager.GetCustomFieldValue(_xenObject, row.CustomFieldDefinition);
object newValue = row.GetValue();
if (currentValue == null && newValue == null)
continue;
if (currentValue == null || newValue == null)
return true;
if (newValue.Equals(currentValue))
continue;
return true;
}
return false;
}
}
#endregion
#region IVerticalTab Members
public string SubText
{
get
{
List fields = new List();
foreach (var row in _fieldRows)
{
object newValue = row.GetValue();
if (newValue == null || newValue.ToString() == string.Empty)
continue;
fields.Add(row.CustomFieldDefinition.Name + Messages.GENERAL_PAGE_KVP_SEPARATOR + newValue);
}
if (fields.Count == 0)
return Messages.NONE;
return string.Join(", ", fields);
}
}
public Image Image => Images.StaticImages._000_Fields_h32bit_16;
#endregion
private void CustomFields_CustomFieldsChanged()
{
Rebuild(false);
}
private void Rebuild(bool resetValues)
{
var customFieldDefinitions = CustomFieldsManager.GetCustomFields(_xenObject.Connection).ToArray();
tableLayoutPanel.SuspendLayout();
tableLayoutPanel.Controls.Remove(_panelDummy);
// Add new custom fields
foreach (var definition in customFieldDefinitions)
{
object value = CustomFieldsManager.GetCustomFieldValue(_xenObject, definition);
var row = _fieldRows.FirstOrDefault(r => r.CustomFieldDefinition.Equals(definition));
if (row == null)
{
row = new CustomFieldRow(definition, value);
row.DeleteCustomFieldClicked += DeleteCustomFieldClicked_Click;
_fieldRows.Add(row);
tableLayoutPanel.Controls.AddRange(row.Controls);
if (1 < row.Controls.Length && row.Controls.Length < tableLayoutPanel.ColumnCount)
tableLayoutPanel.SetColumnSpan(row.Controls[1], 2); //this is the textbox
}
else if (resetValues)
{
row.SetValue(value);
}
}
tableLayoutPanel.Controls.Add(_panelDummy);
var extraRows = _fieldRows.Where(r => !customFieldDefinitions.Contains(r.CustomFieldDefinition)).ToList();
foreach (var row in extraRows)
{
row.DeleteCustomFieldClicked -= DeleteCustomFieldClicked_Click;
_fieldRows.Remove(row);
foreach (var control in row.Controls)
{
tableLayoutPanel.Controls.Remove(control);
control.Dispose();
}
}
tableLayoutPanel.ResumeLayout();
}
private void buttonNewCustomField_Click(object sender, EventArgs e)
{
using (var dialog = new NewCustomFieldDialog(_xenObject.Connection))
{
if (dialog.ShowDialog() == DialogResult.OK)
new AddCustomFieldAction(_xenObject.Connection, dialog.Definition).RunAsync();
}
}
private void DeleteCustomFieldClicked_Click(CustomFieldRow row)
{
string name = row.CustomFieldDefinition.Name.Ellipsise(50);
if (!Program.RunInAutomatedTestMode)
{
using (var dialog = new WarningDialog(string.Format(Messages.MESSAGEBOX_DELETE_CUSTOM_FIELD, name),
ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo))
{
if (dialog.ShowDialog(Program.MainWindow) != DialogResult.Yes)
return;
}
}
if (_xenObject.Connection != null && _xenObject.Connection.IsConnected)
{
new RemoveCustomFieldAction(_xenObject.Connection, row.CustomFieldDefinition).RunAsync();
}
else if (!Program.RunInAutomatedTestMode)
{
using (var dlg = new ErrorDialog(Messages.DISCONNECTED_BEFORE_ACTION_STARTED))
dlg.ShowDialog(this);
}
}
private class CustomFieldRow
{
private readonly TextBox _textBox;
private readonly DateTimePicker _datePicker;
private readonly DateTimePicker _timePicker;
public event Action DeleteCustomFieldClicked;
public CustomFieldRow(CustomFieldDefinition definition, object value)
{
CustomFieldDefinition = definition;
var controls = new List();
var label = CreateNewLabel(definition);
controls.Add(label);
switch (definition.Type)
{
case CustomFieldDefinition.Types.String:
_textBox = CreateNewTextBox(value as string);
controls.Add(_textBox);
break;
case CustomFieldDefinition.Types.Date:
_datePicker = CreateNewDatePicker(value as DateTime?);
_timePicker = CreateNewTimePicker(value as DateTime?);
controls.Add(_datePicker);
controls.Add(_timePicker);
break;
default:
throw new InvalidEnumArgumentException(nameof(definition));
}
var deleteButton = CreateNewDeleteButton();
controls.Add(deleteButton);
Controls = controls.ToArray();
UpdateDateTImePickerState();
}
public CustomFieldDefinition CustomFieldDefinition { get; }
public Control[] Controls { get; }
public object GetValue()
{
if (_textBox != null && !string.IsNullOrEmpty(_textBox.Text))
return _textBox.Text;
if (_datePicker != null && _timePicker != null && _datePicker.Checked)
{
DateTime date = _datePicker.Value;
DateTime time = _timePicker.Value;
return new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second);
}
return null;
}
public void SetValue(object value)
{
if (_textBox != null)
{
_textBox.Text = value as string;
return;
}
if (_datePicker != null && _timePicker != null)
{
if (value != null)
{
_datePicker.Checked = true;
_datePicker.Value = (DateTime)value;
_timePicker.Value = (DateTime)value;
}
else
{
_datePicker.Checked = false;
}
}
}
private Label CreateNewLabel(CustomFieldDefinition customFieldDefinition)
{
return new Label
{
Anchor = AnchorStyles.Left | AnchorStyles.Right,
Text = customFieldDefinition.Name.EscapeAmpersands().Ellipsise(45),
Font = Program.DefaultFont,
AutoSize = true,
AutoEllipsis = false
};
}
private TextBox CreateNewTextBox(string text)
{
return new TextBox
{
Anchor = AnchorStyles.Left | AnchorStyles.Right,
Text = text ?? string.Empty
};
}
private DateTimePicker CreateNewDatePicker(DateTime? value)
{
var date = new DateTimePicker
{
Anchor = AnchorStyles.Left | AnchorStyles.Right,
MinDate = DateTime.MinValue,
MaxDate = DateTime.MaxValue,
ShowCheckBox = true,
Format = DateTimePickerFormat.Long,
Checked = value.HasValue
};
if (value.HasValue)
date.Value = (DateTime)value;
date.ValueChanged += Date_ValueChanged;
return date;
}
private DateTimePicker CreateNewTimePicker(DateTime? value)
{
var time = new DateTimePicker
{
Anchor = AnchorStyles.Left | AnchorStyles.Right,
MinDate = DateTime.MinValue,
MaxDate = DateTime.MaxValue,
Format = DateTimePickerFormat.Time,
ShowUpDown = true,
Enabled = value.HasValue
};
if (value.HasValue)
time.Value = (DateTime)value;
return time;
}
private Button CreateNewDeleteButton()
{
var buttonDelete = new Button
{
Anchor = AnchorStyles.Left,
BackColor = Color.Transparent,
FlatStyle = FlatStyle.Flat,
Image = Images.StaticImages._000_Abort_h32bit_16,
Size = new Size(22, 22),
Text = string.Empty,
};
buttonDelete.FlatAppearance.BorderSize = 0;
buttonDelete.Click += buttonDelete_Click;
return buttonDelete;
}
private void UpdateDateTImePickerState()
{
if (_datePicker != null && _timePicker != null)
_timePicker.Enabled = _datePicker.Checked;
}
private void Date_ValueChanged(object sender, EventArgs e)
{
UpdateDateTImePickerState();
}
private void buttonDelete_Click(object sender, EventArgs e)
{
DeleteCustomFieldClicked?.Invoke(this);
}
}
}
}