From c1f798be7aee2a6335ee3557a0d8173aa576c641 Mon Sep 17 00:00:00 2001 From: Grayson Riffe Date: Tue, 21 Jan 2025 03:08:49 -0600 Subject: [PATCH] Add saving and loading --- Mainspring/CMakeLists.txt | 2 +- Mainspring/resource.rc | 12 ++-- Mainspring/src/Application.cpp | 119 ++++++++++++++++++++++++++++----- Mainspring/src/Application.h | 10 ++- Mainspring/src/pch.h | 3 +- Mainspring/src/resources.h | 2 +- Mainspring/src/utility.cpp | 26 +++++++ Mainspring/src/utility.h | 6 ++ 8 files changed, 155 insertions(+), 25 deletions(-) create mode 100644 Mainspring/src/utility.cpp create mode 100644 Mainspring/src/utility.h diff --git a/Mainspring/CMakeLists.txt b/Mainspring/CMakeLists.txt index b099d42..24c94c5 100644 --- a/Mainspring/CMakeLists.txt +++ b/Mainspring/CMakeLists.txt @@ -1,5 +1,5 @@ # Mainspring app CMakeLists.txt -add_executable(Mainspring WIN32 "src/main.cpp" "src/pch.h" "src/Application.h" "src/Application.cpp" "resource.rc" "src/resources.h") +add_executable(Mainspring WIN32 "src/main.cpp" "src/pch.h" "src/Application.h" "src/Application.cpp" "resource.rc" "src/resources.h" "src/utility.h" "src/utility.cpp") set_property(TARGET Mainspring PROPERTY CXX_STANDARD 20) diff --git a/Mainspring/resource.rc b/Mainspring/resource.rc index 433d485..b07997c 100644 --- a/Mainspring/resource.rc +++ b/Mainspring/resource.rc @@ -9,7 +9,7 @@ IDR_MENUMAIN MENU BEGIN POPUP "&File" BEGIN - MENUITEM "&New Time\tCtrl+N" ID_FILE_NEW + MENUITEM "&Reset\tCtrl+R" ID_FILE_RESET MENUITEM "&Save Time\tCtrl+S" ID_FILE_SAVE MENUITEM "Save Time &As...\tCtrl+Shift+S" ID_FILE_SAVEAS MENUITEM "&Open Time\tCtrl+O" ID_FILE_OPEN @@ -23,19 +23,19 @@ BEGIN END END -IDD_DIALOGMAIN DIALOGEX 0, 0, 175, 70 +IDD_DIALOGMAIN DIALOGEX 0, 0, 200, 70 STYLE WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT FONT 8, "MS Shell Dlg", 400, 0, 0x1 MENU IDR_MENUMAIN BEGIN - CTEXT "APPNAME", IDC_STATICTITLE, 30, 0, 115, 20, SS_CENTERIMAGE - CTEXT "TIME", IDC_STATICTIME, 8, 20, 158, 28, SS_CENTERIMAGE | SS_SUNKEN - DEFPUSHBUTTON "STARTPAUSE" IDC_BUTTONSTARTPAUSE, 63, 53, 50, 15 + CTEXT "APPNAME", IDC_STATICTITLE, 0, 0, 200, 20, SS_CENTERIMAGE + CTEXT "TIME", IDC_STATICTIME, 8, 20, 184, 28, SS_CENTERIMAGE | SS_SUNKEN + DEFPUSHBUTTON "STARTPAUSE" IDC_BUTTONSTARTPAUSE, 75, 53, 50, 15 END IDA_ACCELMAIN ACCELERATORS BEGIN - "N", ID_FILE_NEW, CONTROL, VIRTKEY + "R", ID_FILE_RESET, CONTROL, VIRTKEY "S", ID_FILE_SAVE, CONTROL, VIRTKEY "S", ID_FILE_SAVEAS, CONTROL, SHIFT, VIRTKEY "O", ID_FILE_OPEN, CONTROL, VIRTKEY diff --git a/Mainspring/src/Application.cpp b/Mainspring/src/Application.cpp index 9ec88da..a335cc0 100644 --- a/Mainspring/src/Application.cpp +++ b/Mainspring/src/Application.cpp @@ -2,6 +2,11 @@ #include "pch.h" #include "Application.h" #include "resources.h" +#include "utility.h" + +// Define timer IDs +#define IDT_UPDATEAPP 1 +#define IDT_SAVEDTEXT 2 // Define window message to update the timer display #define WM_UPDATEAPP WM_USER + 1 @@ -15,9 +20,13 @@ namespace mainspring { Application::Application(const char* appName, const char* appVersion) : m_appName(appName) , m_appVersion(appVersion) + , m_dlg() , m_timing(false) , m_startpauseButtonPressed(false) , m_elapsedSaved() + , m_startTime() + , m_file() + , m_showSavedText(false) { std::cout << std::format("{} {}\n", m_appName, m_appVersion); } @@ -25,14 +34,14 @@ namespace mainspring { void Application::run() { // Read last time here - HWND dlg = CreateDialogParam(nullptr, MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast(mainDlgProc), reinterpret_cast(this)); + m_dlg = CreateDialogParam(nullptr, MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast(mainDlgProc), reinterpret_cast(this)); HACCEL hAccel = LoadAccelerators(GetModuleHandle(NULL), MAKEINTRESOURCE(IDA_ACCELMAIN)); MSG msg = {}; - ShowWindow(dlg, SW_SHOW); + ShowWindow(m_dlg, SW_SHOW); while (GetMessage(&msg, nullptr, 0, 0)) - if (!TranslateAccelerator(dlg, hAccel, &msg)) - IsDialogMessage(dlg, &msg); + if (!TranslateAccelerator(m_dlg, hAccel, &msg)) + IsDialogMessage(m_dlg, &msg); } BOOL CALLBACK Application::mainDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -52,7 +61,6 @@ namespace mainspring { GetMonitorInfo(MonitorFromPoint(cursor, MONITOR_DEFAULTTONEAREST), &mi); SetWindowPos(dlg, nullptr, mi.rcMonitor.left + 100, mi.rcMonitor.top + 100, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - SetDlgItemText(dlg, IDC_STATICTITLE, app->m_appName); LOGFONT lFont = {}; lFont.lfHeight = 30; HFONT titleFont = CreateFontIndirect(&lFont); @@ -64,7 +72,7 @@ namespace mainspring { SendMessage(GetDlgItem(dlg, IDC_STATICTIME), WM_SETFONT, reinterpret_cast(timeFont), NULL); SendMessage(dlg, WM_UPDATEAPP, NULL, NULL); - SetTimer(dlg, 1, 50, nullptr); + SetTimer(dlg, IDT_UPDATEAPP, 50, nullptr); return TRUE; } @@ -75,23 +83,29 @@ namespace mainspring { app->m_startpauseButtonPressed = true; return TRUE; - case ID_FILE_NEW: { - std::cout << std::format("New!\n"); + case ID_FILE_RESET: { + if (!app->m_file.empty()) + app->save(); + + app->reset(); return TRUE; } case ID_FILE_SAVE: { - std::cout << std::format("Save!\n"); + app->save(); return TRUE; } case ID_FILE_SAVEAS: { - std::cout << std::format("Save As!\n"); + app->save(true); return TRUE; } case ID_FILE_OPEN: { - std::cout << std::format("Open!\n"); + if (!app->m_file.empty()) + app->save(); + + app->open(); return TRUE; } @@ -108,7 +122,18 @@ namespace mainspring { return FALSE; case WM_TIMER: - SendMessage(dlg, WM_UPDATEAPP, NULL, NULL); + switch (wParam) { + case IDT_UPDATEAPP: + SendMessage(dlg, WM_UPDATEAPP, NULL, NULL); + return TRUE; + + case IDT_SAVEDTEXT: + app->m_showSavedText = false; + return TRUE; + } + + return FALSE; + return TRUE; case WM_UPDATEAPP: { @@ -123,22 +148,33 @@ namespace mainspring { app->m_timing = !app->m_timing; } + if (app->m_showSavedText) + SetDlgItemText(dlg, IDC_STATICTITLE, "Saved!"); + + else if (!app->m_file.empty()) { + std::string filename = app->m_file.substr(app->m_file.find_last_of('\\') + 1); + SetDlgItemText(dlg, IDC_STATICTITLE, filename.c_str()); + } + else + SetDlgItemText(dlg, IDC_STATICTITLE, app->m_appName); + if (app->m_timing) SetDlgItemText(dlg, IDC_BUTTONSTARTPAUSE, "Pause"); else SetDlgItemText(dlg, IDC_BUTTONSTARTPAUSE, !app->m_elapsedSaved ? "Start" : "Resume"); - Seconds totalTime = app->m_elapsedSaved + app->getElapsed(); uint32_t hr = totalTime / (3600), min = totalTime % (3600) / 60, sec = totalTime % 60; - SetDlgItemText(dlg, IDC_STATICTIME, std::format("{:02}:{:02}:{:02}", hr, min, sec).c_str()); + SetDlgItemText(dlg, IDC_STATICTIME, std::format("{:03}:{:02}:{:02}", hr, min, sec).c_str()); return TRUE; } case WM_CLOSE: - // TODO: Ask to / save time here + if (!app->m_file.empty()) + app->save(); + KillTimer(dlg, 1); DestroyWindow(dlg); return TRUE; @@ -157,4 +193,57 @@ namespace mainspring { else return 0; } + + void Application::reset() { + m_timing = false; + m_elapsedSaved = 0; + m_file.clear(); + } + + void Application::save(bool saveas) { + if (m_file.empty() || saveas) { + OPENFILENAME ofn = {}; + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = m_dlg; + ofn.lpstrFilter = "Mainspring Time File (*.mspring)\0*.mspring\0\0"; + ofn.lpstrDefExt = "mspring"; + char fileBuf[MAX_PATH] = {}; + ofn.lpstrFile = fileBuf; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_OVERWRITEPROMPT; + + if (GetSaveFileName(&ofn)) + m_file = fileBuf; + else + return; + } + + Seconds totalTime = m_elapsedSaved + getElapsed(); + std::string saveData = std::to_string(totalTime); + writeFile(m_file.c_str(), saveData); + m_showSavedText = true; + SetTimer(m_dlg, IDT_SAVEDTEXT, 3000, nullptr); + } + + void Application::open() { + OPENFILENAME ofn = {}; + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = m_dlg; + ofn.lpstrFilter = "Mainspring Time File (*.mspring)\0*.mspring\0\0"; + ofn.lpstrDefExt = "mspring"; + char fileBuf[MAX_PATH] = {}; + ofn.lpstrFile = fileBuf; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_FILEMUSTEXIST; + + if (GetOpenFileName(&ofn)) + m_file = fileBuf; + else + return; + + std::string openData; + if (readFile(m_file.c_str(), openData)) + // Update status text; + m_elapsedSaved = std::stoull(openData); + } } \ No newline at end of file diff --git a/Mainspring/src/Application.h b/Mainspring/src/Application.h index 88103c1..a9f434e 100644 --- a/Mainspring/src/Application.h +++ b/Mainspring/src/Application.h @@ -17,13 +17,21 @@ namespace mainspring { Seconds getElapsed(); // Gets the current timing session time + void reset(); + void save(bool saveas = false); + void open(); + const char* m_appName; const char* m_appVersion; - // Current "Time" (state of the app) + HWND m_dlg; + + // Current state of the app bool m_timing; // Currently timing? bool m_startpauseButtonPressed; // Was the start / pause button pressed? Seconds m_elapsedSaved; // Time saved TimePoint m_startTime; // When the current timing session was started + std::string m_file; // Current time file path + bool m_showSavedText; // Save confirmation }; } \ No newline at end of file diff --git a/Mainspring/src/pch.h b/Mainspring/src/pch.h index f41df39..861d084 100644 --- a/Mainspring/src/pch.h +++ b/Mainspring/src/pch.h @@ -11,4 +11,5 @@ // Windows #define WIN32_LEAN_AND_MEAN -#include \ No newline at end of file +#include +#include \ No newline at end of file diff --git a/Mainspring/src/resources.h b/Mainspring/src/resources.h index 194f98f..20a61b2 100644 --- a/Mainspring/src/resources.h +++ b/Mainspring/src/resources.h @@ -8,7 +8,7 @@ // Menu #define IDR_MENUMAIN 301 -#define ID_FILE_NEW 302 +#define ID_FILE_RESET 302 #define ID_FILE_SAVE 303 #define ID_FILE_SAVEAS 304 #define ID_FILE_OPEN 305 diff --git a/Mainspring/src/utility.cpp b/Mainspring/src/utility.cpp new file mode 100644 index 0000000..bc3bc60 --- /dev/null +++ b/Mainspring/src/utility.cpp @@ -0,0 +1,26 @@ +// Utility implementation +#include "pch.h" +#include "utility.h" + +namespace mainspring { + bool readFile(const char* filename, std::string& out) { + std::ifstream fileStream(filename, std::ios::binary | std::ios::ate); + if (!fileStream.is_open()) + return false; + + size_t fileSize = fileStream.tellg(); + fileStream.seekg(0); + out.resize(fileSize); + fileStream.read(out.data(), fileSize); + return true; + } + + bool writeFile(const char* filename, const std::string& in) { + std::ofstream fileStream(filename, std::ios::binary | std::ios::trunc); + if (!fileStream.is_open()) + return false; + + fileStream << in; + return true; + } +} \ No newline at end of file diff --git a/Mainspring/src/utility.h b/Mainspring/src/utility.h new file mode 100644 index 0000000..68c2068 --- /dev/null +++ b/Mainspring/src/utility.h @@ -0,0 +1,6 @@ +// Utility header + +namespace mainspring { + bool readFile(const char* filename, std::string& out); + bool writeFile(const char* filename, const std::string& in); +} \ No newline at end of file