From 58cbc7c7f4c0339928bdb704cb34ba387f12fa97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Mon, 14 Feb 2022 18:00:33 +0100 Subject: Implement list view, "about" dialog. --- Makefile | 9 +- episode_browser.manifest | 30 ++++ resource.h | 6 + resource.rc | 32 ++++ win.c | 386 +++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 418 insertions(+), 45 deletions(-) create mode 100644 episode_browser.manifest create mode 100644 resource.h create mode 100644 resource.rc diff --git a/Makefile b/Makefile index bf04586..c5e8670 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,14 @@ PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) INPUTS = $(PROJECT_ROOT)win.c +INPUTS += resource.obj INPUTS += $(PROJECT_ROOT)track_episodes.pl INPUTS += $(PROJECT_ROOT)local_episodes.pl INPUTS += $(PROJECT_ROOT)episode_data.pl +CFLAGS += -v -cc-options,-mwindows,-municode -DUNICODE -D_UNICODE +LDFLAGS += -lcomctl32 -lgdi32 + ifeq ($(BUILD_MODE),debug) CFLAGS += -g else ifeq ($(BUILD_MODE),run) @@ -21,9 +25,12 @@ endif all: episode_browser.exe episode_browser.exe: $(INPUTS) - swipl-ld -v $(CFLAGS) $(LDFLAGS) -goal true -o $@ $(INPUTS) + swipl-ld $(CFLAGS) $(LDFLAGS) -goal true -o $@ $(INPUTS) $(EXTRA_CMDS) +resource.obj: $(PROJECT_ROOT)resource.h $(PROJECT_ROOT)resource.rc + windres -i $(PROJECT_ROOT)resource.rc -o resource.obj + #%.o: $(PROJECT_ROOT)%.c # $(CC) -c $(CFLAGS) -o $@ $< diff --git a/episode_browser.manifest b/episode_browser.manifest new file mode 100644 index 0000000..978f471 --- /dev/null +++ b/episode_browser.manifest @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..6d3b85c --- /dev/null +++ b/resource.h @@ -0,0 +1,6 @@ +#define IDR_MENU 101 +#define IDD_ABOUT 201 +#define IDC_ABOUTTEXT 301 +#define IDC_LISTVIEW 302 +#define ID_FILE_EXIT 4001 +#define ID_FILE_ABOUT 4002 diff --git a/resource.rc b/resource.rc new file mode 100644 index 0000000..9f8af6e --- /dev/null +++ b/resource.rc @@ -0,0 +1,32 @@ +#include +#include "resource.h" + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "episode_browser.manifest" + +IDR_MENU MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Exit", ID_FILE_EXIT + END + POPUP "&Help" + BEGIN + MENUITEM "&About", ID_FILE_ABOUT + END +END + +#define PAD 7 +#define ABOUTW 190 +#define ABOUTH 40 +#define OKW 48 +#define OKH 16 + +IDD_ABOUT DIALOGEX DISCARDABLE 20, 20, ABOUTW, ABOUTH +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "MS Shell Dlg 2" +BEGIN + DEFPUSHBUTTON "&OK", IDOK, ABOUTW-OKW-PAD, ABOUTH-OKH-PAD, OKW, OKH + LTEXT TEXT("Episode Browser\r\nCopyright 2021 John Ankarström"), + IDC_ABOUTTEXT, PAD, PAD, ABOUTW-OKW-PAD*2, ABOUTH-PAD +END \ No newline at end of file diff --git a/win.c b/win.c index 660275d..8764092 100644 --- a/win.c +++ b/win.c @@ -1,15 +1,25 @@ #include -#include +#include +#include #include +#include +#include +#include "resource.h" + +#define CLASSNAME TEXT("Episode Browser") + +HFONT g_GUIFont; static int Attach(void); -static void ShowEpisode(int); -static void UpdateList(void); +static INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM); +static HFONT GetGUIFont(); +static void UpdateName(HWND, int); +static void UpdateList(HWND); +static void ShowEpisode(HWND, int); +static TCHAR *TSZFromSZ(int, char *); +static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); /* -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - PSTR lpCmdLine, INT nCmdShow) -*/ int main(int argc, char *argv[]) { char *rgArgs[2]; @@ -22,13 +32,189 @@ int main(int argc, char *argv[]) Attach(); //UpdateList(); - ShowEpisode(400); + //ShowEpisode(400); PL_halt(0); } +*/ + +int WINAPI +WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, INT nCmdShow) +{ + char *rgArgs[2]; + HWND hwnd; + MSG msg; + INITCOMMONCONTROLSEX icc; + WNDCLASSEX wc; + + /* Initialize Prolog. */ + + rgArgs[0] = "episode_browser"; + rgArgs[1] = NULL; + + if (!PL_initialise(1, rgArgs)) + PL_halt(1); + + Attach(); + + /* Create window. */ + + icc.dwSize = sizeof(icc); + icc.dwICC = ICC_WIN95_CLASSES; + InitCommonControlsEx(&icc); + + g_GUIFont = GetGUIFont(); + + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = 0; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); + wc.lpszClassName = CLASSNAME; + wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + RegisterClassEx(&wc); + + hwnd = CreateWindowEx( + 0, + CLASSNAME, + TEXT("Episode Browser"), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, /* Position */ + 510, 300, /* Size */ + NULL, /* Parent window */ + NULL, /* Menu */ + hInstance, + NULL + ); + + if (!hwnd) + return 0; + + ShowWindow(hwnd, nCmdShow); + + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} + +LRESULT CALLBACK +WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case ID_FILE_EXIT: + PostMessage(hwnd, WM_CLOSE, 0, 0); + break; + case ID_FILE_ABOUT: + DialogBox( + GetModuleHandle(NULL), + MAKEINTRESOURCE(IDD_ABOUT), + hwnd, + AboutDlgProc + ); + break; + } + break; + case WM_CREATE: + { + HWND hListView; + LVCOLUMN lvc; + + hListView = CreateWindowEx( + 0, + WC_LISTVIEW, + TEXT(""), + WS_CHILD|WS_VISIBLE|WS_VSCROLL|LVS_REPORT, + 0, 0, 100, 100, + hwnd, + (HMENU)IDC_LISTVIEW, + GetModuleHandle(NULL), + NULL + ); + + SendMessage(hListView,LVM_SETEXTENDEDLISTVIEWSTYLE, + 0, LVS_EX_FULLROWSELECT); + + SendMessage(hListView, WM_SETFONT, + (WPARAM)g_GUIFont, MAKELPARAM(FALSE, 0)); + + lvc.mask = LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM; + + lvc.iSubItem = 0; + lvc.pszText = TEXT("#"); + lvc.cx = 40; + ListView_InsertColumn(hListView, 0, &lvc); + + lvc.iSubItem = 1; + lvc.pszText = TEXT("Title"); + lvc.cx = 500; + ListView_InsertColumn(hListView, 1, &lvc); + + UpdateList(hListView); + } + break; + case WM_SIZE: + { + HWND hListView; + RECT rc; + + GetClientRect(hwnd, &rc); + hListView = GetDlgItem(hwnd, IDC_LISTVIEW); + MoveWindow(hListView, 0, 0, + rc.right, rc.bottom, + TRUE); + } + break; + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + return 0; +} + +INT_PTR CALLBACK +AboutDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_INITDIALOG: + return TRUE; + case WM_CLOSE: + EndDialog(hwnd, IDOK); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, IDOK); + break; + } + break; + default: + return FALSE; + } + + return TRUE; +} /* Attach persistent databases. */ -int Attach() +int +Attach() { int r; term_t t; @@ -47,53 +233,40 @@ int Attach() return 1; } -/* Update episode list. */ -void UpdateList() +HFONT +GetGUIFont() { - term_t t; - qid_t q; - - t = PL_new_term_refs(2); - PL_call_predicate(NULL, PL_Q_NORMAL, - PL_predicate("update_tracked_episodes", 0, "track_episode"), - t); - - q = PL_open_query(NULL, PL_Q_NORMAL, - PL_predicate("episode_file", 2, "local_episodes"), - t); - - while (PL_next_solution(q)) { - char *sFile; - int nEpisode; - - if (!PL_get_integer(t+0, &nEpisode)) - goto close; - if (!PL_get_atom_chars(t+1, &sFile)) - goto close; - printf("%d: %s\n", nEpisode, sFile); - } +#ifndef WIN9X + NONCLIENTMETRICS m; -close: - PL_close_query(q); /* Or PL_cut_query? */ + m.cbSize = sizeof(NONCLIENTMETRICS); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), + &m, 0); + return CreateFontIndirect(&m.lfMessageFont); +#else + return GetStockObject(DEFAULT_GUI_FONT); +#endif } /* Show episode data. */ -void ShowEpisode(int nEpisode) +void +ShowEpisode(HWND hwnd, int iEpisode) { int r; term_t t; t = PL_new_term_refs(3); - if(!PL_put_integer(t+0, nEpisode)) + if(!PL_put_integer(t+0, iEpisode)) return; r = PL_call_predicate(NULL, PL_Q_NORMAL, - PL_predicate("lookup_episode_local", 3, "episode_data"), + PL_predicate("lookup_episode_local", 3, "episode_data"), t); - if (!r) return; + if (!r) + return; /* The episode data is a list of unary compounds, - * whose name is the key and whose value is the value. */ + * whose functor is the key and whose argument is the value. */ { term_t tHead, tList; @@ -103,20 +276,145 @@ void ShowEpisode(int nEpisode) while (PL_get_list(tList, tHead, tList)) { atom_t aKey; - const char *sKey; - char *sValue; + const char *szKey; + char *szValue; term_t tValue; size_t iArity; if (!PL_get_name_arity(tHead, &aKey, &iArity)) continue; + szKey = PL_atom_chars(aKey); + if (!PL_get_arg(1, tHead, tValue)) continue; - sKey = PL_atom_chars(aKey); - if(!PL_get_atom_chars(tValue, &sValue)) + if (!PL_get_atom_chars(tValue, &szValue)) continue; - printf("%s/%d: %s\n", sKey, iArity, sValue); + printf("%s/%d: %s\n", szKey, iArity, szValue); } } } + +TCHAR * +TSZFromSZ(int iCp, char *sz) +{ + TCHAR *tsz; + +#ifdef UNICODE + int cb; + + cb = MultiByteToWideChar(iCp, 0, sz, -1, NULL, 0); + tsz = malloc(cb*sizeof(wchar_t)); + if (!tsz) + return NULL; + if (!MultiByteToWideChar(iCp, 0, sz, -1, tsz, cb)) + return NULL; +#else + tsz = malloc(strlen(sz)+1); + if (!tsz) + return NULL; + strcpy(tsz, sz); +#endif + + return tsz; +} + +/* Update episode name. */ +void +UpdateName(HWND hwnd, int iEpisode) +{ + char *szName; + term_t t; + + t = PL_new_term_refs(3); + if (!PL_put_integer(t+0, iEpisode)) + return; + + PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("lookup_episode", 3, "episode_data"), + t); + + if (!PL_get_atom_chars(t+1, &szName)) + return; + + printf("%s\n", szName); +} + +/* Update episode list. */ +void +UpdateList(HWND hListView) +{ + LVITEM lviEpisode, lviName; + qid_t q; + term_t t; + + lviEpisode.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM; + lviEpisode.state = 0; + lviEpisode.stateMask = 0; + + lviName.mask = LVIF_TEXT; + + t = PL_new_term_refs(2); + PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("update_tracked_episodes", 0, "track_episode"), + t); + + q = PL_open_query(NULL, PL_Q_NORMAL, + PL_predicate("episode_file", 2, "local_episodes"), + t); + + for (int i = 0; PL_next_solution(q); i++) { + char *szName; + int cb, iEpisode, iItem, r; + TCHAR *tszEpisode, *tszName; + term_t t2; + + /* Lookup episode name. */ + + if (!PL_get_integer(t+0, &iEpisode)) + goto close; + + t2 = PL_new_term_refs(3); + if(!PL_put_integer(t2+0, iEpisode)) + return; + + r = PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("lookup_episode_local", 3, "episode_data"), + t2); + + /* Format episode, name strings. */ + + tszName = NULL; + if (r && PL_get_atom_chars(t2+1, &szName)) { + tszName = TSZFromSZ(CP_UTF8, szName); + if (!tszName) + continue; + } + + cb = 100; + tszEpisode = malloc(cb*sizeof(TCHAR)); + if (!tszEpisode) + continue; + _stprintf_s(tszEpisode, cb, TEXT("%d"), + iEpisode); + + /* Insert item. */ + + lviEpisode.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM; + lviEpisode.iItem = i; + lviEpisode.iSubItem = 0; + lviEpisode.pszText = tszEpisode; + lviEpisode.lParam = iEpisode; + ListView_InsertItem(hListView, &lviEpisode); + + if (tszName) { + lviName.iItem = i; + lviName.iSubItem = 1; + lviName.pszText = tszName; + ListView_SetItem(hListView, &lviName); + } + } + +close: + PL_close_query(q); +} -- cgit v1.2.3