using System.Diagnostics; using Microsoft.Data.Sqlite; namespace CadSearchGui; public partial class Form1 : Form { private readonly TextBox _txtCerca = new(); private readonly ComboBox _cmbTipo = new(); private readonly ComboBox _cmbCartella = new(); private readonly CheckBox _chkUltima = new(); private readonly Button _btnCerca = new(); private readonly Button _btnApriDb = new(); private readonly Button _btnAggiorna = new(); private readonly DataGridView _grid = new(); private readonly Label _lblStatus = new(); private readonly Label _lblDb = new(); private const string OpzionePrtAsm = "Solo prt/asm (predefinito)"; private const string OpzioneTuttiTipi = "Tutti i tipi"; private static readonly string[] TipiPredefiniti = { "prt", "asm" }; private const string RadiceDiscoDefault = @"X:\"; private string? _dbPath; public Form1() { InitializeComponent(); BuildUi(); Load += (_, _) => InizializzaDatabase(); } private void BuildUi() { var topPanel = new TableLayoutPanel { Dock = DockStyle.Top, Height = 76, ColumnCount = 7, Padding = new Padding(8), RowCount = 2, }; topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40)); topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 15)); topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20)); topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 130)); topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 90)); topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 110)); topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 130)); _txtCerca.Dock = DockStyle.Fill; _txtCerca.Font = new Font(_txtCerca.Font!.FontFamily, 11f); _txtCerca.PlaceholderText = "Cerca per codice o nome file (es. 9231000036)..."; _txtCerca.KeyDown += (_, e) => { if (e.KeyCode == Keys.Enter) { e.SuppressKeyPress = true; EseguiRicerca(); } }; _cmbTipo.Dock = DockStyle.Fill; _cmbTipo.DropDownStyle = ComboBoxStyle.DropDownList; _cmbTipo.Items.Add(OpzionePrtAsm); _cmbTipo.Items.Add(OpzioneTuttiTipi); _cmbCartella.Dock = DockStyle.Fill; _cmbCartella.DropDownStyle = ComboBoxStyle.DropDownList; _cmbCartella.Items.Add("Tutte le cartelle"); _cmbCartella.Items.AddRange(new object[] { "__Accessori", "__B.end", "__Corpi", "__Fondi" }); _cmbCartella.SelectedIndex = 0; _chkUltima.Text = "Solo ultima versione"; _chkUltima.Dock = DockStyle.Fill; _chkUltima.TextAlign = ContentAlignment.MiddleLeft; _chkUltima.Checked = true; _btnCerca.Text = "Cerca"; _btnCerca.Dock = DockStyle.Fill; _btnCerca.Click += (_, _) => EseguiRicerca(); _btnApriDb.Text = "Apri DB..."; _btnApriDb.Dock = DockStyle.Fill; _btnApriDb.Click += (_, _) => ScegliDatabase(); _btnAggiorna.Text = "Aggiorna indice"; _btnAggiorna.Dock = DockStyle.Fill; _btnAggiorna.Click += (_, _) => AggiornaIndice(); topPanel.Controls.Add(_txtCerca, 0, 0); topPanel.Controls.Add(_cmbTipo, 1, 0); topPanel.Controls.Add(_cmbCartella, 2, 0); topPanel.Controls.Add(_chkUltima, 3, 0); topPanel.Controls.Add(_btnCerca, 4, 0); topPanel.Controls.Add(_btnApriDb, 5, 0); topPanel.Controls.Add(_btnAggiorna, 6, 0); topPanel.SetColumnSpan(_lblDb, 7); _lblDb.Dock = DockStyle.Fill; _lblDb.AutoSize = false; _lblDb.ForeColor = SystemColors.GrayText; _lblDb.TextAlign = ContentAlignment.MiddleLeft; topPanel.Controls.Add(_lblDb, 0, 1); _grid.Dock = DockStyle.Fill; _grid.ReadOnly = true; _grid.AllowUserToAddRows = false; _grid.AllowUserToDeleteRows = false; _grid.AllowUserToResizeRows = false; _grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect; _grid.MultiSelect = false; _grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; _grid.CellDoubleClick += (_, e) => ApriCartellaFileSelezionato(); var statusPanel = new Panel { Dock = DockStyle.Bottom, Height = 28 }; _lblStatus.Dock = DockStyle.Fill; _lblStatus.TextAlign = ContentAlignment.MiddleLeft; _lblStatus.Padding = new Padding(8, 0, 0, 0); _lblStatus.Text = "Pronto."; statusPanel.Controls.Add(_lblStatus); Controls.Add(_grid); Controls.Add(statusPanel); Controls.Add(topPanel); } private void InizializzaDatabase() { var candidati = new[] { Path.Combine(AppContext.BaseDirectory, "cad_index.db"), Path.Combine(Directory.GetCurrentDirectory(), "cad_index.db"), }; _dbPath = candidati.FirstOrDefault(File.Exists); if (_dbPath is null) { _lblStatus.Text = "Database non trovato. Usa 'Apri DB...' per selezionarlo."; _lblDb.Text = "Nessun database caricato."; return; } CaricaDatabase(_dbPath); } private void ScegliDatabase() { using var dialog = new OpenFileDialog { Filter = "Database SQLite (*.db)|*.db|Tutti i file (*.*)|*.*", Title = "Seleziona cad_index.db", }; if (dialog.ShowDialog(this) == DialogResult.OK) { CaricaDatabase(dialog.FileName); } } private void CaricaDatabase(string path) { try { using var connection = new SqliteConnection($"Data Source={path};Mode=ReadOnly"); connection.Open(); _cmbTipo.Items.Clear(); _cmbTipo.Items.Add(OpzionePrtAsm); _cmbTipo.Items.Add(OpzioneTuttiTipi); using (var cmd = connection.CreateCommand()) { cmd.CommandText = "SELECT DISTINCT tipo_file FROM files ORDER BY tipo_file"; using var reader = cmd.ExecuteReader(); while (reader.Read()) { _cmbTipo.Items.Add(reader.GetString(0)); } } _cmbTipo.SelectedIndex = 0; long totale = (long)(new SqliteCommand("SELECT COUNT(*) FROM files", connection).ExecuteScalar() ?? 0L); _dbPath = path; _lblDb.Text = $"Database: {path} ({totale:N0} file indicizzati)"; _lblStatus.Text = "Pronto."; } catch (Exception ex) { MessageBox.Show(this, $"Impossibile aprire il database:\n{ex.Message}", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private async void AggiornaIndice() { string dbPath = _dbPath ?? Path.Combine(AppContext.BaseDirectory, "cad_index.db"); if (!Directory.Exists(RadiceDiscoDefault)) { MessageBox.Show(this, $"Disco non raggiungibile: {RadiceDiscoDefault}", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } _btnAggiorna.Enabled = false; _btnCerca.Enabled = false; _lblStatus.Text = "Aggiornamento indice in corso..."; using var cts = new CancellationTokenSource(); using var dialog = new ProgressForm(); var progress = new Progress(dialog.Aggiorna); dialog.Annullato += (_, _) => cts.Cancel(); var scanTask = Indexer.ScanAsync(RadiceDiscoDefault, dbPath, progress, cts.Token); // Chiude automaticamente il dialog quando lo scan termina (successo, errore o annullamento) _ = scanTask.ContinueWith(_ => { if (dialog.Visible) { dialog.BeginInvoke(dialog.Close); } }, TaskScheduler.Default); dialog.ShowDialog(this); try { await scanTask; _lblStatus.Text = "Indice aggiornato."; CaricaDatabase(dbPath); } catch (OperationCanceledException) { _lblStatus.Text = "Aggiornamento annullato."; } catch (Exception ex) { MessageBox.Show(this, $"Errore durante l'aggiornamento dell'indice:\n{ex.Message}", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); _lblStatus.Text = "Aggiornamento fallito."; } finally { _btnAggiorna.Enabled = true; _btnCerca.Enabled = true; } } private void EseguiRicerca() { if (_dbPath is null) { _lblStatus.Text = "Nessun database caricato. Usa 'Apri DB...'."; return; } string testo = _txtCerca.Text.Trim(); string? tipoSelezionato = (string?)_cmbTipo.SelectedItem; string[]? tipi = tipoSelezionato switch { OpzionePrtAsm => TipiPredefiniti, OpzioneTuttiTipi => null, null => TipiPredefiniti, _ => new[] { tipoSelezionato }, }; string? cartella = _cmbCartella.SelectedIndex > 0 ? (string)_cmbCartella.SelectedItem! : null; bool soloUltima = _chkUltima.Checked; if (testo.Length == 0 && tipi is null && cartella is null) { _lblStatus.Text = "Inserisci un termine di ricerca o imposta un filtro."; return; } Cursor = Cursors.WaitCursor; _btnCerca.Enabled = false; try { var (colonne, righe, troncato) = Cerca(_dbPath, testo, soloUltima, cartella, tipi); PopolaGriglia(colonne, righe); _lblStatus.Text = $"{righe.Count} risultat{(righe.Count == 1 ? "o" : "i")}" + (troncato ? " (troncato a 500, affina la ricerca)" : ""); } catch (Exception ex) { MessageBox.Show(this, $"Errore durante la ricerca:\n{ex.Message}", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { Cursor = Cursors.Default; _btnCerca.Enabled = true; } } private static (string[] colonne, List righe, bool troncato) Cerca( string dbPath, string testo, bool soloUltimaVersione, string? cartella, string[]? tipi) { using var connection = new SqliteConnection($"Data Source={dbPath};Mode=ReadOnly"); connection.Open(); string tabella = soloUltimaVersione ? "files_ultima_versione" : "files"; string filtroTipo = "1=1"; if (tipi is { Length: > 0 }) { var nomiParametri = tipi.Select((_, i) => $"@tipo{i}"); filtroTipo = $"tipo_file IN ({string.Join(",", nomiParametri)})"; } var sql = $""" SELECT percorso_completo, cartella_radice, famiglia, codice, tipo_file, versione, dimensione_byte, data_modifica FROM {tabella} WHERE (@testo = '' OR codice LIKE @like OR nome_file LIKE @like) AND (@cartella IS NULL OR cartella_radice = @cartella) AND ({filtroTipo}) ORDER BY codice, tipo_file, versione LIMIT 500 """; using var command = connection.CreateCommand(); command.CommandText = sql; command.Parameters.AddWithValue("@testo", testo); command.Parameters.AddWithValue("@like", $"%{testo}%"); command.Parameters.AddWithValue("@cartella", (object?)cartella ?? DBNull.Value); if (tipi is { Length: > 0 }) { for (int i = 0; i < tipi.Length; i++) { command.Parameters.AddWithValue($"@tipo{i}", tipi[i]); } } using var reader = command.ExecuteReader(); var righe = new List(); while (reader.Read()) { righe.Add(new[] { reader.GetString(0), reader.IsDBNull(2) ? "" : reader.GetString(2), reader.IsDBNull(3) ? "" : reader.GetString(3), reader.IsDBNull(4) ? "" : reader.GetString(4), reader.IsDBNull(5) ? "" : reader.GetInt64(5).ToString(), FormatSize(reader.IsDBNull(6) ? 0 : reader.GetInt64(6)), reader.IsDBNull(7) ? "" : reader.GetString(7), }); } var colonne = new[] { "percorso", "famiglia", "codice", "tipo", "ver", "dimensione", "data modifica" }; return (colonne, righe, righe.Count == 500); } private static string FormatSize(long bytes) { string[] unita = { "B", "KB", "MB", "GB" }; double value = bytes; int i = 0; while (value >= 1024 && i < unita.Length - 1) { value /= 1024; i++; } return $"{value:0.#} {unita[i]}"; } private void PopolaGriglia(string[] colonne, List righe) { _grid.Rows.Clear(); _grid.Columns.Clear(); foreach (var nome in colonne) { _grid.Columns.Add(nome, nome); } foreach (var riga in righe) { _grid.Rows.Add(riga); } if (_grid.Columns.Count > 0) { _grid.Columns[0].FillWeight = 220; } } private void ApriCartellaFileSelezionato() { if (_grid.CurrentRow is null) { return; } string? percorso = _grid.CurrentRow.Cells[0].Value as string; if (string.IsNullOrEmpty(percorso)) { return; } try { Process.Start("explorer.exe", $"/select,\"{percorso}\""); } catch (Exception ex) { MessageBox.Show(this, $"Impossibile aprire Esplora risorse:\n{ex.Message}", "Errore", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }