#region Disclaimer / License // Copyright (C) 2011, Kenneth Bergeron, IAP Worldwide Services, Inc // NOAA :: National Marine Fisheries Service // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace Duplicati.Scheduler { /// /// Edit a job /// public partial class JobDialog : Form { private Microsoft.Win32.TaskScheduler.Trigger itsTrigger; /// /// Gets/Sets the job's task scheduler trigger /// public Microsoft.Win32.TaskScheduler.Trigger Trigger { get { return itsTrigger; } set { this.TaskEditor.SetTrigger(value); } } /// /// Gets the JobsRow being edited /// public Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow Row { get; private set; } /// /// Gets the settings /// private Duplicati.Scheduler.Data.SchedulerDataSet.SettingsDataTable Settings { get { return ((Duplicati.Scheduler.Data.SchedulerDataSet)Row.Table.DataSet).Settings; } } /// /// XML of row prior to any changes /// private byte[] OriginalXML; /// /// Edit a Job /// /// Row to edit public JobDialog(Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow aRow) { InitializeComponent(); if (!string.IsNullOrEmpty(Properties.Settings.Default.TreeViewState)) this.folderSelectControl1.State = Properties.Settings.Default.TreeViewState; this.Text = "Edit job " + aRow.Name + "(" + Utility.User.UserName + ")"; // Dang background colors in tab pages - can't trust them. foreach (TabPage P in this.MainTabControl.TabPages) P.BackColor = this.BackColor; foreach (TabPage P in this.SourceTabControl.TabPages) P.BackColor = this.BackColor; this.folderSelectControl1.BackColor = this.BackColor; // Add radio buttons for backends; put the backend in the tag this.BackEndTableLayoutPanel.Controls.AddRange( (from Library.Interface.IBackend qB in Duplicati.Library.DynamicLoader.BackendLoader.Backends orderby qB.DisplayName select BackendRadioButton(qB)).ToArray()); // Put the tooltip text at the bottom of the screen for easy user viewage this.ExplainToolStripLabel.Text = this.MainTabControl.SelectedTab.ToolTipText; // And use the row SetRow(aRow); } /// /// Make a radio button with the backend attached to tag /// /// Backend to attach /// new radio button private RadioButton BackendRadioButton(Library.Interface.IBackend aBackend) { RadioButton Result = new RadioButton() { Text = aBackend.DisplayName, Tag = aBackend, }; Result.CheckedChanged += new EventHandler(BackendRadioButtonClicked); return Result; } /// /// The backend control was entered, probably user selected a backend /// void BackendRadioButtonClicked(object sender, EventArgs e) { if (!((RadioButton)sender).Checked) return; // Find the checked radio button and assign the GUI from the tag GuiControl = ((RadioButton)sender).Tag as Library.Interface.IGUIControl; // Show the new user interface GuiInterface = GuiControl.GetControl(new Dictionary(), Row.GuiOptions); GuiInterface.SetBounds(0, 0, this.UIFPanel.Width, this.UIFPanel.Height); GuiInterface.Visible = true; this.UIFPanel.Controls.Clear(); this.UIFPanel.Controls.Add(GuiInterface); } // The current backend user control private Library.Interface.IGUIControl GuiControl = null; // The current backend user control control private Control GuiInterface = null; /// /// Returns the XML of a jobs row /// /// Row to convert /// XML text in byte array private static byte[] RowtoXml(Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow aRow) { byte[] Result = null; using (Duplicati.Scheduler.Data.SchedulerDataSet.JobsDataTable cTable = new Data.SchedulerDataSet.JobsDataTable()) { cTable.Rows.Add(aRow.ItemArray); using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { cTable.WriteXml(ms); Result = ms.ToArray(); } } return Result; } /// /// Set up the controls for a job row /// /// Row to use private void SetRow(Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow aRow) { this.Row = aRow; this.OriginalXML = RowtoXml(aRow); this.Text = "Job: "+ Row.Name; // Set the source tree contents if (!Row.IsSourceNull()) this.folderSelectControl1.SelectedFolders = Row.Source.Split(new char[] { System.IO.Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); if (Row.IsFullRepeatDaysNull()) Row.FullRepeatDays = 10; // Full of it. this.FullAlwaysRadioButton.Checked = (Row.IsFullOnlyNull() ? false : Row.FullOnly) || (Row.FullAfterN == 0 && Row.FullRepeatDays == 0); this.FullDaysNumericUpDown.Value = (decimal)Row.FullRepeatDays; this.FullDaysRadioButton.Checked = Row.FullRepeatDays > 0; this.FullAfterNNumericUpDown.Value = (decimal)Row.FullAfterN; this.FullAfterNRadioButton.Checked = Row.FullAfterN > 0; if (!Row.IsFilterNull()) this.FilterList = Row.FilterLines; if (!Row.IsMaxFullsNull()) this.MaxFullsNumericUpDown.Value = Row.MaxFulls; if (Row.IsMaxAgeDaysNull()) Row.MaxAgeDays = 0; this.MaxAgeCheckBox.Checked = Row.MaxAgeDays > 0; this.MaxAgeNnumericUpDown.Value = (decimal)Row.MaxAgeDays; if (Row.IsMaxFullsNull()) Row.MaxFulls = 4; this.MaxFullsCheckBox.Checked = Row.MaxFulls > 0; this.MaxFullsNumericUpDown.Value = (decimal)Row.MaxFulls; this.PasswordMethodComboBox.SelectedIndex = Row.GetCheckSrc(); itsAdvanced.Map = Row.MapDrives; itsAdvanced.AutoDelete = Row.AutoCleanup; this.passwordControl1.CheckMod = aRow.CheckMod; this.passwordControl1.Checksum = aRow.Checksum; if (Settings.Values.IsCheckSrcNull()) Settings.Values.CheckSrc = false; if (!Settings.Values.CheckSrc) this.PasswordMethodComboBox.Items.RemoveAt(2); // No global option if not set if (!Row.IsDestinationNull()) { foreach (Control C in this.BackEndTableLayoutPanel.Controls) ((RadioButton)C).Checked = Row.Destination.StartsWith(((Duplicati.Library.Interface.IBackend)C.Tag).ProtocolKey); } } /// /// Rips the configuration from the backend control /// /// The Options from the control /// The backup destination /// private bool GetConfiguration(out Dictionary outOptions, out Dictionary outGuiOptions, out string outDestination) { // This makes me feel like a kiddie, and out args are evil outDestination = null; outOptions = null; outGuiOptions = null; if (GuiControl == null) return false; // First, call the 'Leave' thing GuiControl.Leave(GuiInterface); bool Result = false; // Now, go in with reflection and get that m_options thing try { System.Reflection.FieldInfo fld = GuiInterface.GetType().GetField("m_options", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (fld != null) { outGuiOptions = (Dictionary)fld.GetValue(GuiInterface); outOptions = new Dictionary(); // Now, we can call the GetConfiguration thing and convert the Options to usable ones. outDestination = GuiControl.GetConfiguration(new Dictionary(), outGuiOptions, outOptions); Result = true; } } catch (Exception Ex) { MessageBox.Show(Ex.Message); } return Result; } /// /// Returns a new un-attached JobsRow filled with edit results /// /// a new un-attached JobsRow private Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow GetNewRow() { Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow NewRow = null; // The table will die, yet the row lives on... using (Duplicati.Scheduler.Data.SchedulerDataSet.JobsDataTable TempTable = new Duplicati.Scheduler.Data.SchedulerDataSet.JobsDataTable()) { NewRow = TempTable.NewJobsRow(); NewRow.ItemArray = Row.ItemArray; GetRow(NewRow); } return NewRow; } /// /// Populate a JobsRow with results from the controls /// /// Row to fill private void GetRow(Duplicati.Scheduler.Data.SchedulerDataSet.JobsRow aRow) { // Now, if User has been in the Destination tab, we may have a new // set of destination and options, fetch them. if (GuiControl != null && VisitedDestination) { Dictionary Opts = new Dictionary(); Dictionary GuiOpts = new Dictionary(); string Dest = string.Empty; if (GetConfiguration(out Opts, out GuiOpts, out Dest)) { aRow.SetOptions(Opts); aRow.SetGuiOptions(GuiOpts); aRow.Destination = Dest; } } // Get stuff from the controls aRow.Source = string.Join(System.IO.Path.PathSeparator.ToString(), this.folderSelectControl1.SelectedFolders); aRow.FullOnly = this.FullAlwaysRadioButton.Checked; aRow.FullAfterN = this.FullAfterNRadioButton.Checked ? (int)this.FullAfterNNumericUpDown.Value : 0; aRow.FullRepeatDays = this.FullDaysRadioButton.Checked ? (int)this.FullDaysNumericUpDown.Value : 0; aRow.FilterLines = this.FilterList; aRow.MaxFulls = this.MaxFullsCheckBox.Checked ? (int)this.MaxFullsNumericUpDown.Value : 0; aRow.MaxAgeDays = this.MaxAgeCheckBox.Checked ? (int)this.MaxAgeNnumericUpDown.Value : 0; aRow.Enabled = this.TaskEditor.GetTrigger().Enabled; aRow.TriggerXML = TaskEditControl.TriggerToXml(this.TaskEditor.GetTrigger()); if (itsAdvanced.DialogResult == DialogResult.OK) { aRow.AutoCleanup = itsAdvanced.AutoDelete; aRow.MapDrives = itsAdvanced.Map; } aRow.Checksum = Settings.Values.Checksum; aRow.CheckMod = Settings.Values.CheckMod; aRow.SetCheckSrc(this.PasswordMethodComboBox.SelectedIndex); // Never edited (?) if (aRow.IsLastModNull()) aRow.LastMod = DateTime.Now; } /// /// Updates a summary with the latest /// private void GetSummary() { this.jobSummary1.SetSummary(GetNewRow(), TaskScheduler.Describe(this.TaskEditor.GetTrigger())); // (Row.TaskName)); this.jobSummary1.Enabled = this.TaskEditor.GetTrigger().Enabled; this.SummaryTabPage.ImageIndex = this.jobSummary1.ValidateSettings() ? 5 : 6; } /// /// Pressed the OK button, update the row and close /// /// /// private void OKButton_Click(object sender, EventArgs e) { // JobSummary has error checking, do it if (!this.jobSummary1.ValidateSettings()) { MessageBox.Show("The errors must be resolved first."); return; } GetRow(Row); // Populate the row // Get the trigger itsTrigger = this.TaskEditor.GetTrigger(); // And report all OK if there was a change this.DialogResult = (OriginalXML == RowtoXml(Row)) ? DialogResult.Ignore : DialogResult.OK; Close(); } /// /// Pressed CANCEL, just close /// private void CanButton_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.Cancel; Close(); } // Has user been in the Destination Tab private bool VisitedDestination = false; /// /// Changed a TAB either directly or with NEXT or BACK /// private void MainTabControlSelectedIndexChanged(object sender, EventArgs e) { // Can we go back? this.BackButton.Enabled = this.MainTabControl.SelectedIndex > 0; // Show user the tooltip this.ExplainToolStripLabel.Text = this.MainTabControl.SelectedTab.ToolTipText; // Are we on the summary page? bool LastPage = (this.MainTabControl.SelectedIndex == this.MainTabControl.TabPages.Count - 1); // What's next this.NextButton.Text = LastPage ? " FINISHED " : " NEXT > "; // Is we in the Destination place? VisitedDestination = VisitedDestination || this.MainTabControl.SelectedTab.Text == "Destination"; // If summary page, populate it with the latest stuff if (LastPage) GetSummary(); } /// /// Pressed BACK - go to previous page /// private void BackButton_Click(object sender, EventArgs e) { this.MainTabControl.SelectedIndex -= 1; } /// /// Pressed NEXT - go to next page or show 'finished' /// private void NextButton_Click(object sender, EventArgs e) { if (this.NextButton.Text == " FINISHED ") this.OKButton_Click(sender, e); else this.MainTabControl.SelectedIndex++; } /// /// Get/Set the filters /// private string[] FilterList { get { return (from object qO in this.FilterListBox.Items select qO.ToString()).ToArray(); } set { this.FilterListBox.Items.Clear(); this.FilterListBox.Items.AddRange(value); } } /// /// Pressed FILTERS - edit the filters /// private void FiltersToolStripButton_Click(object sender, EventArgs e) { FilterDialog fd = new FilterDialog(); fd.Filter = this.FilterList; if (fd.ShowDialog() == DialogResult.OK) this.FilterList = fd.Filter; } /// /// One of the checkboxes or radio buttons changes /// private void CheckedChanged(object sender, EventArgs e) { this.MaxFullsNumericUpDown.Enabled = this.MaxFullsCheckBox.Checked; this.MaxAgeNnumericUpDown.Enabled = this.MaxAgeCheckBox.Checked; this.FullAfterNNumericUpDown.Enabled = this.FullAfterNRadioButton.Checked; this.FullDaysNumericUpDown.Enabled = this.FullDaysRadioButton.Checked; } private AdvancedDialog itsAdvanced = new AdvancedDialog(); /// /// Pressed the ADVANCED button /// private void AdvancedButtonClick(object sender, EventArgs e) { itsAdvanced.ShowDialog(); } /// /// Changes the Source TAB page /// private void SourceTabControl_SelectedIndexChanged(object sender, EventArgs e) { if (this.SourceTabControl.SelectedTab == this.ListViewTabPage) { this.SourceListBox.Items.Clear(); this.SourceListBox.Items.AddRange(this.folderSelectControl1.SelectedFolders); } } /// /// Changes the password method /// private void PasswordMethodComboBox_SelectedIndexChanged(object sender, EventArgs e) { this.passwordControl1.Enabled = this.PasswordMethodComboBox.SelectedIndex == 1; } private void JobDialog_FormClosing(object sender, FormClosingEventArgs e) { Properties.Settings.Default.TreeViewState = this.folderSelectControl1.State; } } }