aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-09-02 20:16:04 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-09-02 20:16:18 +0200
commitbc4cef92d8efbf97a9215122abc2d7247c287f12 (patch)
treea5ca307281c4f143b5f172428c9fd2b629d6b426
parent5c1c2ce2bdbf9735ad8a4d162609a8c22a4f0954 (diff)
downloadEpisodeBrowser-bc4cef92d8efbf97a9215122abc2d7247c287f12.tar.gz
Improve Window object.
-rw-r--r--README4
-rw-r--r--c/CMakeLists.txt10
-rw-r--r--c/data.cpp44
-rw-r--r--c/data.h5
-rw-r--r--c/datalistview.cpp7
-rw-r--r--c/debug.cpp2
-rw-r--r--c/drag.cpp (renamed from c/layout.cpp)58
-rw-r--r--c/drag.h (renamed from c/layout.h)10
-rw-r--r--c/episodelistview.cpp19
-rw-r--r--c/ext.cpp17
-rw-r--r--c/ext.h8
-rw-r--r--c/listview.cpp8
-rw-r--r--c/main.cpp79
-rw-r--r--c/test.cpp16
-rw-r--r--c/test.h3
-rw-r--r--c/win32.cpp (renamed from c/win.cpp)4
-rw-r--r--c/win32.h (renamed from c/win.h)10
-rw-r--r--c/window.h50
18 files changed, 188 insertions, 166 deletions
diff --git a/README b/README
index f0cecc9..c26ab24 100644
--- a/README
+++ b/README
@@ -26,8 +26,6 @@ episodelistview.cpp,h
Defines the interface to the episode list view.
datalistview.cpp,h
Defines the interface to the data list view.
-layout.cpp,h
- Handles the placement of child windows.
resource.rc
Defines menu and dialog window resources.
resource.h
@@ -43,7 +41,7 @@ data.cpp,h
>> Miscellanea <<
-win.cpp,h
+win32.cpp,h
Helpers for interacting with the Win32 API.
util.h
Utility functions.
diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt
index 35af6ec..cd86f08 100644
--- a/c/CMakeLists.txt
+++ b/c/CMakeLists.txt
@@ -20,23 +20,23 @@ target_sources(EpisodeBrowser PRIVATE
datalistview.h
debug.cpp
debug.h
+ drag.cpp
+ drag.h
episodelistview.cpp
episodelistview.h
ext.cpp
ext.h
- layout.cpp
- layout.h
listview.cpp
listview.h
main.cpp
- main.h
resource.h
resource.rc
test.cpp
test.h
util.h
- win.cpp
- win.h
+ win32.cpp
+ win32.h
+ window.h
)
if (CMAKE_GENERATOR MATCHES "Visual Studio")
diff --git a/c/data.cpp b/c/data.cpp
index 1274321..b1c028e 100644
--- a/c/data.cpp
+++ b/c/data.cpp
@@ -5,12 +5,12 @@
#include <libxml/HTMLtree.h>
#include <libxml/xpath.h>
-#include "resource.h"
#include "data.h"
#include "episodelistview.h"
-#include "main.h"
+#include "resource.h"
#include "util.h"
-#include "win.h"
+#include "win32.h"
+#include "window.h"
UniqueOk<htmlParserCtxtPtr, xmlFreeParserCtxt> RemoteParserCtxt(const wchar_t* wszUrl, const char* szUrl)
{
@@ -88,9 +88,10 @@ enum Signal : unsigned char
ABORT = 1<<2 /* Main -> fetch: exit prematurely! */
};
-void WaitFor(void (*f)(unsigned char*))
+static Window* s_window;
+
+void WaitFor(Window& window, void (*f)(unsigned char*))
{
- extern Window* g_window;
static unsigned char sig = READY;
static UINT_PTR iTimer = 0;
@@ -102,13 +103,13 @@ void WaitFor(void (*f)(unsigned char*))
KillTimer(nullptr, iTimer);
i = 0;
sig = READY; /* Reset signals. */
- g_window->elv.Update(); /* Reset status bar. */
- EnableMenuItem(GetMenu(g_window->hWnd), IDM_FILE_FETCH_CANCEL, MF_GRAYED);
+ s_window->elv.Update(); /* Reset status bar. */
+ EnableMenuItem(GetMenu(s_window->hWnd), IDM_FILE_FETCH_CANCEL, MF_GRAYED);
} else {
/* Animate ellipsis in status bar. */
static const wchar_t* text[] = {L".", L"..", L"...", L""};
i = (i+1)%(sizeof(text)/sizeof(*text));
- Status(text[i], 1);
+ s_window->Status(text[i], 1);
}
};
@@ -118,7 +119,7 @@ void WaitFor(void (*f)(unsigned char*))
while (!(sig & READY))
Sleep(100);
sig = 0;
- EnableMenuItem(GetMenu(g_window->hWnd), IDM_FILE_FETCH_CANCEL, MF_ENABLED);
+ EnableMenuItem(GetMenu(s_window->hWnd), IDM_FILE_FETCH_CANCEL, MF_ENABLED);
try {
f(&sig);
sig |= DONE;
@@ -132,7 +133,7 @@ void WaitFor(void (*f)(unsigned char*))
/* Null indicates that any active task should be cancelled. */
if (!f) {
sig |= ABORT;
- EnableMenuItem(GetMenu(g_window->hWnd), IDM_FILE_FETCH_CANCEL, MF_GRAYED);
+ EnableMenuItem(GetMenu(s_window->hWnd), IDM_FILE_FETCH_CANCEL, MF_GRAYED);
return;
}
@@ -146,8 +147,9 @@ void WaitFor(void (*f)(unsigned char*))
return;
}
+ s_window = &window;
std::thread(procThread, f).detach();
- Status(L".", 1);
+ s_window->Status(L".", 1);
Prefer(iTimer = SetTimer(nullptr, iTimer, 500, procTimer));
}
@@ -180,8 +182,6 @@ void FetchData(unsigned char* sig)
throw std::runtime_error("could not find remote episode information");
for (int i = 0; i < nodes->nodeNr; i++) {
- extern Window* g_window;
-
if (*sig & ABORT)
return;
@@ -189,8 +189,8 @@ void FetchData(unsigned char* sig)
if (xmlChildElementCount(node) != 8)
throw std::runtime_error("unexpected remote data format");
- ElvDataA& e = g_window->fvElv.At(i);
- DlvDataA& d = g_window->fvDlv.At(i);
+ ElvDataA& e = s_window->fvElv.At(i);
+ DlvDataA& d = s_window->fvDlv.At(i);
/* Each datum is contained within a specific cell in
* the row. The child element count above ensures that
@@ -222,8 +222,6 @@ void FetchData(unsigned char* sig)
void FetchScreenwriters(unsigned char* sig)
{
- extern Window* g_window;
-
/* Screenwriters are expensive to fetch, so we try to avoid
* fetching screenwriters for episodes that already have a
* screenwriter. Additionally, in the same session, we don't
@@ -231,18 +229,18 @@ void FetchScreenwriters(unsigned char* sig)
* already tried to fetch screenwriters. We keep track of
* these states using the iLast variable. */
static int iLast = -1;
- int iMax = g_window->cfg.cEp-1;
+ int iMax = s_window->cfg.cEp-1;
/* Find the last episode that has a screenwriter. */
if (iLast == -1)
- for (size_t i = 0; i < g_window->fvDlv.c; i++)
- if (const DlvDataA& d = g_window->fvDlv[i]; !d.date[0]) {
+ for (size_t i = 0; i < s_window->fvDlv.c; i++)
+ if (const DlvDataA& d = s_window->fvDlv[i]; !d.date[0]) {
iMax = i-1;
break;
} else if (d.screenwriter[0])
iLast = i;
- FINALLY { Status(L""); };
+ FINALLY { s_window->Status(L""); };
/* Fetch screenwriters for the rest of the episodes. */
const wchar_t prefix[] = L"https://www.detectiveconanworld.com";
@@ -257,10 +255,10 @@ void FetchScreenwriters(unsigned char* sig)
wchar_t msg[48];
Swprintf(msg, L"Fetching screenwriter for episode %d...", iLast+1);
- Status(msg);
+ s_window->Status(msg);
/* Retrieve URL for episode's wiki page. */
- DlvDataA& d = g_window->fvDlv[iLast];
+ DlvDataA& d = s_window->fvDlv[iLast];
Wcscpy(Buf(url)+Len(prefix), d.wiki);
/* Retrieve screenwriter from HTML. */
diff --git a/c/data.h b/c/data.h
index 0f00ecb..919e1b4 100644
--- a/c/data.h
+++ b/c/data.h
@@ -6,7 +6,7 @@
#include <libxml/xmlerror.h>
#include "util.h"
-#include "win.h"
+#include "win32.h"
struct XmlError : public std::exception
{
@@ -26,7 +26,8 @@ void FetchData(unsigned char* sig);
void FetchScreenwriters(unsigned char* sig);
/* Wait for thread. */
-void WaitFor(void (*f)(unsigned char*));
+struct Window;
+void WaitFor(Window& window, void (*f)(unsigned char*));
/* The structs ending with A are written as-is to disk. As such, they
* should be regarded as immutable. If the format needs to be changed
diff --git a/c/datalistview.cpp b/c/datalistview.cpp
index cdd8189..92fc05c 100644
--- a/c/datalistview.cpp
+++ b/c/datalistview.cpp
@@ -3,13 +3,12 @@
#include <windows.h>
#include <commctrl.h>
-#include "resource.h"
#include "data.h"
#include "datalistview.h"
#include "episodelistview.h"
#include "listview.h"
-#include "layout.h"
-#include "main.h"
+#include "resource.h"
+#include "window.h"
DataListView::DataListView(Window& parent)
: ListView(parent, reinterpret_cast<HMENU>(IDC_DATALISTVIEW), LVS_NOCOLUMNHEADER)
@@ -81,7 +80,7 @@ void DataListView::ShowEpisode(const int iEpisode)
}
}
- UpdateLayout();
+ parent.UpdateLayout();
LVFINDINFO lvfi;
lvfi.flags = LVFI_PARAM;
diff --git a/c/debug.cpp b/c/debug.cpp
index e8a9d6e..3e20d73 100644
--- a/c/debug.cpp
+++ b/c/debug.cpp
@@ -3,7 +3,7 @@
#include <windows.h>
#include "debug.h"
-#include "win.h"
+#include "win32.h"
struct Avg {
int id;
diff --git a/c/layout.cpp b/c/drag.cpp
index e817b23..b92429a 100644
--- a/c/layout.cpp
+++ b/c/drag.cpp
@@ -1,42 +1,10 @@
#include <windows.h>
-#include "episodelistview.h"
#include "datalistview.h"
-#include "layout.h"
-#include "main.h"
-#include "win.h"
-
-extern Window* g_window;
-extern HWND g_hWndStatus;
-
-void UpdateLayout(int w, int h)
-{
- if (!g_hWndStatus) return;
-
- RECT rc, rrStatus;
- if (w && h) rc = {0, 0, w, h};
- else Require(GetClientRect(g_window->hWnd, &rc));
- Require(GetRelativeRect(g_hWndStatus, &rrStatus));
-
- SendMessageW(g_window->hWnd, WM_SETREDRAW, FALSE, 0);
-
- /* Resize list views. */
- const long pad = EBIsThemeActive()? Dpi(6): 0; /* Add padding in modern themes. */
- const long cyDlv = rrStatus.top-g_window->dlv.Height()-pad;
- Require(SetWindowRect(g_window->dlv.hWnd, pad, cyDlv, rc.right-pad, rrStatus.top-pad));
- Require(SetWindowRect(g_window->elv.hWnd, pad, pad, rc.right-pad, cyDlv-pad));
- g_window->dlv.ResizeColumns(rc.right-pad-pad);
- g_window->elv.ResizeColumns(rc.right-pad-pad);
-
- /* Resize status bar parts. */
- const int aParts[] = {rc.right-Dpi(55), rc.right};
- SendMessageW(g_hWndStatus, SB_SETPARTS,
- sizeof(aParts), reinterpret_cast<LPARAM>(aParts));
-
- SendMessageW(g_window->hWnd, WM_SETREDRAW, TRUE, 0);
- RedrawWindow(g_window->hWnd, nullptr, nullptr,
- RDW_ERASE|RDW_FRAME|RDW_INVALIDATE|RDW_ALLCHILDREN);
-}
+#include "drag.h"
+#include "episodelistview.h"
+#include "win32.h"
+#include "window.h"
bool Dragger::IsDouble(const long time, const POINT& pt)
{
@@ -92,7 +60,7 @@ bool Dragger::HandleSetCursor()
bool DlvDragger::InDragArea(const int x, const int y) const
{
RECT rrDlv;
- Require(GetRelativeRect(g_window->dlv.hWnd, &rrDlv));
+ Require(GetRelativeRect(parent.dlv.hWnd, &rrDlv));
const int pad = EBIsThemeActive()? Dpi(6): 0;
const int extra = EBIsThemeActive()? 0: Dpi(2);
@@ -104,26 +72,26 @@ bool DlvDragger::InDragArea(const int x, const int y) const
void DlvDragger::Drag(const int, const int y) const
{
RECT rrDlv;
- Require(GetRelativeRect(g_window->dlv.hWnd, &rrDlv));
+ Require(GetRelativeRect(parent.dlv.hWnd, &rrDlv));
if (y < Dpi(50) || y > rrDlv.bottom-Dpi(20)) return;
int h;
h = rrDlv.bottom-y;
- g_window->dlv.SetHeight(h);
- UpdateLayout();
- RedrawWindow(g_window->hWnd, nullptr, nullptr,
+ parent.dlv.SetHeight(h);
+ parent.UpdateLayout();
+ RedrawWindow(parent.hWnd, nullptr, nullptr,
RDW_ERASE|RDW_FRAME|RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_UPDATENOW);
}
void DlvDragger::Reset() const
{
- g_window->dlv.SetHeight(0);
- g_window->cfg.heightDlv = 0;
- UpdateLayout();
+ parent.dlv.SetHeight(0);
+ parent.cfg.heightDlv = 0;
+ parent.UpdateLayout();
}
void DlvDragger::Done() const
{
- g_window->cfg.heightDlv = g_window->dlv.Height();
+ parent.cfg.heightDlv = parent.dlv.Height();
}
diff --git a/c/layout.h b/c/drag.h
index 4780821..ac97090 100644
--- a/c/layout.h
+++ b/c/drag.h
@@ -1,14 +1,8 @@
-#ifndef LAYOUT_H
-#define LAYOUT_H
+#ifndef DRAG_H
+#define DRAG_H
#include <windows.h>
-#include "win.h"
-
-/* Given main window's width and height, set appropriate positions and
- * sizes for child windows. */
-void UpdateLayout(int w = 0, int h = 0);
-
/* Dragger objects implement draggable portions of the client area,
* such as the split between two list views. HandleLButtonDown and
* HandleSetCursor are called by relevant window procedures upon
diff --git a/c/episodelistview.cpp b/c/episodelistview.cpp
index ea4849f..29c015b 100644
--- a/c/episodelistview.cpp
+++ b/c/episodelistview.cpp
@@ -3,15 +3,15 @@
#include <windows.h>
#include <commctrl.h>
-#include "resource.h"
#include "data.h"
#include "datalistview.h"
#include "episodelistview.h"
#include "ext.h"
#include "listview.h"
-#include "main.h"
+#include "resource.h"
#include "util.h"
-#include "win.h"
+#include "win32.h"
+#include "window.h"
EpisodeListView::EpisodeListView(Window& parent)
: ListView(parent, reinterpret_cast<HMENU>(IDC_EPISODELISTVIEW), 0)
@@ -66,12 +66,12 @@ void EpisodeListView::HandleContextMenu(const WORD command)
/* Process other commands. */
switch (command) {
case IDM_WATCH_LOCALLY:
- if(!OpenLocally(lvi.lParam))
+ if(!OpenLocally(parent.cfg, parent.fvElv.At(lvi.lParam-1)))
cNotFound++;
break;
case IDM_WATCH_ONLINE:
- OpenOnline(lvi.lParam);
+ OpenOnline(parent.cfg, lvi.lParam);
break;
case IDM_TOGGLE:
@@ -79,7 +79,7 @@ void EpisodeListView::HandleContextMenu(const WORD command)
break;
case IDM_WIKI:
- OpenWiki(lvi.lParam);
+ OpenWiki(parent.fvDlv.At(lvi.lParam-1));
break;
}
}
@@ -176,15 +176,14 @@ LRESULT EpisodeListView::HandleNotify(const LPARAM lParam)
{
LVITEM lvi = {LVIF_PARAM, -1};
while (FindNextItem(&lvi, LVNI_SELECTED))
- OpenLocally(lvi.lParam) || OpenOnline(lvi.lParam);
+ OpenLocally(parent.cfg, parent.fvElv.At(lvi.lParam-1)) || OpenOnline(parent.cfg, lvi.lParam);
return 0;
}
case NM_RCLICK:
{
- extern HMENU g_hMenuPopup;
const DWORD pos = GetMessagePos();
- Require(TrackPopupMenu(g_hMenuPopup, TPM_RIGHTBUTTON,
+ Require(TrackPopupMenu(parent.hMenuPopup, TPM_RIGHTBUTTON,
LOWORD(pos), HIWORD(pos), 0, parent.hWnd, nullptr));
return 0;
}
@@ -415,7 +414,7 @@ void EpisodeListView::Update()
/* Show number of displayed items in status bar. */
wchar_t disp[16];
Swprintf(disp, L"%d", cItem);
- Status(disp, 1);
+ parent.Status(disp, 1);
}
Sort();
diff --git a/c/ext.cpp b/c/ext.cpp
index 77028d1..ea6718a 100644
--- a/c/ext.cpp
+++ b/c/ext.cpp
@@ -3,14 +3,12 @@
#include <string_view>
#include "data.h"
-#include "main.h"
+#include "window.h"
-extern Window* g_window;
-
-bool OpenOnline(int iEp)
+bool OpenOnline(const CfgA& cfg, int iEp)
{
- wchar_t url[sizeof(g_window->cfg.url)+4];
- Swprintf(url, L"%s%d", g_window->cfg.url, iEp);
+ wchar_t url[sizeof(cfg.url)+4];
+ Swprintf(url, L"%s%d", cfg.url, iEp);
INT_PTR r = reinterpret_cast<INT_PTR>(
ShellExecuteW(nullptr, L"open", url, nullptr, nullptr, SW_SHOWNORMAL));
if (r <= 32)
@@ -18,9 +16,8 @@ bool OpenOnline(int iEp)
return true;
}
-bool OpenWiki(int iEp)
+bool OpenWiki(const DlvDataA& d)
{
- const DlvDataA& d = g_window->fvDlv.At(iEp-1);
wchar_t url[sizeof(d.wiki)+35];
Swprintf(url, L"https://www.detectiveconanworld.com%s", d.wiki);
INT_PTR r = reinterpret_cast<INT_PTR>(
@@ -110,10 +107,10 @@ static bool FindMatchingFile(wchar_t (&file)[MAX_PATH], const wchar_t* const roo
return false;
}
-bool OpenLocally(int iEp)
+bool OpenLocally(CfgA& cfg, const ElvDataA& e)
{
wchar_t file[MAX_PATH];
- if (FindMatchingFile(file, g_window->cfg.root, g_window->fvElv.At(iEp-1).siEp)) {
+ if (FindMatchingFile(file, cfg.root, e.siEp)) {
INT_PTR r = reinterpret_cast<INT_PTR>(
ShellExecuteW(nullptr, L"open", file, nullptr, nullptr, SW_SHOWNORMAL));
if (r <= 32)
diff --git a/c/ext.h b/c/ext.h
index 3f9ddbd..5925206 100644
--- a/c/ext.h
+++ b/c/ext.h
@@ -1,8 +1,10 @@
#ifndef EXT_H
#define EXT_H
-bool OpenOnline(int iEp);
-bool OpenWiki(int iEp);
-bool OpenLocally(int iEp);
+#include "data.h"
+
+bool OpenOnline(const CfgA& cfg, int iEp);
+bool OpenWiki(const DlvDataA& d);
+bool OpenLocally(CfgA& cfg, const ElvDataA& e);
#endif
diff --git a/c/listview.cpp b/c/listview.cpp
index 6074707..03a65ae 100644
--- a/c/listview.cpp
+++ b/c/listview.cpp
@@ -1,10 +1,10 @@
#include <windows.h>
#include <commctrl.h>
+#include "drag.h"
#include "listview.h"
-#include "layout.h"
-#include "main.h"
-#include "win.h"
+#include "win32.h"
+#include "window.h"
/* Actual window procedure for all list views, which calls the
* appropriate WndProc member function. */
@@ -66,7 +66,7 @@ LRESULT CALLBACK ListView::WndProc(const HWND hWnd, const UINT uMsg,
case WM_NOTIFY:
switch (reinterpret_cast<NMHDR*>(lParam)->code) {
case HDN_ENDTRACK:
- UpdateLayout();
+ parent.UpdateLayout();
return TRUE;
}
break;
diff --git a/c/main.cpp b/c/main.cpp
index 58c0441..ea12db9 100644
--- a/c/main.cpp
+++ b/c/main.cpp
@@ -4,14 +4,14 @@
#include <commctrl.h>
#include <libxml/xmlversion.h>
-#include "debug.h"
-#include "resource.h"
#include "datalistview.h"
+#include "debug.h"
+#include "drag.h"
#include "episodelistview.h"
-#include "layout.h"
-#include "main.h"
+#include "resource.h"
#include "test.h"
#include "util.h"
+#include "window.h"
#ifdef _DEBUG
#define XMAIN 30
@@ -21,10 +21,6 @@
#define YMAIN CW_USEDEFAULT
#endif
-/* main.cpp defines all global (non-template) variables used in the
- * program. `extern' is used to access them from other files, when
- * need be. */
-
/* Looked-up constants. */
int g_dpi = 96;
@@ -36,26 +32,19 @@ HFONT g_hfBold;
HCURSOR g_hcArrow = LoadCursorW(nullptr, IDC_ARROW);
HCURSOR g_hcSizeNs = LoadCursorW(nullptr, IDC_SIZENS);
-/* Menus. */
-HMENU g_hMenuPopup;
-
-/* Windows. */
-HWND g_hWndFocus;
-HWND g_hWndStatus;
+/* Main window object. */
+Window* g_window;
/* Optional Windows functions. */
BOOL (__stdcall *IsThemeActive)();
HRESULT (__stdcall *SetWindowTheme)(HWND, const wchar_t*, const wchar_t*);
-/* Main window object. */
-Window* g_window;
-
/* Initialize important global state on parent window creation. */
static void InitializeMainWindow(HWND) noexcept;
/* Process parent window commands. */
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
/* Handle messages to Help > About dialog. */
-INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
+static INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(
_In_ const HINSTANCE hInstance,
@@ -99,7 +88,7 @@ int WINAPI WinMain(
XMAIN, YMAIN, 0, 0,
nullptr, nullptr, hInstance, nullptr));
- g_hWndStatus = Require(CreateWindowExW(
+ g_window->hWndStatus = Require(CreateWindowExW(
0,
STATUSCLASSNAME,
nullptr,
@@ -115,7 +104,7 @@ int WINAPI WinMain(
g_window->elv.RestoreFocus();
#ifdef _DEBUG
- RunTests();
+ RunTests(*g_window);
#endif
MSG msg;
@@ -167,10 +156,6 @@ static void InitializeMainWindow_(const HWND hWnd)
SetWindowTheme = (decltype(SetWindowTheme))(void*)GetProcAddress(hModule, "SetWindowTheme");
}
- /* Load context menu. */
- g_hMenuPopup = Require(LoadMenuW(nullptr, MAKEINTRESOURCE(IDR_POPUPMENU)));
- g_hMenuPopup = Require(GetSubMenu(g_hMenuPopup, 0));
-
g_window = new Window(hWnd);
}
@@ -235,7 +220,7 @@ LRESULT CALLBACK Window::WndProc(const HWND hWnd, const UINT uMsg, const WPARAM
return 0;
case WM_SIZE:
- SendMessage(g_hWndStatus, WM_SIZE, wParam, lParam);
+ SendMessage(hWndStatus, WM_SIZE, wParam, lParam);
UpdateLayout(LOWORD(lParam), HIWORD(lParam));
return 0;
@@ -272,9 +257,9 @@ LRESULT CALLBACK Window::WndProc(const HWND hWnd, const UINT uMsg, const WPARAM
case WM_ACTIVATE:
if (wParam == WA_INACTIVE)
- g_hWndFocus = GetFocus();
+ hWndFocus = GetFocus();
else {
- SetFocus(g_hWndFocus);
+ SetFocus(hWndFocus);
/* TODO: Update tracked episodes. */
elv.Redraw();
}
@@ -387,16 +372,16 @@ void Window::HandleMainMenu(const HWND hWnd, const WORD command)
case IDM_FILE_FETCH_DATA:
{
- WaitFor(FetchData);
+ WaitFor(*this, FetchData);
break;
}
case IDM_FILE_FETCH_SCREENWRITERS:
- WaitFor(FetchScreenwriters);
+ WaitFor(*this, FetchScreenwriters);
break;
case IDM_FILE_FETCH_CANCEL:
- WaitFor(nullptr);
+ WaitFor(*this, nullptr);
break;
case IDM_FILE_ABOUT:
@@ -441,6 +426,40 @@ void Window::HandleMainMenu(const HWND hWnd, const WORD command)
}
}
+void Window::Status(const wchar_t* msg, unsigned short i)
+{
+ SendMessage(hWndStatus, SB_SETTEXT, MAKEWPARAM(i, 0), reinterpret_cast<LPARAM>(msg));
+}
+
+void Window::UpdateLayout(int w, int h)
+{
+ if (!hWndStatus) return;
+
+ RECT rc, rrStatus;
+ if (w && h) rc = {0, 0, w, h};
+ else Require(GetClientRect(hWnd, &rc));
+ Require(GetRelativeRect(hWndStatus, &rrStatus));
+
+ SendMessageW(hWnd, WM_SETREDRAW, FALSE, 0);
+
+ /* Resize list views. */
+ const long pad = EBIsThemeActive()? Dpi(6): 0; /* Add padding in modern themes. */
+ const long cyDlv = rrStatus.top-dlv.Height()-pad;
+ Require(SetWindowRect(dlv.hWnd, pad, cyDlv, rc.right-pad, rrStatus.top-pad));
+ Require(SetWindowRect(elv.hWnd, pad, pad, rc.right-pad, cyDlv-pad));
+ dlv.ResizeColumns(rc.right-pad-pad);
+ elv.ResizeColumns(rc.right-pad-pad);
+
+ /* Resize status bar parts. */
+ const int aParts[] = {rc.right-Dpi(55), rc.right};
+ SendMessageW(hWndStatus, SB_SETPARTS,
+ sizeof(aParts), reinterpret_cast<LPARAM>(aParts));
+
+ SendMessageW(hWnd, WM_SETREDRAW, TRUE, 0);
+ RedrawWindow(hWnd, nullptr, nullptr,
+ RDW_ERASE|RDW_FRAME|RDW_INVALIDATE|RDW_ALLCHILDREN);
+}
+
void Window::UpdateTheme()
{
if (IsThemeActive) {
diff --git a/c/test.cpp b/c/test.cpp
index 35c6904..c0a9445 100644
--- a/c/test.cpp
+++ b/c/test.cpp
@@ -4,11 +4,11 @@
#include "data.h"
#include "episodelistview.h"
#include "ext.h"
-#include "main.h"
#include "util.h"
-#include "win.h"
+#include "win32.h"
+#include "window.h"
-extern Window* g_window;
+Window* s_window;
struct Test
{
@@ -36,8 +36,8 @@ TESTS
TEST(IO)
{
- ElvDataA& e1_0 = g_window->fvElv.At(5);
- ElvDataA& e2_0 = g_window->fvElv.At(9);
+ ElvDataA& e1_0 = s_window->fvElv.At(5);
+ ElvDataA& e2_0 = s_window->fvElv.At(9);
/* Write two ElvDataA structs to disk. */
{
@@ -91,7 +91,7 @@ TESTS
// TEST(MigrateCfg)
// {
// FileView<CfgB> fvb = FileView<CfgB>::Initialized(L"cfgb.dat", 1);
-// CfgA* a = &g_window->cfg;
+// CfgA* a = &s_window->cfg;
// CfgB* b = fvb+0;
// #define CPY(member) b->member = a->member;
@@ -113,8 +113,10 @@ TESTS
// }
};
-int RunTests()
+int RunTests(Window& window)
{
+ s_window = &window;
+
const Test tests[] = {
StrcpyWithSmallerDestination(),
//IO(),
diff --git a/c/test.h b/c/test.h
index 50ae9e6..31eb974 100644
--- a/c/test.h
+++ b/c/test.h
@@ -1,6 +1,7 @@
#ifndef TEST_H
#define TEST_H
-int RunTests();
+struct Window;
+int RunTests(Window& window);
#endif
diff --git a/c/win.cpp b/c/win32.cpp
index 40d68e4..f06f96c 100644
--- a/c/win.cpp
+++ b/c/win32.cpp
@@ -4,9 +4,9 @@
#include <windows.h>
#include <wininet.h>
-#include "main.h"
#include "util.h"
-#include "win.h"
+#include "win32.h"
+#include "window.h"
std::wstring WideFromNarrow(const std::string_view src, const int cp)
{
diff --git a/c/win.h b/c/win32.h
index 1e09c1f..c4e57b7 100644
--- a/c/win.h
+++ b/c/win32.h
@@ -1,5 +1,5 @@
-#ifndef WIN_H
-#define WIN_H
+#ifndef WIN32_H
+#define WIN32_H
#include <optional>
#include <string_view>
@@ -144,10 +144,4 @@ inline HRESULT EBSetWindowTheme(const HWND hWnd, const wchar_t* const pszSubAppN
return SetWindowTheme? SetWindowTheme(hWnd, pszSubAppName, pszSubIdList): 0;
}
-inline void Status(const wchar_t* msg, unsigned short i = 0)
-{
- extern HWND g_hWndStatus;
- SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(i,0), reinterpret_cast<LPARAM>(msg));
-}
-
#endif
diff --git a/c/window.h b/c/window.h
new file mode 100644
index 0000000..5cdffe4
--- /dev/null
+++ b/c/window.h
@@ -0,0 +1,50 @@
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <windows.h>
+
+#include "data.h"
+#include "datalistview.h"
+#include "drag.h"
+#include "episodelistview.h"
+#include "resource.h"
+#include "win32.h"
+
+struct Window
+{
+ HWND hWnd;
+ HWND hWndFocus = nullptr;
+ HWND hWndStatus = nullptr;
+ HMENU hMenuPopup = Require(GetSubMenu(Require(LoadMenuW(nullptr, MAKEINTRESOURCE(IDR_POPUPMENU))), 0));
+
+ /* File views. */
+ FileView<CfgA> fvCfg = FileView<CfgA>::Initialized(L"cfg.dat", 1);
+ CfgA& cfg = fvCfg.At(0);
+ FileView<ElvDataA> fvElv{L"elvdata.dat", cfg.cEp+128u};
+ FileView<DlvDataA> fvDlv{L"dlvdata.dat", cfg.cEp+128u};
+
+ /* Layout handlers. */
+ DlvDragger dragDlv = DlvDragger(*this);
+
+ /* Child window objects. */
+ DataListView dlv = DataListView(*this);
+ EpisodeListView elv = EpisodeListView(*this);
+
+ inline Window(HWND hWnd) : hWnd(hWnd) {}
+ LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+
+ /* Given main window's width and height, set appropriate
+ * positions and sizes for child windows. */
+ void UpdateLayout(int w = 0, int h = 0);
+
+ /* Process main menu commands. */
+ void HandleMainMenu(HWND, WORD);
+
+ /* Display text in status bar. */
+ void Status(const wchar_t* msg, unsigned short i = 0);
+
+ /* Try to style application according to current Windows theme. */
+ void UpdateTheme();
+};
+
+#endif