aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-02-15 16:29:59 +0100
committerJohn Ankarström <john@ankarstrom.se>2022-02-15 16:29:59 +0100
commit52fb337856497cb151081f3738e7cfa4bc2883bd (patch)
tree3309edbd0834bf90a5635b93038d1d088224e96e
parent09ec144a99f1234a37d04854bdfb81485540be97 (diff)
downloadEpisodeBrowser-52fb337856497cb151081f3738e7cfa4bc2883bd.tar.gz
Rework list view code.
-rw-r--r--Makefile6
-rw-r--r--c/application.manifest (renamed from c/episode_browser.manifest)0
-rw-r--r--c/common.c45
-rw-r--r--c/common.h5
-rw-r--r--c/datalistview.c60
-rw-r--r--c/datalistview.h5
-rw-r--r--c/episodelistview.c200
-rw-r--r--c/episodelistview.h7
-rw-r--r--c/listview.c68
-rw-r--r--c/listview.h4
-rw-r--r--c/main.c232
-rw-r--r--c/main.h4
-rw-r--r--c/resource.h15
-rw-r--r--c/resource.rc2
-rw-r--r--c/win.c560
15 files changed, 644 insertions, 569 deletions
diff --git a/Makefile b/Makefile
index a8b8276..cffc2bf 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,10 @@
PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
-INPUTS = $(PROJECT_ROOT)c/win.c
+INPUTS = $(PROJECT_ROOT)c/main.c
+INPUTS += $(PROJECT_ROOT)c/common.c
+INPUTS += $(PROJECT_ROOT)c/listview.c
+INPUTS += $(PROJECT_ROOT)c/episodelistview.c
+INPUTS += $(PROJECT_ROOT)c/datalistview.c
INPUTS += resource.obj
INPUTS += $(PROJECT_ROOT)pl/track_episodes.pl
INPUTS += $(PROJECT_ROOT)pl/local_episodes.pl
diff --git a/c/episode_browser.manifest b/c/application.manifest
index 978f471..978f471 100644
--- a/c/episode_browser.manifest
+++ b/c/application.manifest
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 <windows.h>
+#include <SWI-Prolog.h>
+
+#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 <windows.h>
+
+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 <windows.h>
+#include <SWI-Prolog.h>
+
+#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 <windows.h>
+
+void DlvCreate(HWND);
+void DlvShowEpisode(int);
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 <windows.h>
+#include <commctrl.h>
+#include <TCHAR.H>
+#include <SWI-Prolog.h>
+
+#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 <windows.h>
+
+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 <windows.h>
+#include <commctrl.h>
+#include <uxtheme.h>
+
+#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 <windows.h>
+
+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 <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <string.h>
+#include <SWI-Prolog.h>
+
+#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 <windows.h>
+
+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 <windows.h>
#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 <windows.h>
-#include <TCHAR.H>
-#include <commctrl.h>
-#include <uxtheme.h>
-#include <stdio.h>
-#include <string.h>
-#include <SWI-Prolog.h>
-#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);
-}