random-file-runner/source/MainForm.cs
Ray e1087a31db Changed to use simple memobox instead of memobox form
Added new icon
Changed to implement UI layout
2026-07-05 17:18:09 +01:00

658 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using RyzStudio.IO;
using RyzStudio.Runtime.InteropServices;
using RyzStudio.Windows.Forms;
namespace RandomFileRunner
{
public partial class MainForm : Form
{
private readonly FileSearcher _fileSearcher;
private CancellationTokenSource _cancellationToken = new CancellationTokenSource();
private List<string> _foundFiles = new List<string>();
private Process _currentProcess = null;
public MainForm()
{
InitializeComponent();
_fileSearcher = new FileSearcher();
_fileSearcher.OnDirectoryFound += fileSearcher_OnDirectoryFound;
_fileSearcher.OnFileFound += fileSearcher_OnFileFound;
_fileSearcher.OnSearchCompleted += fileSearcher_OnSearchCompleted;
memoBox1.WordWrap = false;
memoBox1.ScrollBars = ScrollBars.Vertical;
}
protected async override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.ClearSession();
var args = this.GetCommandLine();
string jsonfigFilename = null;
if (!args.TryGetValue("o", out jsonfigFilename))
{
if (!args.TryGetValue("open", out jsonfigFilename))
{
jsonfigFilename = null;
}
}
if (string.IsNullOrWhiteSpace(jsonfigFilename))
{
jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig");
}
if (System.IO.File.Exists(jsonfigFilename))
{
await LoadSessionFile(jsonfigFilename);
UIControl.Invoke(button1, (x) => button1.Enabled = (this.CurrentSession.SearchItems.Count > 0));
}
}
protected async override void OnShown(EventArgs e)
{
base.OnShown(e);
memoBox1.Focus();
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (this.IsBusy)
{
e.Cancel = true;
return;
}
if (this.CurrentSession.ClosePrevOnNext)
{
CloseCurrentProcess(_currentProcess);
}
if (this.CurrentSession.NextHotKey != null)
{
if (this.CurrentSession.NextHotKey.KeyCode != Keys.None)
{
//#if !DEBUG
User32.UnregisterHotKey((IntPtr)Handle, 1);
//#endif
}
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case User32.WM_HOTKEY:
if (m.WParam.ToInt32() == 1)
{
dialogFooter1_OnButton1Click(null, null);
}
break;
//case WM_QUERYENDSESSION:
// requestExit = true;
// //this.Close();
// Application.Exit();
// break;
default:
break;
}
base.WndProc(ref m);
}
public bool IsBusy
{
get => field;
private set
{
field = value;
UIControl.Invoke(pictureBox1, (x) => pictureBox1.Image = (value ? RyzStudio.Resources.T3Resource.loading_block : null));
UIControl.Invoke(textBox1, (x) => textBox1.Enabled = !value);
UIControl.Invoke(button1, (x) => button1.LabelText = (value ? "&Cancel" : "&Search"));
UIControl.Invoke(dialogFooter1, (x) => dialogFooter1.Button1Visible = !value);
}
}
public AppSession CurrentSession
{
get
{
if (field == null) field = new AppSession();
return field;
}
private set => field = value;
} = new AppSession();
private List<string> SearchPathList
{
get
{
return (memoBox1.Lines?.ToList() ?? new List<string>()).Where(x => !string.IsNullOrWhiteSpace(x)).ToList();
}
}
private void menuStrip1_MenuActivate(object sender, EventArgs e)
{
saveAsToolStripMenuItem.Enabled = (!this.IsBusy && (this.SearchPathList.Count > 0));
toolStripMenuItem2.Enabled = (!this.IsBusy && (_foundFiles.Count > 0));
}
#region Main Menu
/// <summary>
/// New
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void newToolStripMenuItem_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
if (this.IsBusy)
{
return;
}
if (this.CurrentSession.ClosePrevOnNext)
{
CloseCurrentProcess(_currentProcess);
}
ClearSession();
});
}
/// <summary>
/// Open
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void openToolStripMenuItem_Click(object sender, EventArgs e)
{
if (this.IsBusy)
{
return;
}
if (openFileDialog2.ShowDialog() == DialogResult.OK)
{
await LoadSessionFile(openFileDialog2.FileName);
}
}
/// <summary>
/// Save as
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (this.IsBusy)
{
return;
}
saveFileDialog1.Title = "Save session";
saveFileDialog1.Filter = "Session files (*.jsonfig)|*.jsonfig";
saveFileDialog1.DefaultExt = "jsonfig";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
await SaveSessionFile(saveFileDialog1.FileName);
}
}
/// <summary>
/// Export file list
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void toolStripMenuItem2_Click(object sender, EventArgs e)
{
if (this.IsBusy)
{
return;
}
if (_foundFiles.Count <= 0)
{
return;
}
saveFileDialog1.Title = "Save File List";
saveFileDialog1.Filter = "Text files (*.txt)|*.txt";
saveFileDialog1.DefaultExt = "txt";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
await System.IO.File.WriteAllTextAsync(saveFileDialog1.FileName, string.Join(Environment.NewLine, _foundFiles.ToArray()));
}
catch (Exception exc)
{
RyzStudio.Forms.T3MessageBox.Show(this, exc.Message, "Save File List", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// Close
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void exitToolStripMenuItem2_Click(object sender, EventArgs e)
{
this.Close();
}
/// <summary>
/// Export File List
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void toolStripMenuItem1_Click(object sender, EventArgs e)
{
}
/// <summary>
/// Options
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (this.IsBusy)
{
return;
}
var optionsForm = new OptionsForm(this.CurrentSession);
if (optionsForm.ShowDialog() == DialogResult.OK)
{
this.CurrentSession = optionsForm.Result;
InvalidateHotKey();
}
}
/// <summary>
/// Help
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void viewHelpToolStripMenuItem1_Click(object sender, EventArgs e)
{
RyzStudio.Diagnostics.Process.Execute(AppResource.AppHelpURL);
}
/// <summary>
/// About
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void aboutToolStripMenuItem1_Click(object sender, EventArgs e)
{
var form = new RyzStudio.Forms.T3AboutForm();
form.ProductURL = AppResource.AppProductURL;
form.AuthorURL = AppResource.AppAuthorURL;
form.CompanyURL = AppResource.AppCompanyURL;
form.ProductCopyrightStartYear = 2021;
form.ProductLogo = AppResource.icon_64;
form.ShowDialog();
}
#endregion
#region Search Engine
private async Task fileSearcher_OnFileFound(FileSearcher sender, string searchPath, string fileName)
{
await Task.Run(() =>
{
_foundFiles.Add(fileName);
UIControl.Invoke(label3, (x) => label3.Text = Path.GetFileName(fileName));
progressBar2.Maximum = (sender.FileCount + sender.NoAccessFileCount);
progressBar2.Value = sender.FileCount;
});
}
private async Task fileSearcher_OnDirectoryFound(FileSearcher sender, string searchPath, string directoryName)
{
await Task.Run(() =>
{
UIControl.Invoke(label4, (x) => label4.Text = Path.GetFileName(directoryName));
progressBar1.Maximum = sender.DirectoryCount;
progressBar1.Value = sender.BufferLevel;
});
}
private async Task fileSearcher_OnSearchCompleted(FileSearcher sender, TimeSpan elapsedTime)
{
await Task.Run(() =>
{
if (!_cancellationToken.IsCancellationRequested)
{
UIControl.Invoke(label4, (x) => label4.Text = $"Done ({Math.Floor(elapsedTime.TotalMinutes)}m {elapsedTime.Seconds}s)");
}
else
{
UIControl.Invoke(label4, (x) => label4.Text = $"Cancelled ({Math.Floor(elapsedTime.TotalMinutes)}m {elapsedTime.Seconds}s)");
_cancellationToken = new CancellationTokenSource();
}
UIControl.Invoke(label3, (x) => label3.Text = ((_foundFiles.Count <= 0) ? "0" : _foundFiles.Count.ToString("#,#", System.Globalization.CultureInfo.CurrentCulture)) + " File" + ((_foundFiles.Count == 1) ? "" : "s") + " Found");
progressBar1.Value = sender.BufferLevel;
progressBar2.Value = sender.FileCount;
UIControl.Invoke(dialogFooter1, (x) => dialogFooter1.Button1Visible = (_foundFiles.Count > 0));
this.IsBusy = false;
});
}
#endregion
/// <summary>
/// Search
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button2_MouseClick(object sender, MouseEventArgs e)
{
if (this.IsBusy)
{
button1.LabelText = "&Cancelling...";
_cancellationToken.Cancel();
return;
}
this.IsBusy = true;
progressBar1.ShowText = true;
progressBar2.ShowText = true;
_foundFiles = new List<string>();
_cancellationToken = new CancellationTokenSource();
_currentProcess = null;
var paths = this.SearchPathList;
_fileSearcher.FileSearchPattern = textBox1.Text;
_fileSearcher.SearchPath = new List<string>();
UIControl.Invoke(label4, (x) => label4.Text = $"Peeking...");
UIControl.Invoke(label3, (x) => label3.Text = "");
await Task.Run(() =>
{
foreach (var item in paths)
{
if (_cancellationToken.IsCancellationRequested)
{
break;
}
if (System.IO.File.Exists(item))
{
_foundFiles.Add(item);
UIControl.Invoke(label3, (x) => label3.Text = item);
continue;
}
if (System.IO.Directory.Exists(item))
{
_fileSearcher.SearchPath.Add(item);
UIControl.Invoke(label3, (x) => label3.Text = item);
continue;
}
}
UIControl.Invoke(label4, (x) => label4.Text = $"Peeking... {paths.Count}");
UIControl.Invoke(label3, (x) => label3.Text = ((_foundFiles.Count <= 0) ? "0" : _foundFiles.Count.ToString("#,#", System.Globalization.CultureInfo.CurrentCulture)) + " File" + ((_foundFiles.Count == 1) ? "" : "s") + " Found");
});
if (_fileSearcher.SearchPath.Count <= 0)
{
UIControl.Invoke(label4, (x) => label4.Text = $"Done");
UIControl.Invoke(label3, (x) => label3.Text = ((_foundFiles.Count <= 0) ? "0" : _foundFiles.Count.ToString("#,#", System.Globalization.CultureInfo.CurrentCulture)) + " File" + ((_foundFiles.Count == 1) ? "" : "s") + " Found");
}
else
{
UIControl.Invoke(label4, (x) => label4.Text = $"Searching...");
await _fileSearcher.Search(_cancellationToken.Token);
}
//this.IsBusy = false;
}
/// <summary>
/// Close
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_MouseClick(object sender, MouseEventArgs e)
{
this.Close();
}
private void CloseCurrentProcess(Process p)
{
if (p == null)
{
return;
}
try
{
p.CloseMainWindow();
}
catch (Exception)
{
// do nothing
}
}
private void InvalidateHotKey()
{
//#if !DEBUG
UIControl.Invoke(this, (x) =>
{
User32.UnregisterHotKey((IntPtr)Handle, 1);
});
//#endif
if (this.CurrentSession.NextHotKey != null)
{
if (this.CurrentSession.NextHotKey.KeyCode != Keys.None)
{
//#if !DEBUG
UIControl.Invoke(this, (x) =>
{
User32.RegisterHotKey((IntPtr)Handle, 1, this.CurrentSession.NextHotKey.ModifierCode, this.CurrentSession.NextHotKey.Key);
});
//#endif
}
}
}
private void ClearSession()
{
_foundFiles = new List<string>();
_currentProcess = null;
UIControl.Invoke(textBox1, (_) => textBox1.Text = "*.*");
UIControl.Invoke(memoBox1, (_) => memoBox1.Clear());
progressBar1.Clear(0);
progressBar2.Clear(0);
UIControl.Invoke(label4, (x) => label4.Text = "");
UIControl.Invoke(label3, (x) => label3.Text = "");
UIControl.Invoke(button1, (x) => button1.Enabled = false);
UIControl.Invoke(dialogFooter1, (x) => dialogFooter1.Button1Visible = false);
}
protected async Task LoadSessionFile(string filename)
{
await Task.Run(async () =>
{
this.CurrentSession = await RyzStudio.Text.Json.JsonSerialiser.DeserialiseFile<AppSession>(filename);
ClearSession();
textBox1.Text = (string.IsNullOrWhiteSpace(this.CurrentSession.SearchFilePattern) ? "*" : this.CurrentSession.SearchFilePattern?.Trim());
foreach (var item in this.CurrentSession?.SearchItems)
{
if (string.IsNullOrWhiteSpace(item))
{
continue;
}
UIControl.Invoke(memoBox1, (_) => memoBox1.AddLine(item));
}
UIControl.Invoke(label4, (x) => label4.Text = "Ready");
UIControl.Invoke(button1, (x) => button1.Enabled = (this.CurrentSession?.SearchItems.Count > 0));
// hotkey
InvalidateHotKey();
});
}
protected async Task SaveSessionFile(string filename)
{
if (string.IsNullOrWhiteSpace(filename))
{
return;
}
if (this.CurrentSession == null)
{
this.CurrentSession = new AppSession();
}
await Task.Run(() =>
{
this.CurrentSession.SearchFilePattern = textBox1.Text;
this.CurrentSession.SearchItems = this.SearchPathList;
var result = RyzStudio.Text.Json.JsonSerialiser.SerialiseFile(filename, this.CurrentSession);
if (result.Success)
{
RyzStudio.Forms.T3MessageBox.Show(this, "Session saved!", "Save session");
}
else
{
RyzStudio.Forms.T3MessageBox.Show(this, result.Message, "Save session", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
});
}
/// <summary>
/// Run Next
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void dialogFooter1_OnButton1Click(object sender, MouseEventArgs e)
{
if (this.IsBusy)
{
return;
}
if (_foundFiles.Count <= 0)
{
return;
}
await Task.Run(() =>
{
this.IsBusy = true;
if (this.CurrentSession.ClosePrevOnNext)
{
CloseCurrentProcess(_currentProcess);
}
string filename = null;
// retry 8 times
for (int i = 0; i < this.CurrentSession.RetryOnError; i++)
{
filename = _foundFiles[Random.Shared.Next(0, (_foundFiles.Count - 1))];
if (System.IO.File.Exists(filename))
{
continue;
}
filename = null;
}
if (!string.IsNullOrWhiteSpace(filename))
{
_currentProcess = RyzStudio.Diagnostics.Process.Execute(filename);
}
this.IsBusy = false;
});
}
/// <summary>
/// Close
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dialogFooter1_OnButton2Click(object sender, MouseEventArgs e)
{
this.Close();
}
private void memoBox1_TextChanged(object sender, EventArgs e)
{
UIControl.Invoke(button1, (x) => button1.Enabled = !string.IsNullOrWhiteSpace(memoBox1.Text));
}
}
}