From 52fb337856497cb151081f3738e7cfa4bc2883bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Tue, 15 Feb 2022 16:29:59 +0100 Subject: Rework list view code. --- c/application.manifest | 30 +++ c/common.c | 45 ++++ c/common.h | 5 + c/datalistview.c | 60 +++++ c/datalistview.h | 5 + c/episode_browser.manifest | 30 --- c/episodelistview.c | 200 ++++++++++++++++ c/episodelistview.h | 7 + c/listview.c | 68 ++++++ c/listview.h | 4 + c/main.c | 232 +++++++++++++++++++ c/main.h | 4 + c/resource.h | 15 +- c/resource.rc | 2 +- c/win.c | 560 --------------------------------------------- 15 files changed, 669 insertions(+), 598 deletions(-) create mode 100644 c/application.manifest create mode 100644 c/common.c create mode 100644 c/common.h create mode 100644 c/datalistview.c create mode 100644 c/datalistview.h delete mode 100644 c/episode_browser.manifest create mode 100644 c/episodelistview.c create mode 100644 c/episodelistview.h create mode 100644 c/listview.c create mode 100644 c/listview.h create mode 100644 c/main.c create mode 100644 c/main.h delete mode 100644 c/win.c (limited to 'c') diff --git a/c/application.manifest b/c/application.manifest new file mode 100644 index 0000000..978f471 --- /dev/null +++ b/c/application.manifest @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/c/common.c b/c/common.c new file mode 100644 index 0000000..c1f9614 --- /dev/null +++ b/c/common.c @@ -0,0 +1,45 @@ +#include +#include + +#include "resource.h" + +/* Convert zero-terminated non-wide (multi-byte) string to + * zero-terminated wide/non-wide string depending on UNICODE. */ +TCHAR * +TSZFromSZ(char *sz, int iCp) +{ + TCHAR *tsz; + +#ifdef UNICODE + int cbMultiByte, cchWideChar; + + cbMultiByte = strlen(sz)+1; + cchWideChar = MultiByteToWideChar(iCp, 0, sz, cbMultiByte, NULL, 0); + tsz = malloc(cchWideChar*sizeof(WCHAR)); + if (!tsz) + return NULL; + if (!MultiByteToWideChar(iCp, 0, sz, cbMultiByte, tsz, cchWideChar)) + return NULL; +#else + tsz = malloc(strlen(sz)+1); + if (!tsz) + return NULL; + strcpy(tsz, sz); +#endif + + return tsz; +} + +int +Watched(int iEpisode) +{ + term_t t; + + t = PL_new_term_refs(1); + if (!PL_put_integer(t+0, iEpisode)) + return 0; + + return PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("watched", 1, "track_episodes"), + t); +} diff --git a/c/common.h b/c/common.h new file mode 100644 index 0000000..b59b205 --- /dev/null +++ b/c/common.h @@ -0,0 +1,5 @@ +#pragma once +#include + +TCHAR *TSZFromSZ(char *, int); +int Watched(int); diff --git a/c/datalistview.c b/c/datalistview.c new file mode 100644 index 0000000..4980d3b --- /dev/null +++ b/c/datalistview.c @@ -0,0 +1,60 @@ +#include +#include + +#include "resource.h" +#include "datalistview.h" + +HWND gDlv_hWnd; + +void +DlvCreate(HWND hWnd) +{ + gDlv_hWnd = hWnd; +} + +/* Show episode data. */ +void +DlvShowEpisode(int iEpisode) +{ + int r; + term_t t; + + t = PL_new_term_refs(3); + if(!PL_put_integer(t+0, iEpisode)) + return; + + r = PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("lookup_episode_local", 3, "episode_data"), + t); + if (!r) + return; + + /* The episode data is a list of unary compounds, + * whose functor is the key and whose argument is the value. */ + + { + term_t tHead, tList; + + tHead = PL_new_term_ref(); + tList = PL_copy_term_ref(t+2); + + while (PL_get_list(tList, tHead, tList)) { + atom_t aKey; + 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; + if (!PL_get_atom_chars(tValue, &szValue)) + continue; + + //printf("%s/%d: %s\n", szKey, iArity, szValue); + } + } +} diff --git a/c/datalistview.h b/c/datalistview.h new file mode 100644 index 0000000..e1e2748 --- /dev/null +++ b/c/datalistview.h @@ -0,0 +1,5 @@ +#pragma once +#include + +void DlvCreate(HWND); +void DlvShowEpisode(int); diff --git a/c/episode_browser.manifest b/c/episode_browser.manifest deleted file mode 100644 index 978f471..0000000 --- a/c/episode_browser.manifest +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/c/episodelistview.c b/c/episodelistview.c new file mode 100644 index 0000000..82f460a --- /dev/null +++ b/c/episodelistview.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include + +#include "resource.h" +#include "common.h" +#include "listview.h" +#include "episodelistview.h" +#include "main.h" + +extern HFONT g_GUIFontBold; + +HWND gElv_hWnd; +WNDPROC g_PrevElvProc; +int g_SelectedItem = -1; /* Remembered after refresh. */ + +HWND +ElvCreate(HWND hWnd) +{ + HMODULE hModule; + HWND hListView; + LVCOLUMN lvc; + + gElv_hWnd = hWnd; + hListView = LvCreate(hWnd, (HMENU)IDC_EPISODELISTVIEW); + + lvc.mask = LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM; + + lvc.iSubItem = 0; + lvc.pszText = TEXT("#"); + lvc.cx = 42; + ListView_InsertColumn(hListView, 0, &lvc); + + lvc.iSubItem = 1; + lvc.pszText = TEXT("Title"); + lvc.cx = 500; + ListView_InsertColumn(hListView, 1, &lvc); + + ElvUpdate(); + + return hListView; +} + +LRESULT +ElvHandleNotify(LPARAM lParam) +{ + NMLISTVIEW *pNmLv; + + pNmLv = (NMLISTVIEW *)lParam; + + switch (pNmLv->hdr.code) { + case LVN_ITEMCHANGED: + if ((pNmLv->uChanged & LVIF_STATE) + && (pNmLv->uNewState & LVIS_FOCUSED)) { + g_SelectedItem = pNmLv->iItem; + ElvUpdateName(pNmLv); + //DlvShowEpisode(pNmLv->lParam); + } + break; + case NM_CUSTOMDRAW: + { + NMLVCUSTOMDRAW *pLvCd; + pLvCd = (NMLVCUSTOMDRAW *)lParam; + switch (pLvCd->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + break; + case CDDS_ITEMPREPAINT: + if (!Watched(pLvCd->nmcd.lItemlParam)) { + SelectObject(pLvCd->nmcd.hdc, + g_GUIFontBold); + return CDRF_NEWFONT; + } + break; + } + } + break; + } + + return 0; +} + +/* Update episode list. */ +void +ElvUpdate() +{ + HWND hListView; + LVITEM lviEpisode, lviName; + qid_t q; + term_t t; + + hListView = GetDlgItem(gElv_hWnd, IDC_EPISODELISTVIEW); + + ListView_DeleteAllItems(hListView); + + lviEpisode.mask = LVIF_TEXT|LVIF_PARAM; + lviName.mask = LVIF_TEXT; + + t = PL_new_term_refs(2); + PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("update_tracked_episodes", 0, "track_episodes"), + 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(szName, CP_UTF8); + 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); + } + + free(tszName); + free(tszEpisode); + } + + if (g_SelectedItem != -1) { + ListView_SetItemState(hListView, g_SelectedItem, + LVIS_SELECTED, LVIS_SELECTED); + ListView_EnsureVisible(hListView, g_SelectedItem, TRUE); + } + +close: + PL_close_query(q); +} + +/* Update episode name. */ +void +ElvUpdateName(NMLISTVIEW *pNmLv) +{ + char *szName; + HWND hListView; + TCHAR *tszName; + term_t t; + + t = PL_new_term_refs(3); + if (!PL_put_integer(t+0, pNmLv->lParam)) + 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; + + tszName = TSZFromSZ(szName, CP_UTF8); + if (!tszName) + return; + + hListView = GetDlgItem(gElv_hWnd, IDC_EPISODELISTVIEW); + ListView_SetItemText(hListView, pNmLv->iItem, 1, tszName); +} diff --git a/c/episodelistview.h b/c/episodelistview.h new file mode 100644 index 0000000..8bec69b --- /dev/null +++ b/c/episodelistview.h @@ -0,0 +1,7 @@ +#pragma once +#include + +HWND ElvCreate(HWND); +LRESULT ElvHandleNotify(LPARAM); +void ElvUpdate(void); +void ElvUpdateName(NMLISTVIEW *); diff --git a/c/listview.c b/c/listview.c new file mode 100644 index 0000000..362029f --- /dev/null +++ b/c/listview.c @@ -0,0 +1,68 @@ +#include +#include +#include + +#include "resource.h" +#include "main.h" + +extern HFONT g_GUIFont; + +WNDPROC g_PrevLvProc; + +static LRESULT CALLBACK LvProc(HWND, UINT, WPARAM, LPARAM); + +HWND +LvCreate(HWND hWnd, HMENU hMenu) +{ + HMODULE hModule; + HWND hListView; + + hListView = CreateWindowEx( + 0, + WC_LISTVIEW, + TEXT(""), + WS_CHILD|WS_VISIBLE|WS_VSCROLL|LVS_REPORT, + 0, 0, 0, 0, + hWnd, + hMenu, + GetModuleHandle(NULL), + NULL + ); + + g_PrevLvProc = (WNDPROC)SetWindowLongPtr(hListView, + GWLP_WNDPROC, (LONG_PTR)LvProc); + + ListView_SetExtendedListViewStyle(hListView, + LVS_EX_DOUBLEBUFFER); + + SendMessage(hListView, WM_SETFONT, + (WPARAM)g_GUIFont, MAKELPARAM(FALSE, 0)); + + hModule = LoadLibrary(TEXT("uxtheme.dll")); + if (hModule && GetProcAddress(hModule, "SetWindowTheme")) { + ListView_SetExtendedListViewStyle(hListView, + LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER); + SendMessage(hListView, WM_CHANGEUISTATE, + MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS), 0); + SetWindowTheme(hListView, TEXT("Explorer"), NULL); + } + + return hListView; +} + +LRESULT CALLBACK +LvProc(HWND hListView, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_NOTIFY: + switch (((NMHDR *)lParam)->code) { + case HDN_ENDTRACK: + UpdateLayout(GetParent(hListView)); + return TRUE; + } + break; + } + + return CallWindowProc(g_PrevLvProc, + hListView, uMsg, wParam, lParam); +} diff --git a/c/listview.h b/c/listview.h new file mode 100644 index 0000000..3ca0cf3 --- /dev/null +++ b/c/listview.h @@ -0,0 +1,4 @@ +#pragma once +#include + +HWND LvCreate(HWND, HMENU); diff --git a/c/main.c b/c/main.c new file mode 100644 index 0000000..abe6044 --- /dev/null +++ b/c/main.c @@ -0,0 +1,232 @@ +#include "main.h" + +#include +#include +#include +#include +#include + +#include "resource.h" +#include "common.h" +#include "episodelistview.h" +#include "main.h" + +HFONT g_GUIFont; +HFONT g_GUIFontBold; + +static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +static INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM); +static void SetupFonts(); +static int Attach(void); + +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); + + SetupFonts(); + + 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 = TEXT("Episode Browser"); + wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + RegisterClassEx(&wc); + + hWnd = CreateWindowEx( + 0, + TEXT("Episode Browser"), + 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_REFRESH: + ElvUpdate(); + break; + case ID_FILE_ABOUT: + DialogBox( + GetModuleHandle(NULL), + MAKEINTRESOURCE(IDD_ABOUT), + hWnd, + AboutDlgProc + ); + break; + } + break; + case WM_CREATE: + ElvCreate(hWnd); + break; + case WM_SIZE: + UpdateLayout(hWnd); + break; + case WM_NOTIFY: + switch (((NMHDR *)lParam)->idFrom) { + case IDC_EPISODELISTVIEW: + return ElvHandleNotify(lParam); + } + 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 r; + term_t t; + + t = PL_new_term_refs(2); + r = PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("attach", 0, "track_episodes"), + t); + if (!r) return r; + + r = PL_call_predicate(NULL, PL_Q_NORMAL, + PL_predicate("attach", 0, "episode_data"), + t); + if (!r) return r; + + return 1; +} + +void +SetupFonts() +{ + HMODULE hModule; + LOGFONT lf; + + hModule = LoadLibrary(TEXT("User32.dll")); + if (hModule && GetProcAddress(hModule, "SystemParametersInfoW")) { + NONCLIENTMETRICS m; + + m.cbSize = sizeof(NONCLIENTMETRICS); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, + sizeof(NONCLIENTMETRICS), &m, 0); + g_GUIFont = CreateFontIndirect(&m.lfMessageFont); + } else { + g_GUIFont = GetStockObject(DEFAULT_GUI_FONT); + } + + GetObject(g_GUIFont, sizeof(LOGFONT), &lf); + lf.lfWeight = FW_BOLD; + g_GUIFontBold = CreateFontIndirect(&lf); +} + +/***/ + +void +UpdateLayout(HWND hWnd) +{ + HWND hListView; + int cxColumn; + RECT rc; + static int cxVScroll = 0; + extern int g_SelectedItem; + + if (cxVScroll == 0) + cxVScroll = GetSystemMetrics(SM_CXVSCROLL); + + GetClientRect(hWnd, &rc); + hListView = GetDlgItem(hWnd, IDC_EPISODELISTVIEW); + MoveWindow(hListView, 0, 0, + rc.right, rc.bottom, + TRUE); + + cxColumn = ListView_GetColumnWidth(hListView, 0); + ListView_SetColumnWidth(hListView, 1, + rc.right-cxColumn-cxVScroll); + + ListView_EnsureVisible(hListView, g_SelectedItem, TRUE); +} diff --git a/c/main.h b/c/main.h new file mode 100644 index 0000000..582e906 --- /dev/null +++ b/c/main.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void UpdateLayout(HWND); diff --git a/c/resource.h b/c/resource.h index 8167760..00176f0 100644 --- a/c/resource.h +++ b/c/resource.h @@ -1,7 +1,8 @@ -#define IDR_MENU 101 -#define IDD_ABOUT 201 -#define IDC_ABOUTTEXT 301 -#define IDC_LISTVIEW 302 -#define ID_FILE_EXIT 4001 -#define ID_FILE_REFRESH 4002 -#define ID_FILE_ABOUT 4011 +#pragma once +#define IDR_MENU 101 +#define IDD_ABOUT 201 +#define IDC_ABOUTTEXT 301 +#define IDC_EPISODELISTVIEW 302 +#define ID_FILE_EXIT 4001 +#define ID_FILE_REFRESH 4002 +#define ID_FILE_ABOUT 4011 diff --git a/c/resource.rc b/c/resource.rc index 36fa014..bd936bb 100644 --- a/c/resource.rc +++ b/c/resource.rc @@ -1,7 +1,7 @@ #include #include "resource.h" -CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "episode_browser.manifest" +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "application.manifest" IDR_MENU MENU BEGIN diff --git a/c/win.c b/c/win.c deleted file mode 100644 index 6f82645..0000000 --- a/c/win.c +++ /dev/null @@ -1,560 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "resource.h" - -#define CLASSNAME TEXT("Episode Browser") - -HFONT g_GUIFont; -HFONT g_GUIFontBold; -WNDPROC g_PrevLvProc; - -int g_SelectedItem = -1; /* Remembered after refresh. */ - -static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -static INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM); -static LRESULT CALLBACK LvProc(HWND, UINT, WPARAM, LPARAM); - -static void CreateListView(HWND); -static LRESULT HandleListViewNotify(HWND, LPARAM); -static void UpdateLayout(HWND); - -static void SetupFonts(); -static TCHAR *TSZFromSZ(char *, int); - -static int Attach(void); -static void UpdateName(HWND, NMLISTVIEW *); -static void UpdateList(HWND); -static void ShowEpisode(HWND, int); -static int Watched(int); - -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); - - SetupFonts(); - - 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_REFRESH: - UpdateList(hWnd); - break; - case ID_FILE_ABOUT: - DialogBox( - GetModuleHandle(NULL), - MAKEINTRESOURCE(IDD_ABOUT), - hWnd, - AboutDlgProc - ); - break; - } - break; - case WM_CREATE: - CreateListView(hWnd); - break; - case WM_SIZE: - UpdateLayout(hWnd); - break; - case WM_NOTIFY: - switch (((NMHDR *)lParam)->idFrom) { - case IDC_LISTVIEW: - return HandleListViewNotify(hWnd, lParam); - } - 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; -} - -LRESULT CALLBACK -LvProc(HWND hListView, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) { - case WM_NOTIFY: - switch (((NMHDR *)lParam)->code) { - case HDN_ENDTRACK: - UpdateLayout(GetParent(hListView)); - return TRUE; - } - break; - } - - return CallWindowProc(g_PrevLvProc, - hListView, uMsg, wParam, lParam); -} - -/***/ - -void -CreateListView(HWND hWnd) -{ - HMODULE hModule; - HWND hListView; - LVCOLUMN lvc; - - hListView = CreateWindowEx( - 0, - WC_LISTVIEW, - TEXT(""), - WS_CHILD|WS_VISIBLE|WS_VSCROLL|LVS_REPORT, - 0, 0, 0, 0, - hWnd, - (HMENU)IDC_LISTVIEW, - GetModuleHandle(NULL), - NULL - ); - - g_PrevLvProc = (WNDPROC)SetWindowLongPtr(hListView, - GWLP_WNDPROC, (LONG_PTR)LvProc); - - SendMessage(hListView, WM_SETFONT, - (WPARAM)g_GUIFont, MAKELPARAM(FALSE, 0)); - - ListView_SetExtendedListViewStyle(hListView, - LVS_EX_DOUBLEBUFFER); - - hModule = LoadLibrary(TEXT("uxtheme.dll")); - if (hModule && GetProcAddress(hModule, "SetWindowTheme")) { - ListView_SetExtendedListViewStyle(hListView, - LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER); - SendMessage(hListView, WM_CHANGEUISTATE, - MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS), 0); - SetWindowTheme(hListView, TEXT("Explorer"), NULL); - } - - lvc.mask = LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM; - - lvc.iSubItem = 0; - lvc.pszText = TEXT("#"); - lvc.cx = 42; - ListView_InsertColumn(hListView, 0, &lvc); - - lvc.iSubItem = 1; - lvc.pszText = TEXT("Title"); - lvc.cx = 500; - ListView_InsertColumn(hListView, 1, &lvc); - - UpdateList(hWnd); -} - -LRESULT -HandleListViewNotify(HWND hWnd, LPARAM lParam) -{ - NMLISTVIEW *pNmLv; - - pNmLv = (NMLISTVIEW *)lParam; - - switch (pNmLv->hdr.code) { - case LVN_ITEMCHANGED: - if ((pNmLv->uChanged & LVIF_STATE) - && (pNmLv->uNewState & LVIS_FOCUSED)) { - g_SelectedItem = pNmLv->iItem; - UpdateName(hWnd, pNmLv); - //ShowEpisode(hWnd, pNmLv->lParam); - } - break; - case NM_CUSTOMDRAW: - { - NMLVCUSTOMDRAW *pLvCd; - pLvCd = (NMLVCUSTOMDRAW *)lParam; - switch (pLvCd->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - return CDRF_NOTIFYITEMDRAW; - break; - case CDDS_ITEMPREPAINT: - if (!Watched(pLvCd->nmcd.lItemlParam)) { - SelectObject(pLvCd->nmcd.hdc, - g_GUIFontBold); - return CDRF_NEWFONT; - } - break; - } - } - break; - } - - return 0; -} - -void -UpdateLayout(HWND hWnd) -{ - HWND hListView; - int cxColumn; - RECT rc; - static int cxVScroll = 0; - - if (cxVScroll == 0) - cxVScroll = GetSystemMetrics(SM_CXVSCROLL); - - GetClientRect(hWnd, &rc); - hListView = GetDlgItem(hWnd, IDC_LISTVIEW); - MoveWindow(hListView, 0, 0, - rc.right, rc.bottom, - TRUE); - - cxColumn = ListView_GetColumnWidth(hListView, 0); - ListView_SetColumnWidth(hListView, 1, - rc.right-cxColumn-cxVScroll); - - ListView_EnsureVisible(hListView, g_SelectedItem, TRUE); -} - -/***/ - -void -SetupFonts() -{ - HMODULE hModule; - LOGFONT lf; - - hModule = LoadLibrary(TEXT("User32.dll")); - if (hModule && GetProcAddress(hModule, "SystemParametersInfoW")) { - NONCLIENTMETRICS m; - - m.cbSize = sizeof(NONCLIENTMETRICS); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, - sizeof(NONCLIENTMETRICS), &m, 0); - g_GUIFont = CreateFontIndirect(&m.lfMessageFont); - } else { - g_GUIFont = GetStockObject(DEFAULT_GUI_FONT); - } - - GetObject(g_GUIFont, sizeof(LOGFONT), &lf); - lf.lfWeight = FW_BOLD; - g_GUIFontBold = CreateFontIndirect(&lf); -} - -/* Convert zero-terminated non-wide (multi-byte) string to - * zero-terminated wide/non-wide string depending on UNICODE. */ -TCHAR * -TSZFromSZ(char *sz, int iCp) -{ - TCHAR *tsz; - -#ifdef UNICODE - int cbMultiByte, cchWideChar; - - cbMultiByte = strlen(sz)+1; - cchWideChar = MultiByteToWideChar(iCp, 0, sz, cbMultiByte, NULL, 0); - tsz = malloc(cchWideChar*sizeof(WCHAR)); - if (!tsz) - return NULL; - if (!MultiByteToWideChar(iCp, 0, sz, cbMultiByte, tsz, cchWideChar)) - return NULL; -#else - tsz = malloc(strlen(sz)+1); - if (!tsz) - return NULL; - strcpy(tsz, sz); -#endif - - return tsz; -} - -/***/ - -/* Attach persistent databases. */ -int -Attach() -{ - int r; - term_t t; - - t = PL_new_term_refs(2); - r = PL_call_predicate(NULL, PL_Q_NORMAL, - PL_predicate("attach", 0, "track_episodes"), - t); - if (!r) return r; - - r = PL_call_predicate(NULL, PL_Q_NORMAL, - PL_predicate("attach", 0, "episode_data"), - t); - if (!r) return r; - - return 1; -} - -/* Show episode data. */ -void -ShowEpisode(HWND hWnd, int iEpisode) -{ - int r; - term_t t; - - t = PL_new_term_refs(3); - if(!PL_put_integer(t+0, iEpisode)) - return; - - r = PL_call_predicate(NULL, PL_Q_NORMAL, - PL_predicate("lookup_episode_local", 3, "episode_data"), - t); - if (!r) - return; - - /* The episode data is a list of unary compounds, - * whose functor is the key and whose argument is the value. */ - - { - term_t tHead, tList; - - tHead = PL_new_term_ref(); - tList = PL_copy_term_ref(t+2); - - while (PL_get_list(tList, tHead, tList)) { - atom_t aKey; - 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; - if (!PL_get_atom_chars(tValue, &szValue)) - continue; - - printf("%s/%d: %s\n", szKey, iArity, szValue); - } - } -} - -/* Update episode name. */ -void -UpdateName(HWND hWnd, NMLISTVIEW *pNmLv) -{ - char *szName; - HWND hListView; - TCHAR *tszName; - term_t t; - - t = PL_new_term_refs(3); - if (!PL_put_integer(t+0, pNmLv->lParam)) - 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; - - tszName = TSZFromSZ(szName, CP_UTF8); - if (!tszName) - return; - - hListView = GetDlgItem(hWnd, IDC_LISTVIEW); - ListView_SetItemText(hListView, pNmLv->iItem, 1, tszName); -} - -/* Update episode list. */ -void -UpdateList(HWND hWnd) -{ - HWND hListView; - LVITEM lviEpisode, lviName; - qid_t q; - term_t t; - - hListView = GetDlgItem(hWnd, IDC_LISTVIEW); - - ListView_DeleteAllItems(hListView); - - lviEpisode.mask = LVIF_TEXT|LVIF_PARAM; - lviName.mask = LVIF_TEXT; - - t = PL_new_term_refs(2); - PL_call_predicate(NULL, PL_Q_NORMAL, - PL_predicate("update_tracked_episodes", 0, "track_episodes"), - 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(szName, CP_UTF8); - 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); - } - - free(tszName); - free(tszEpisode); - } - - if (g_SelectedItem != -1) { - ListView_SetItemState(hListView, g_SelectedItem, - LVIS_SELECTED, LVIS_SELECTED); - ListView_EnsureVisible(hListView, g_SelectedItem, TRUE); - } - -close: - PL_close_query(q); -} - -int -Watched(int iEpisode) -{ - term_t t; - - t = PL_new_term_refs(1); - if (!PL_put_integer(t+0, iEpisode)) - return 0; - - return PL_call_predicate(NULL, PL_Q_NORMAL, - PL_predicate("watched", 1, "track_episodes"), - t); -} -- cgit v1.2.3