using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using MmdPdf.Services; namespace MmdPdf.Features.Scan { /// /// Asks the copier for pages. The person picks the machine, the feed, how much ink and how /// much detail; everything else is the copier's own default. The pages come back as image /// files which MainWindow turns into one document. /// /// A run cannot be interrupted mid-sheet - that is the copier's hardware, not a decision made /// here - so Cancel stops after the sheet in progress and keeps the pages already read. /// public partial class ScanDialog : Window { private readonly List _scanners; private CancellationTokenSource? _cancel; private bool _running; /// Image files, one per page, in the order the copier read them. internal string[] Pages { get; private set; } = Array.Empty(); /// The person asked for the words to be made searchable afterwards. internal bool MakeSearchable { get; private set; } /// The person asked for crooked pages to be straightened afterwards. internal bool Straighten { get; private set; } public ScanDialog() { InitializeComponent(); _scanners = ScanService.IsAvailable ? ScanService.ListScanners() : new List(); foreach (var s in _scanners) CopierBox.Items.Add(s.Name); if (_scanners.Count > 0) { CopierBox.SelectedIndex = 0; } else { CopierBox.Items.Add(ScanService.IsAvailable ? "No copier found on this machine" : "This machine has no imaging service"); CopierBox.SelectedIndex = 0; CopierBox.IsEnabled = false; ScanBtn.IsEnabled = false; FootNote.Text = "Switch the copier on, or connect it, then open this again"; } FeedBox.Items.Add("Feeder, one side"); FeedBox.Items.Add("Feeder, both sides"); FeedBox.Items.Add("Glass, one sheet"); FeedBox.SelectedIndex = 0; ColourBox.Items.Add("Black and white"); ColourBox.Items.Add("Shades of grey"); ColourBox.Items.Add("Colour"); ColourBox.SelectedIndex = 0; DetailBox.Items.Add("200 dpi, quick"); DetailBox.Items.Add("300 dpi, good for text"); DetailBox.Items.Add("600 dpi, fine detail"); DetailBox.SelectedIndex = 1; foreach (var p in ScanService.PaperSizes) PaperBox.Items.Add(p); PaperBox.SelectedIndex = 0; } private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (e.ButtonState == MouseButtonState.Pressed) DragMove(); } private void Cancel_Click(object sender, RoutedEventArgs e) { if (_running) { _cancel?.Cancel(); FootNote.Text = "Stopping after this sheet…"; return; } DialogResult = false; Close(); } private async void Scan_Click(object sender, RoutedEventArgs e) { if (_running || _scanners.Count == 0) return; var device = _scanners[Math.Max(0, CopierBox.SelectedIndex)]; var feed = FeedBox.SelectedIndex switch { 1 => ScanFeed.FeederBothSides, 2 => ScanFeed.Flatbed, _ => ScanFeed.FeederOneSide, }; var colour = ColourBox.SelectedIndex switch { 1 => ScanColour.Greyscale, 2 => ScanColour.Colour, _ => ScanColour.BlackAndWhite, }; int dpi = DetailBox.SelectedIndex switch { 0 => 200, 2 => 600, _ => 300, }; string paper = PaperBox.SelectedItem as string ?? "A4 portrait"; MakeSearchable = SearchableBox.IsChecked == true; Straighten = StraightenBox.IsChecked == true; _running = true; _cancel = new CancellationTokenSource(); ScanBtn.IsEnabled = false; CancelBtn.Content = "Stop"; SetInputsEnabled(false); FootNote.Text = feed == ScanFeed.Flatbed ? "Reading the sheet on the glass…" : "Reading…"; var progress = new Progress(n => FootNote.Text = n == 1 ? "1 page read" : n.ToString(CultureInfo.InvariantCulture) + " pages read"); try { var token = _cancel.Token; var reporter = (IProgress)progress; string[] pages = await Task.Run(() => ScanService.Acquire(device.DeviceId, feed, colour, dpi, paper, maxPages: feed == ScanFeed.Flatbed ? 1 : 200, cancel: token, onPage: reporter.Report).ToArray(), token); Pages = pages; if (pages.Length == 0) { FootNote.Text = "The copier returned no pages"; ResetAfterRun(); return; } DialogResult = true; Close(); } catch (OperationCanceledException) { FootNote.Text = "Stopped"; ResetAfterRun(); } catch (Exception ex) { FootNote.Text = "The copier could not be used: " + ex.Message; ResetAfterRun(); } } private void ResetAfterRun() { _running = false; _cancel?.Dispose(); _cancel = null; ScanBtn.IsEnabled = _scanners.Count > 0; CancelBtn.Content = "Cancel"; SetInputsEnabled(true); } private void SetInputsEnabled(bool on) { CopierBox.IsEnabled = on && _scanners.Count > 0; FeedBox.IsEnabled = on; ColourBox.IsEnabled = on; DetailBox.IsEnabled = on; PaperBox.IsEnabled = on; SearchableBox.IsEnabled = on; StraightenBox.IsEnabled = on; } } }