529 lines
20 KiB
C#
529 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using CarManagerV3.Forms;
|
|
|
|
namespace CarManagerV3
|
|
{
|
|
public partial class MainForm : Form
|
|
{
|
|
List<Car> cars = new List<Car>();
|
|
string filepath = "";
|
|
|
|
public MainForm(string pathToOpen = "")
|
|
{
|
|
InitializeComponent();
|
|
|
|
// Open the most recent file if it exists. Otherwise, use default filepath.
|
|
List<string> recentFiles = SafeManager.GetRecentPaths();
|
|
if (!String.IsNullOrEmpty(pathToOpen))
|
|
{
|
|
filepath = pathToOpen;
|
|
SafeManager.AddRecentPath(filepath);
|
|
}
|
|
else if (recentFiles.Count > 0)
|
|
{
|
|
filepath = recentFiles[0];
|
|
}
|
|
else
|
|
{
|
|
openWelcomeScreen();
|
|
}
|
|
|
|
SafeManager.InitializeFile(filepath);
|
|
StateManager.setFilePath(filepath);
|
|
try
|
|
{
|
|
List<Car> _cars = SafeManager.ReadCars(filepath);
|
|
cars = _cars;
|
|
refreshCars(_cars, false);
|
|
}
|
|
catch (LegacyException)
|
|
{
|
|
Console.Error.WriteLine("Legacy file format detected. Prompting user to select a new file.");
|
|
showOpenFileDialog();
|
|
}
|
|
|
|
refreshRecents();
|
|
|
|
}
|
|
|
|
public void openWelcomeScreen()
|
|
{
|
|
Welcome welcome = new Welcome();
|
|
// disable main form while welcome screen is open
|
|
this.Enabled = false;
|
|
welcome.OpenFileCallback = () =>
|
|
{
|
|
showOpenFileDialog();
|
|
if (filepath != "")
|
|
{
|
|
welcome.Close();
|
|
this.Enabled = true;
|
|
}
|
|
};
|
|
welcome.NewFileCallback = () =>
|
|
{
|
|
showSaveAsDialog();
|
|
if (filepath != "")
|
|
{
|
|
welcome.Close();
|
|
this.Enabled = true;
|
|
}
|
|
};
|
|
|
|
welcome.ShowDialog();
|
|
|
|
}
|
|
|
|
public void showOpenFileDialog()
|
|
{
|
|
openToolStripMenuItem.PerformClick();
|
|
}
|
|
|
|
public void showSaveAsDialog()
|
|
{
|
|
saveAsToolStripMenuItem.PerformClick();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Refreshes the cars displayed in the flow layout panel.
|
|
/// </summary>
|
|
/// <param name="_cars">The cars.</param>
|
|
/// <param name="updateGlobal">if set to <c>true</c> [update global].</param>
|
|
private async void refreshCars(List<Car> _cars, bool updateGlobal = true)
|
|
{
|
|
|
|
this.Text = "Car Manager - " + System.IO.Path.GetFileName(filepath);
|
|
|
|
// Sort by Car.Order. If equal, sort by ID
|
|
_cars = _cars.Count > 0 ? _cars.OrderBy(c => c.Order).ToList() : _cars;
|
|
|
|
if (updateGlobal)
|
|
{
|
|
cars = _cars;
|
|
}
|
|
|
|
foreach (Car car in _cars)
|
|
{
|
|
// not in list? add it
|
|
bool isNew = flpCars.Controls.OfType<CarCard>().All(c => c.Car.Id != car.Id);
|
|
// existing but changed? update it
|
|
CarCard card = new CarCard();
|
|
if (!isNew)
|
|
{
|
|
CarCard existing = flpCars.Controls.OfType<CarCard>().First(c => c.Car.Id == car.Id);
|
|
Car existingCar = existing.Car;
|
|
if (existing == null)
|
|
{
|
|
Console.Error.WriteLine($"[L] Error: Existing car card not found for car ID: {car.Id}");
|
|
continue;
|
|
}
|
|
// compare details
|
|
// Console.WriteLine($"[L] Checking car: {car.Id} | Car Color: {car.Color} | Ex Color: {existingCar.Color}");
|
|
if (existingCar.IsChanged(car))
|
|
{
|
|
Console.WriteLine($"[L] Updating car: {car.Id}");
|
|
// changes
|
|
card = existing;
|
|
|
|
}
|
|
else
|
|
{
|
|
// no changes
|
|
// Console.WriteLine($"[L] No changes for car: {car.Id}");
|
|
flpCars.Controls.SetChildIndex(existing, _cars.IndexOf(car));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
card.CarName = $"{car.Make} {car.Model}";
|
|
card.CarDetails = $"({car.Order}) {car.Year}, {car.Mileage} km, ${car.Price}";
|
|
card.Car = car.Clone();
|
|
card.LoadImage();
|
|
// clear existing event handlers to prevent multiple subscriptions
|
|
card.ClearCardClickedHandlers();
|
|
card.CardClicked += (s, e) =>
|
|
{
|
|
Console.WriteLine($"Card clicked: {car.Id}");
|
|
CarDetailsForm detailsForm = new CarDetailsForm(car);
|
|
detailsForm.FormClosed += async (s2, e2) =>
|
|
{
|
|
Console.WriteLine("Car details form closed.");
|
|
// refresh cars
|
|
Console.WriteLine("Refreshing cars...");
|
|
List<Car> __cars = await Task.Run(() => SafeManager.ReadCars(filepath));
|
|
if (tbxSearch.Text.Length > 0)
|
|
{
|
|
Console.WriteLine("Search box has text, applying search filter.");
|
|
cars = __cars;
|
|
searchList(tbxSearch.Text);
|
|
return;
|
|
}
|
|
refreshCars(__cars);
|
|
|
|
};
|
|
detailsForm.ShowDialog();
|
|
};
|
|
|
|
ContextMenuStrip cms = new ContextMenuStrip();
|
|
cms.Items.Add("Move up", null, (s, e) =>
|
|
{
|
|
int order = car.Order;
|
|
// find car with order just less than this one
|
|
Car other = cars.Where(c => c.Order < order).OrderByDescending(c => c.Order).FirstOrDefault();
|
|
if (other != null)
|
|
{
|
|
Console.WriteLine($"Swapping order of {car.ToString()} ({car.Order}) and {other.ToString()} ({other.Order})");
|
|
int temp = car.Order;
|
|
car.Order = other.Order;
|
|
other.Order = temp;
|
|
cars = StateManager.normalizeOrders(cars);
|
|
SafeManager.SaveCars(filepath, cars);
|
|
refreshCars(cars);
|
|
}
|
|
});
|
|
|
|
cms.Items.Add("Move down", null, (s, e) =>
|
|
{
|
|
int order = car.Order;
|
|
// find car with order just greater than this one
|
|
Car other = cars.Where(c => c.Order > order).OrderBy(c => c.Order).FirstOrDefault();
|
|
if (other != null)
|
|
{
|
|
Console.WriteLine($"Swapping order of {car.ToString()} ({car.Order}) and {other.ToString()} ({other.Order})");
|
|
int temp = car.Order;
|
|
car.Order = other.Order;
|
|
other.Order = temp;
|
|
cars = StateManager.normalizeOrders(cars);
|
|
SafeManager.SaveCars(filepath, cars);
|
|
refreshCars(cars);
|
|
}
|
|
});
|
|
|
|
card.ContextMenuStrip = cms;
|
|
|
|
if (isNew)
|
|
{
|
|
flpCars.Controls.Add(card);
|
|
}
|
|
flpCars.Controls.SetChildIndex(card, _cars.IndexOf(car));
|
|
}
|
|
|
|
// Remove cards that are no longer in _cars
|
|
var carIds = _cars.Select(c => c.Id).ToList();
|
|
var cardsToRemove = flpCars.Controls.OfType<CarCard>().Where(c => !carIds.Contains(c.Car.Id)).ToList();
|
|
foreach (var card in cardsToRemove)
|
|
{
|
|
flpCars.Controls.Remove(card);
|
|
}
|
|
|
|
flpCars.Refresh();
|
|
flpCars.Invalidate();
|
|
flpCars.Update();
|
|
}
|
|
|
|
|
|
private void btnNewCar_Click(object sender, EventArgs e)
|
|
{
|
|
Car foocar = new Car("0", "New", "Car", 2020, "White", 0, 20000);
|
|
CarDetailsForm detailsForm = new CarDetailsForm(foocar);
|
|
detailsForm.FormClosed += (s2, e2) =>
|
|
{
|
|
// refresh cars
|
|
Console.WriteLine("Refreshing cars...");
|
|
List<Car> cars_ = SafeManager.ReadCars(filepath);
|
|
refreshCars(cars_, false);
|
|
};
|
|
detailsForm.ShowDialog();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters the cars by a search query.
|
|
/// </summary>
|
|
/// <param name="query">The search query.</param>
|
|
/// <returns>
|
|
/// A list of <see cref="Car"/> objects that match the query.
|
|
/// </returns>
|
|
List<Car> filterCarsByQuery(string query)
|
|
{
|
|
List<Car> results = new List<Car>();
|
|
foreach (Car car in cars)
|
|
{
|
|
if (car.Make.ToLower().Contains(query.ToLower()) || car.Model.ToLower().Contains(query.ToLower()) || car.Year.ToString().Contains(query) || car.Mileage.ToString().Contains(query) || car.Price.ToString().Contains(query))
|
|
{
|
|
results.Add(car);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Searches the list of cars by a query and refreshes the display.
|
|
/// </summary>
|
|
/// <param name="query">The query.</param>
|
|
void searchList(string query)
|
|
{
|
|
List<Car> results = filterCarsByQuery(query);
|
|
refreshCars(results, false);
|
|
}
|
|
|
|
private async void tbxSearch_TextChanged(object sender, EventArgs e)
|
|
{
|
|
string query = tbxSearch.Text;
|
|
await Task.Delay(100); // debounce
|
|
if (query != tbxSearch.Text) return; // text changed during delay
|
|
//flpCars.Controls.Clear();
|
|
if (string.IsNullOrWhiteSpace(query))
|
|
{
|
|
refreshCars(cars);
|
|
}
|
|
else
|
|
{
|
|
searchList(query);
|
|
}
|
|
}
|
|
|
|
private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
private void openToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
OpenFileDialog dlgOpen = new OpenFileDialog();
|
|
dlgOpen.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
|
|
dlgOpen.Title = "Open Car Data File";
|
|
// Default to users documents
|
|
dlgOpen.InitialDirectory = SafeManager.getRecentFolder();
|
|
|
|
DialogResult result = dlgOpen.ShowDialog();
|
|
if (result == DialogResult.OK)
|
|
{
|
|
try
|
|
{
|
|
List<Car> importedCars = SafeManager.ReadCars(dlgOpen.FileName);
|
|
if (importedCars.Count == 0)
|
|
{
|
|
throw new Exception("File doesn't contain valid Cars.");
|
|
}
|
|
filepath = dlgOpen.FileName;
|
|
|
|
cars = importedCars;
|
|
StateManager.setFilePath(filepath);
|
|
// Refresh display
|
|
refreshCars(cars);
|
|
MessageBox.Show("File loaded successfully.", "Load File", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
SafeManager.AddRecentPath(filepath);
|
|
refreshRecents();
|
|
}
|
|
catch (LegacyException)
|
|
{
|
|
showOpenFileDialog();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show("Error loading file: " + ex.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
refreshCars(cars);
|
|
SafeManager.SaveCars(filepath, cars);
|
|
MessageBox.Show("File saved successfully.", "Save File", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
|
|
private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
SaveFileDialog dlgSave = new SaveFileDialog();
|
|
dlgSave.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
|
|
dlgSave.Title = "Save Car Data File As";
|
|
// Default to users documents
|
|
dlgSave.InitialDirectory = SafeManager.getRecentFolder();
|
|
DialogResult result = dlgSave.ShowDialog();
|
|
|
|
if (result == DialogResult.OK)
|
|
{
|
|
// does file already exist?
|
|
/*if (System.IO.File.Exists(dlgSave.FileName))
|
|
{
|
|
var overwriteResult = MessageBox.Show("File already exists. Overwrite?", "Overwrite File", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
|
if (overwriteResult != DialogResult.Yes)
|
|
{
|
|
return;
|
|
}
|
|
}*/
|
|
// Windows already handles this lmao
|
|
filepath = dlgSave.FileName;
|
|
this.Text = "Car Manager - " + System.IO.Path.GetFileName(filepath);
|
|
StateManager.setFilePath(filepath);
|
|
SafeManager.SaveCars(filepath, cars);
|
|
SafeManager.AddRecentPath(filepath);
|
|
}
|
|
refreshRecents();
|
|
}
|
|
|
|
private void importToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
DialogResult result = MessageBox.Show("Importing will add cars from another file to this file. This action cannot be undone. Continue?", "Import Cars", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
|
if (result == DialogResult.Yes)
|
|
{
|
|
OpenFileDialog dlgOpen = new OpenFileDialog();
|
|
dlgOpen.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
|
|
dlgOpen.Title = "Import Car Data File";
|
|
// Default to users documents
|
|
dlgOpen.InitialDirectory = SafeManager.getRecentFolder();
|
|
DialogResult dlgResult = dlgOpen.ShowDialog();
|
|
if (dlgResult == DialogResult.OK)
|
|
{
|
|
try
|
|
{
|
|
Console.WriteLine("Starting merge...");
|
|
List<Car> importedCars = SafeManager.ReadCars(dlgOpen.FileName);
|
|
if (importedCars.Count == 0)
|
|
{
|
|
throw new Exception("File doesn't contain valid Cars.");
|
|
}
|
|
// merge cars
|
|
foreach (Car car in importedCars)
|
|
{
|
|
cars.Add(car);
|
|
}
|
|
DialogResult mergeAsNewFileResult = MessageBox.Show("Do you want to save the merged cars as a new file?", "Save As New File", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
|
if (mergeAsNewFileResult == DialogResult.Yes)
|
|
{
|
|
SaveFileDialog dlgSave = new SaveFileDialog();
|
|
dlgSave.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
|
|
dlgSave.Title = "Save Merged Car Data File As";
|
|
// Default to users documents
|
|
dlgSave.InitialDirectory = SafeManager.getRecentFolder();
|
|
DialogResult saveResult = dlgSave.ShowDialog();
|
|
if (saveResult == DialogResult.OK)
|
|
{
|
|
filepath = dlgSave.FileName;
|
|
StateManager.setFilePath(filepath);
|
|
SafeManager.SaveCars(filepath, cars);
|
|
SafeManager.AddRecentPath(filepath);
|
|
refreshRecents();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// save to current file
|
|
SafeManager.SaveCars(filepath, cars);
|
|
}
|
|
// Refresh display
|
|
refreshCars(cars);
|
|
MessageBox.Show("File imported successfully.", "Import File", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
catch (LegacyException)
|
|
{
|
|
MessageBox.Show("The file you are trying to import is in a legacy format that is no longer supported. Please convert the file to the new format and try again.", "Import Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show("Error importing file: " + ex.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("Import cancelled.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Unbind and remove this.
|
|
private void recentFilesToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refreshes the recently opened files menu.
|
|
/// </summary>
|
|
private void refreshRecents()
|
|
{
|
|
recentFilesToolStripMenuItem.DropDownItems.Clear();
|
|
List<string> recentFiles = SafeManager.GetRecentPaths();
|
|
recentFilesToolStripMenuItem.Enabled = recentFiles.Count > 0;
|
|
recentFilesToolStripMenuItem.ToolTipText = recentFiles.Count > 0 ? "" : "No recent files.";
|
|
foreach (string path in recentFiles)
|
|
{
|
|
ToolStripMenuItem item = new ToolStripMenuItem(path);
|
|
item.Click += (s, e2) =>
|
|
{
|
|
try
|
|
{
|
|
List<Car> importedCars = SafeManager.ReadCars(path);
|
|
if (importedCars.Count == 0)
|
|
{
|
|
throw new Exception("File doesn't contain valid Cars.");
|
|
}
|
|
filepath = path;
|
|
cars = importedCars;
|
|
StateManager.setFilePath(filepath);
|
|
// Refresh display
|
|
refreshCars(cars);
|
|
MessageBox.Show("File loaded successfully.", "Load File", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
catch (LegacyException)
|
|
{
|
|
MessageBox.Show("The file you are trying to open is in a legacy format that is no longer supported. Please convert the file to the new format and try again.", "Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show("Error loading file: " + ex.Message);
|
|
}
|
|
};
|
|
recentFilesToolStripMenuItem.DropDownItems.Add(item);
|
|
|
|
}
|
|
}
|
|
|
|
private void revealInFileExplorerToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
// Open File Explorer at the location of the current filepath
|
|
if (System.IO.File.Exists(filepath))
|
|
{
|
|
System.Diagnostics.Process.Start("explorer.exe", "/select,\"" + filepath + "\"");
|
|
}
|
|
else
|
|
{
|
|
MessageBox.Show("File does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private void openWelcomeScreenToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
openWelcomeScreen();
|
|
}
|
|
|
|
private void addCarToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
btnNewCar.PerformClick();
|
|
}
|
|
|
|
private void clearSearchToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
tbxSearch.Text = "";
|
|
}
|
|
|
|
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
SafeManager.SaveCars(filepath, cars);
|
|
Environment.Exit(0);
|
|
}
|
|
|
|
private void clearRecentFilesToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
SafeManager.ClearRecentPaths();
|
|
}
|
|
}
|
|
}
|