Files
duplicati/Duplicati/GUI/Wizard pages/Add backup/SelectFiles.cs
T
kenneth.skovhede@gmail.com daaef5f092 Fixed issue #39.
Fixed issue #41.
Fixed issue #11.
Refactored the way settings are stored, this means that you cannot go back to a previous version if you have used this one.
A backup of the old database will be created, if you need to use a previous version.
Fixed a bug in the S3 backend where the filenames would be prefixed with a slash.
Moved the file time seperator char out of the file backend, and into common settings.


git-svn-id: https://duplicati.googlecode.com/svn/trunk@134 59da171f-624f-0410-aa54-27559c288bec
2009-03-29 18:06:02 +00:00

508 lines
23 KiB
C#

#region Disclaimer / License
// Copyright (C) 2009, Kenneth Skovhede
// http://www.hexad.dk, opensource@hexad.dk
//
// 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.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Wizard;
using Duplicati.Datamodel;
namespace Duplicati.GUI.Wizard_pages.Add_backup
{
public partial class SelectFiles : WizardControl
{
private WorkerThread<string> m_calculator;
private object m_lock = new object();
private Dictionary<string, long> m_sizes;
private WizardSettingsWrapper m_wrapper;
private string m_myPictures;
private string m_myMusic;
private string m_desktop;
private string m_appData;
private string m_myDocuments;
private string[] m_specialFolders;
private bool m_warnedFiltersChanged = true;
public SelectFiles()
: base("Select files to backup", "On this page you must select the folder and files you wish to backup")
{
InitializeComponent();
m_sizes = new Dictionary<string, long>();
//TODO: If any of these variables point to the same folder, bad things will happen...
m_myPictures = Library.Core.Utility.AppendDirSeperator("!" + System.Environment.GetFolderPath(Environment.SpecialFolder.MyPictures));
m_myMusic = Library.Core.Utility.AppendDirSeperator("!" + System.Environment.GetFolderPath(Environment.SpecialFolder.MyMusic));
m_desktop = Library.Core.Utility.AppendDirSeperator("!" + System.Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
m_appData = Library.Core.Utility.AppendDirSeperator("!" + System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
m_myDocuments = Library.Core.Utility.AppendDirSeperator("!" + System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments));
FolderTooltip.SetToolTip(IncludeDocuments, m_myDocuments.Substring(1));
FolderTooltip.SetToolTip(IncludeMusic, m_myMusic.Substring(1));
FolderTooltip.SetToolTip(IncludeImages, m_myPictures.Substring(1));
FolderTooltip.SetToolTip(IncludeDesktop, m_desktop.Substring(1));
FolderTooltip.SetToolTip(IncludeSettings, m_appData.Substring(1));
m_specialFolders = new string[] {
m_myDocuments.Substring(1),
m_myMusic.Substring(1),
m_myPictures.Substring(1),
m_appData.Substring(1),
m_desktop.Substring(1)
};
base.PageEnter += new PageChangeHandler(SelectFiles_PageEnter);
base.PageLeave += new PageChangeHandler(SelectFiles_PageLeave);
}
void m_owner_Cancelled(object sender, CancelEventArgs e)
{
if (m_calculator != null)
m_calculator.ClearQueue(true);
}
void SelectFiles_PageLeave(object sender, PageChangedArgs args)
{
m_owner.Cancelled -= new CancelEventHandler(m_owner_Cancelled);
m_settings["Files:Sizes"] = m_sizes;
m_settings["Files:WarnedFilters"] = m_warnedFiltersChanged;
if (m_calculator != null)
m_calculator.ClearQueue(true);
if (args.Direction == PageChangedDirection.Back)
return;
if (DocumentsRadio.Checked)
{
if (!m_warnedFiltersChanged)
{
if (MessageBox.Show(this, "You have modified the filters, but selected files by groups.\nSince that feature uses filters, there is a chance that your filters\nwill be removed after this.\nIf you switch to using only a single folder, your filters will not be modified.\n\nDo you want to continue, and possibly lose your filter setup?", Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning) != DialogResult.Yes)
{
args.Cancel = true;
return;
}
m_warnedFiltersChanged = true;
}
List<string> folders = new List<string>();
List<string> exfolders = new List<string>();
if (IncludeDocuments.Checked)
folders.Add(m_myDocuments.Substring(1));
else
exfolders.Add(m_myDocuments.Substring(1));
if (IncludeImages.Checked)
folders.Add(m_myPictures.Substring(1));
else
exfolders.Add(m_myPictures.Substring(1));
if (IncludeMusic.Checked)
folders.Add(m_myMusic.Substring(1));
else
exfolders.Add(m_myMusic.Substring(1));
if (IncludeDesktop.Checked)
folders.Add(m_desktop.Substring(1));
else
exfolders.Add(m_desktop.Substring(1));
if (IncludeSettings.Checked)
folders.Add(m_appData.Substring(1));
else
exfolders.Add(m_appData.Substring(1));
if (folders.Count == 0)
{
MessageBox.Show(this, "You have not included any files.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
args.Cancel = true;
return;
}
//Find the most common root
string basefolder = folders[0];
bool hasCommonParent = false;
foreach (string f in folders)
if (basefolder.StartsWith(f))
basefolder = f;
else if (!f.StartsWith(basefolder))
{
string[] p1 = basefolder.Split(System.IO.Path.DirectorySeparatorChar);
string[] p2 = f.Split(System.IO.Path.DirectorySeparatorChar);
int ix = 0;
while (p1.Length > ix && p2.Length > ix && p1[ix] == p2[ix])
ix++;
if (ix == 0)
{
MessageBox.Show(this, "Due to your machine setup, it is not possible to backup\nthe selected folders in the same backup.\nTry unchecking some items, and create more than one backup.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
args.Cancel = true;
return;
}
hasCommonParent = true;
basefolder = Library.Core.Utility.AppendDirSeperator(string.Join(System.IO.Path.DirectorySeparatorChar.ToString(), p1, 0, ix));
}
m_wrapper.EncodedFilters = EncodeFilterString(basefolder, hasCommonParent, folders, exfolders, m_wrapper.EncodedFilters);
m_wrapper.SourcePath = basefolder;
}
else
{
if (!System.IO.Directory.Exists(TargetFolder.Text))
{
MessageBox.Show(this, "The folder \"" + TargetFolder.Text + "\" does not exist.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
args.Cancel = true;
return;
}
m_wrapper.SourcePath = TargetFolder.Text;
}
if (m_calculator != null)
m_calculator.ClearQueue(true);
m_settings["Files:WarnedFilters"] = m_warnedFiltersChanged;
args.NextPage = new PasswordSettings();
}
private string EncodeFilterString(string basefolder, bool hasCommonParent, List<string> folders, List<string> exfolders, string existingFilter)
{
List<KeyValuePair<bool, string>> filters = new List<KeyValuePair<bool, string>>();
List<KeyValuePair<bool, string>> extras = new List<KeyValuePair<bool, string>>();
foreach (KeyValuePair<bool, string> tf in Library.Core.FilenameFilter.DecodeFilter(existingFilter))
if (!tf.Value.StartsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
extras.Add(tf);
folders.Sort();
folders.Reverse();
//Include selected folders
foreach (string f in folders)
{
//Exclude subfolders
foreach (string s in exfolders)
if (s.StartsWith(f) && (basefolder == s || s.StartsWith(basefolder)))
filters.Add(new KeyValuePair<bool, string>(false, Library.Core.FilenameFilter.ConvertGlobbingToRegExp(s.Substring(basefolder.Length - 1) + "*")));
if (hasCommonParent)
filters.Add(new KeyValuePair<bool, string>(true, Library.Core.FilenameFilter.ConvertGlobbingToRegExp(f.Substring(basefolder.Length - 1) + "*")));
}
//Exclude everything else if they have a non-included parent
if (hasCommonParent)
filters.Add(new KeyValuePair<bool, string>(false, Library.Core.FilenameFilter.ConvertGlobbingToRegExp("*")));
extras.AddRange(filters);
return Library.Core.FilenameFilter.EncodeAsFilter(extras);
}
void SelectFiles_PageEnter(object sender, PageChangedArgs args)
{
m_wrapper = new WizardSettingsWrapper(m_settings);
m_owner.Cancelled += new CancelEventHandler(m_owner_Cancelled);
if (m_settings.ContainsKey("Files:Sizes"))
m_sizes = (Dictionary<string, long>)m_settings["Files:Sizes"];
if (m_settings.ContainsKey("Files:WarnedFilters"))
m_warnedFiltersChanged = (bool)m_settings["Files:WarnedFilters"];
if (!m_valuesAutoLoaded)
{
if (string.IsNullOrEmpty(m_wrapper.SourcePath))
{
DocumentsRadio.Checked = true;
IncludeDocuments.Checked = true;
IncludeImages.Checked = true;
IncludeMusic.Checked = false;
IncludeDesktop.Checked = true;
IncludeSettings.Checked = false;
}
else
{
string p = Library.Core.Utility.AppendDirSeperator(m_wrapper.SourcePath);
List<KeyValuePair<bool, string>> filters = Library.Core.FilenameFilter.DecodeFilter(m_wrapper.EncodedFilters);
bool hasCommonBase = false;
List<CheckBox> included = new List<CheckBox>();
List<CheckBox> excluded = new List<CheckBox>();
string startChar = Library.Core.FilenameFilter.ConvertGlobbingToRegExp(System.IO.Path.DirectorySeparatorChar.ToString());
Dictionary<string, CheckBox> specialFolders = new Dictionary<string,CheckBox>();
if (m_myDocuments.Substring(1).StartsWith(p))
{
specialFolders.Add(Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_myDocuments.Substring(p.Length) + "*"), IncludeDocuments);
specialFolders.Add(Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_myDocuments.Substring(1) + "*"), IncludeDocuments);
}
if (m_myPictures.Substring(1).StartsWith(p))
{
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_myPictures.Substring(p.Length) + "*")] = IncludeImages;
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_myPictures.Substring(1) + "*")] = IncludeImages;
}
if (m_myMusic.Substring(1).StartsWith(p))
{
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_myMusic.Substring(p.Length) + "*")] = IncludeMusic;
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_myMusic.Substring(1) + "*")] = IncludeMusic;
}
if (m_desktop.Substring(1).StartsWith(p))
{
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_desktop.Substring(p.Length) + "*")] = IncludeDesktop;
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_desktop.Substring(1) + "*")] = IncludeDesktop;
}
if (m_appData.Substring(1).StartsWith(p))
{
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_appData.Substring(p.Length) + "*")] = IncludeSettings;
specialFolders[Library.Core.FilenameFilter.ConvertGlobbingToRegExp(m_appData.Substring(1) + "*")] = IncludeSettings;
}
bool unsupported = false;
foreach (KeyValuePair<bool, string> s in filters)
{
hasCommonBase |= (s.Key == false && s.Value == ".*");
if (s.Value.StartsWith(startChar))
{
if (specialFolders.ContainsKey(s.Value))
{
if (s.Key)
included.Add(specialFolders[s.Value]);
else
excluded.Add(specialFolders[s.Value]);
}
else
unsupported = true;
}
}
string baseEncoded = Library.Core.FilenameFilter.ConvertGlobbingToRegExp(p);
foreach (string s in specialFolders.Keys)
if (!excluded.Contains(specialFolders[s]) && s.StartsWith(baseEncoded) && !included.Contains(specialFolders[s]))
included.Add(specialFolders[s]);
TargetFolder.Text = m_wrapper.SourcePath;
IncludeDocuments.Checked =
IncludeImages.Checked =
IncludeMusic.Checked =
IncludeDesktop.Checked =
IncludeSettings.Checked = false;
foreach (CheckBox c in included)
c.Checked = true;
if (unsupported || included.Count == 0)
FolderRadio.Checked = true;
else
DocumentsRadio.Checked = true;
m_warnedFiltersChanged = !unsupported;
}
}
if (FolderRadio.Checked)
TargetFolder_Leave(null, null);
else
Rescan();
}
private void StartCalculator()
{
if (m_calculator == null)
{
totalSize.Text = "Calculating size ...";
m_calculator = new WorkerThread<string>(CalculateFolderSize);
m_calculator.CompletedWork += new EventHandler(m_calculator_CompletedWork);
}
}
void m_calculator_CompletedWork(object sender, EventArgs e)
{
if (this.InvokeRequired)
this.Invoke(new EventHandler(m_calculator_CompletedWork), sender, e);
else
{
if (m_calculator == null)
return;
long s = 0;
if (DocumentsRadio.Checked)
{
if (m_sizes.ContainsKey(m_myDocuments) && IncludeDocuments.Checked)
s += m_sizes[m_myDocuments];
if (m_sizes.ContainsKey(m_myMusic) && IncludeMusic.Checked)
s += m_sizes[m_myMusic];
if (m_sizes.ContainsKey(m_myPictures) && IncludeImages.Checked)
s += m_sizes[m_myPictures];
if (m_sizes.ContainsKey(m_appData) && IncludeSettings.Checked)
s += m_sizes[m_appData];
if (m_sizes.ContainsKey(m_desktop) && IncludeDesktop.Checked)
s += m_sizes[m_desktop];
}
else
s = m_sizes.ContainsKey(TargetFolder.Text) ? m_sizes[TargetFolder.Text] : 0;
if (m_calculator.CurrentTasks.Count == 0 && !m_calculator.Active)
totalSize.Text = string.Format("The selected items take up {0} of space", Library.Core.Utility.FormatSizeString(s));
else
totalSize.Text = string.Format("Calculating ... (using more than {0} of space)", Library.Core.Utility.FormatSizeString(s));
if (m_sizes.ContainsKey(m_myMusic))
myMusicSize.Text = Library.Core.Utility.FormatSizeString(m_sizes[m_myMusic]);
if (m_sizes.ContainsKey(m_myPictures))
myPicturesSize.Text = Library.Core.Utility.FormatSizeString(m_sizes[m_myPictures]);
if (m_sizes.ContainsKey(m_desktop))
desktopSize.Text = Library.Core.Utility.FormatSizeString(m_sizes[m_desktop]);
if (m_sizes.ContainsKey(m_appData))
appdataSize.Text = Library.Core.Utility.FormatSizeString(m_sizes[m_appData]);
if (m_sizes.ContainsKey(m_myDocuments))
myDocumentsSize.Text = Library.Core.Utility.FormatSizeString(m_sizes[m_myDocuments]);
if (m_sizes.ContainsKey(TargetFolder.Text))
customSize.Text = Library.Core.Utility.FormatSizeString(m_sizes[TargetFolder.Text]);
}
}
private void Rescan()
{
lock (m_lock)
{
StartCalculator();
if (!m_sizes.ContainsKey(m_myMusic))
m_calculator.AddTask(m_myMusic);
if (!m_sizes.ContainsKey(m_myPictures))
m_calculator.AddTask(m_myPictures);
if (!m_sizes.ContainsKey(m_desktop))
m_calculator.AddTask(m_desktop);
if (!m_sizes.ContainsKey(m_appData))
m_calculator.AddTask(m_appData);
if (!m_sizes.ContainsKey(m_myDocuments))
m_calculator.AddTask(m_myDocuments);
m_calculator_CompletedWork(null, null);
}
}
private void BrowseFolderButton_Click(object sender, EventArgs e)
{
if (folderBrowserDialog.ShowDialog(this) == DialogResult.OK)
{
TargetFolder.Text = folderBrowserDialog.SelectedPath;
TargetFolder_Leave(null, null);
}
}
private void TargetType_CheckedChanged(object sender, EventArgs e)
{
DocumentGroup.Enabled = DocumentsRadio.Checked;
FolderGroup.Enabled = FolderRadio.Checked;
if (DocumentsRadio.Checked)
Rescan();
else
TargetFolder_Leave(sender, e);
}
private void CalculateFolderSize(string folder)
{
lock (m_lock)
if (m_sizes.ContainsKey(folder))
return;
long size = 0;
Library.Core.FilenameFilter fnf = null;
string folderkey = folder;
//Special folders, extended logic
if (folder.StartsWith("!"))
{
folder = folder.Substring(1);
List<KeyValuePair<bool, string>> filters = new List<KeyValuePair<bool,string>>();
filters.Add(new KeyValuePair<bool, string>(true, Library.Core.FilenameFilter.ConvertGlobbingToRegExp(folder + "*")));
foreach (string s in m_specialFolders)
if (s != folder && s.StartsWith(folder))
filters.Add(new KeyValuePair<bool, string>(false, Library.Core.FilenameFilter.ConvertGlobbingToRegExp(s + "*")));
if (filters.Count > 1)
fnf = new Duplicati.Library.Core.FilenameFilter(filters);
}
if (System.IO.Directory.Exists(folder))
{
try
{
foreach (string file in Duplicati.Library.Core.Utility.EnumerateFiles(folder, fnf))
try { size += new System.IO.FileInfo(file).Length; }
catch { }
}
catch { }
}
lock (m_lock)
m_sizes[folderkey] = size;
}
private void TargetFolder_Leave(object sender, EventArgs e)
{
lock (m_lock)
if (TargetFolder.Text.Trim().Length > 0 && !m_sizes.ContainsKey(TargetFolder.Text))
{
StartCalculator();
m_calculator.ClearQueue(true);
m_calculator.AddTask(TargetFolder.Text);
m_calculator_CompletedWork(null, null);
}
else if (TargetFolder.Text == "")
totalSize.Text = "";
else if (m_sizes.ContainsKey(TargetFolder.Text))
{
StartCalculator();
m_calculator.ClearQueue(true);
m_calculator_CompletedWork(null, null);
}
}
private void SelectFiles_VisibleChanged(object sender, EventArgs e)
{
if (!this.Visible && m_calculator != null)
{
m_calculator.Terminate(false);
m_calculator = null;
}
}
}
}