FileSearchWindows/FileSearch/MainForm.cs
Dvurechensky 2edf663d5b 1.0.5
2025-05-12 06:55:09 +03:00

458 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Author: Nikolay Dvurechensky
* Site: https://www.dvurechensky.pro/
* Gmail: dvurechenskysoft@gmail.com
* Last Updated: 12 мая 2025 06:51:53
* Version: 1.0.7
*/
using FileSearch.Logic;
using FileSearch.Logic.Model.Engine;
using FileSearch.Logic.Model.Tree;
using FileSearch.Logic.Plugin;
using FileSearch.Logic.UI.Entries;
using FileSearch.Logic.UI.ViewBuilders;
using System.Diagnostics;
using System.Globalization;
namespace FileSearch
{
internal partial class MainForm : Form
{
private ExceptionsForm _exceptionsForm;
private event EventHandler<PathEventArgs> FileOpened;
private event EventHandler<PathEventArgs> DirectoryOpened;
private static FileSearcher _fileSearcher;
private bool _resultsViewIsUpdated = false;
private static CancellationTokenSource _tokenGetFilesSourceSearch = new CancellationTokenSource();
private string _searchPath = string.Empty;
public MainForm() => InitializeComponent();
#region Logic
/// <summary>
/// Подготовка к запуску сеанса поиска
/// </summary>
private DirectoryInfo SearchInit()
{
toolStripProgressBar1.Style = ProgressBarStyle.Marquee;
lstResults.ClearContent();
_resultsViewIsUpdated = false;
DirsTreeView.Controls.Clear();
DirsTreeView.Nodes.Clear();
_tokenGetFilesSourceSearch.Cancel();
SaveInfo(comboBox: true);
SearchBtn.Text = @"Остановить";
statusLabel.Text = @"Поиск... Пожалуйста подождите.";
statusLabelExceptions.Text = null;
return new DirectoryInfo(DirPathTxt.Text.Trim('\\') + "\\");
}
/// <summary>
/// Обработка события обнаружения данных
/// </summary>
/// <param name="searchResults">searchResults</param>
private void LoadList(IEnumerable<SearchResult> searchResults)
{
if (lstResults.InvokeRequired)
{
lstResults.BeginInvoke((Action<IEnumerable<SearchResult>>)LoadList, searchResults);
return;
}
if (!_resultsViewIsUpdated)
{
lstResults.SetViewBuilder(ViewBuilderFactory.Create());
_resultsViewIsUpdated = true;
}
lstResults.AddSearchResults(searchResults);
AddTimerTextInfo(_fileSearcher.CurrentTime.GetFriendlyNotation()); // обновляем таймер
AddFindFilesCount(lstResults.Count); // Обновляем число обнаруженных файлов
AddThreeViewData(searchResults); // Обновляем дерево директорий
}
/// <summary>
/// Обработка окончания сеанса поиска
/// </summary>
private void LoadListFinished()
{
if (lstResults.InvokeRequired)
{
lstResults.Invoke((Action)LoadListFinished);
return;
}
var exceptions = _fileSearcher.Exceptions;
if (exceptions.Count > 0)
statusLabelExceptions.Text = string.Format(CultureInfo.InvariantCulture, "{0} ошибок в поиске", exceptions.Count);
SearchBtn.Text = @"Поиск";
PauseBtn.Text = @"Пауза";
SearchBtn.Enabled = true;
statusLabel.Text = @"Завершено за " + _fileSearcher.OperatingTime.GetFriendlyNotation() + @". Найдено: " + lstResults.Count;
toolStripProgressBar1.Style = ProgressBarStyle.Blocks;
if (DirsTreeView.Nodes.Count > 0)
DirsTreeView.Focus();
}
/// <summary>
/// Конфигурирование критерий поиска
///
/// TODO: В будущем можно сделать тонкую настройку
/// критерий поиска в UI указав Control для включения тех или иных
/// </summary>
/// <param name="directoryInfo">directoryInfo</param>
/// <param name="text">text</param>
/// <returns></returns>
private EngineOptions BuildOptions(DirectoryInfo directoryInfo, string text)
{
var options = new EngineOptions(new[] { directoryInfo })
{
SearchName = text,
SearchIncludesFolders = true,
SearchNameIgnoreCasing = false,
SearchNameMatchFullPath = false,
SearchRecursive = true,
SearchNameAsRegularExpression = true,
SearchInArchives = false,
ContentAsRegularExpression = true,
ContentText = string.Empty,
ContentIgnoreCasing = true,
ContentWholeWordsOnly = false,
ContentEncodingFactory = null,
ContentForOfficeXml = false
};
return options;
}
/// <summary>
/// Построение дерева директорий
/// </summary>
/// <param name="searchResults">IEnumerable</param>
private void AddThreeViewData(IEnumerable<SearchResult> searchResults)
{
TreeViewBuilder.BuildTreeView(DirsTreeView, searchResults.Select(res => res.FileSystemInfo.FullName).ToArray());
}
/// <summary>
/// Инициализация чтения максимального числа файлов в директории
/// </summary>
private void LoadingMaxFiles()
{
if (_searchPath == DirPathTxt.Text) return;
_searchPath = DirPathTxt.Text;
var task = Task.Run(async () =>
{
AddProgressBarLoadMax(true);
await GetAllFilesExecute(_searchPath);
});
}
/// <summary>
/// Формирование async задачи на чтение максимального числа файлов в директории
/// </summary>
/// <param name="searchPath">path</param>
public async Task GetAllFilesExecute(string searchPath)
{
AddMaxFilesTxtInfo("0");
await Task.Factory.StartNew(() =>
{
GetAllFiles(searchPath);
});
}
/// <summary>
/// Получение максимального числа файлов в директории (с вложенными внутри)
/// </summary>
/// <param name="searchPath">path</param>
private void GetAllFiles(string searchPath)
{
long count = 0;
_tokenGetFilesSourceSearch = new CancellationTokenSource();
var searchTask = Task.Run(async () =>
{
try
{
count = await GetFileCountAsync(searchPath, _tokenGetFilesSourceSearch.Token);
}
catch (OperationCanceledException)
{
// Обработка отмены операции
AddMaxFilesTxtInfo(count.ToString());
AddProgressBarLoadMax(false);
}
}, _tokenGetFilesSourceSearch.Token);
searchTask.ContinueWith(t =>
{
searchTask = null;
AddMaxFilesTxtInfo(count.ToString());
AddProgressBarLoadMax(false);
});
}
public async Task<long> GetFileCountAsync(string directory, CancellationToken cancellationToken)
{
try
{
long count = 0;
foreach (var file in Directory.GetFiles(directory))
{
if (cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
}
count++;
}
foreach (var subDir in Directory.GetDirectories(directory))
{
if (cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
}
count += await GetFileCountAsync(subDir, cancellationToken);
}
return count;
}
catch (OperationCanceledException)
{
throw; // Пробрасываем исключение дальше для обработки в вызывающем коде
}
catch (UnauthorizedAccessException)
{
return 0; // Пропускаем каталоги, к которым нет доступа
}
catch (DirectoryNotFoundException)
{
return 0; // Пропускаем отсутствующие каталоги
}
}
/// <summary>
/// Сохранение полей (простейшее)
/// </summary>
/// <param name="comboBox">сохранение выбранного пути в comboBox в текущей сессии</param>
private void SaveInfo(bool comboBox = false)
{
File.WriteAllText(@"DirInfo.dat", DirPathTxt.Text);
File.WriteAllText(@"FileRegex.dat", FileRegexPathTxt.Text);
//сохранение выбранного пути для поиска в текущей сессии
if (comboBox)
{
var content = DirPathTxt.SelectedItem != null ? (string)DirPathTxt.SelectedItem : DirPathTxt.Text;
if (!string.IsNullOrEmpty(content))
{
if (DirPathTxt.Items.Contains(content))
DirPathTxt.Items.Remove(content);
DirPathTxt.Items.Insert(0, content);
DirPathTxt.SelectedItem = 0;
DirPathTxt.Text = content;
}
}
}
#endregion
#region Events UI
private void MainForm_Load(object sender, EventArgs e)
{
if (File.Exists(@"DirInfo.dat"))
DirPathTxt.Text = File.ReadAllText(@"DirInfo.dat");
if (File.Exists(@"FileRegex.dat"))
FileRegexPathTxt.Text = File.ReadAllText(@"FileRegex.dat");
}
private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
{
SaveInfo();
_tokenGetFilesSourceSearch.Cancel();
}
private void MainForm_DirectoryOpened(object? sender, PathEventArgs e)
{
Process.Start(new ProcessStartInfo("explorer.exe", "\"" + e.Entry.FileSystemInfo.FullName + "\""));
}
private void MainForm_FileOpened(object? sender, PathEventArgs e)
{
var file = e.Entry.FileSystemInfo.FullName;
Process.Start(new ProcessStartInfo("explorer.exe", file));
}
private void SearchBtn_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(DirPathTxt.Text)) return;
if (_fileSearcher != null && _fileSearcher.IsRunning)
{
_fileSearcher.Stop();
SearchBtn.Enabled = false;
return;
}
var dirInfo = SearchInit();
if (!dirInfo.Exists)
{
MessageBox.Show(@"Путь не существует.", @"Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
LoadingMaxFiles();
_fileSearcher = new FileSearcher(BuildOptions(dirInfo, FileRegexPathTxt.Text), Plugins.All().Select(f => f.GetCriterion()).Where(f => f != null).SelectMany(f => f));
_fileSearcher.Start(LoadList, LoadListFinished);
}
private void PauseBtn_Click(object sender, EventArgs e)
{
_fileSearcher?.Pause((state) =>
{
PauseBtn.Text = (state) ? "Далее" : "Пауза";
}, (PauseBtn.Text == "Далее") ? false : true);
}
private void DirPathTxt_TextChanged(object sender, EventArgs e)
{
DirPathTxt.ForeColor = Directory.Exists(DirPathTxt.Text) ? SystemColors.WindowText : Color.Red;
}
private void DirsTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
TreeNode node = e.Node;
var paths = e.Node.FullPath;
DirectoryInfo dir = new DirectoryInfo(paths);
FileSystemInfo fsi = dir as FileSystemInfo;
var path = new PathEntry(fsi);
if (path.IsDirectory)
{
if (DirectoryOpened != null)
DirectoryOpened(this, new PathEventArgs(path));
}
else if (FileOpened != null)
FileOpened(this, new PathEventArgs(path));
}
private void btnBrowse_Click(object sender, EventArgs e)
{
if (_fileSearcher != null && _fileSearcher.IsRunning)
{
MessageBox.Show("Поиск ещё идёт!");
return;
}
folderBrowserDialog.SelectedPath = DirPathTxt.Text;
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
// Set the selected index to -1 so when the text is set the index is forgotten.
DirPathTxt.SelectedIndex = -1;
DirPathTxt.Text = folderBrowserDialog.SelectedPath;
}
}
private void statusLabelExceptions_Click(object sender, EventArgs e)
{
if (_fileSearcher == null || _fileSearcher.Exceptions.Count <= 0)
return;
var form = _exceptionsForm ?? (_exceptionsForm = new ExceptionsForm());
form.SetContent(_fileSearcher.Exceptions);
form.ShowDialog();
}
#endregion
#region Async UI
/// <summary>
/// Обновление AllFilesTxt
/// </summary>
/// <param name="text">count</param>
private void AddMaxFilesTxtInfo(string text)
{
if (string.IsNullOrEmpty(text)) { return; }
if (InvokeRequired)
{
Invoke((Action<string>)AddMaxFilesTxtInfo, text);
return;
}
AllFilesTxt.Text = text;
AllFilesTxt.Update();
}
/// <summary>
/// Обновление pictureBoxLoading
/// </summary>
/// <param name="visible">bool</param>
private void AddProgressBarLoadMax(bool visible)
{
if (InvokeRequired)
{
Invoke((Action<bool>)AddProgressBarLoadMax, visible);
return;
}
pictureBoxLoading.Visible = visible;
pictureBoxLoading.Update();
pictureBoxLoading.Refresh();
}
/// <summary>
/// Обновление timerTxt
/// </summary>
/// <param name="text">count</param>
private void AddTimerTextInfo(string text)
{
if (string.IsNullOrEmpty(text)) { return; }
if (InvokeRequired)
{
Invoke((Action<string>)AddTimerTextInfo, text);
return;
}
timerTxt.Text = text;
timerTxt.Update();
}
/// <summary>
/// Обновление FindFilesTxt
/// </summary>
/// <param name="count">count</param>
private void AddFindFilesCount(int count)
{
if (InvokeRequired)
{
Invoke((Action<int>)AddFindFilesCount, count);
return;
}
FindFilesTxt.Text = count.ToString();
FindFilesTxt.Update();
}
#endregion
}
}