aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-08-15 22:24:31 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-08-15 22:24:31 +0200
commit246fd1369dce903dad48730478bebbe9733ac88e (patch)
tree50a6dc779453ef430a13fd13c9ca21a4d2c3f2a1
parent8877627ee3fcd5a8f0ce2fcccec7688973e9cb2d (diff)
downloadEpisodeBrowser-246fd1369dce903dad48730478bebbe9733ac88e.tar.gz
Start moving data into C++.
This is the first step in the process of getting rid of the SWI Prolog dependency. We'll see how it goes.
-rw-r--r--c/data.h74
-rw-r--r--c/episodelistview.cpp115
-rw-r--r--c/episodelistview.h5
-rw-r--r--c/main.cpp4
-rw-r--r--c/test.cpp25
-rw-r--r--pl/episode_data.pl4
6 files changed, 160 insertions, 67 deletions
diff --git a/c/data.h b/c/data.h
new file mode 100644
index 0000000..a2b6cac
--- /dev/null
+++ b/c/data.h
@@ -0,0 +1,74 @@
+#ifndef DATA_H
+#define DATA_H
+
+#include "pl.h"
+#include "util.h"
+#include "wcharptr.h"
+
+struct ElvData
+{
+ int rating = 0;
+ bool bWatched = false;
+ bool bTVOriginal = false;
+ wchar_t sRating[3] = {0};
+ wchar_t siEp[5] = {0};
+ wchar_t title[128] = {0};
+};
+
+struct DlvData
+{
+ wchar_t date[64] = {0};
+ wchar_t source[64] = {0};
+ wchar_t screenwriter[64] = {0};
+ wchar_t hint[128] = {0};
+ wchar_t wiki[128] = {0};
+};
+
+inline int FromWeb(const int iEp, ElvData& e, DlvData& d) noexcept
+{
+ WcharPtr title, wiki, date, source, hint;
+ const int r = Pl("episode_data","fetch_episode_data",iEp,&title,&wiki,&date,&source,&hint);
+ if (title) Wcscpy(e.title, title);
+ if (wiki) Wcscpy(d.wiki, wiki);
+ if (date) Wcscpy(d.date, date);
+ if (source) Wcscpy(d.source, source);
+ if (hint) Wcscpy(d.hint, hint);
+ return r;
+}
+
+inline bool FromProlog(const int iEp, ElvData& e) noexcept
+{
+ if (WcharPtr title; Pl("episode_data","episode_title",iEp,&title))
+ Wcscpy(e.title, title);
+ else
+ return false;
+
+ if (Pl("episode_data","episode_rating",iEp,&e.rating))
+ Swprintf(e.sRating, L"%d", e.rating);
+
+ if (Pl("episode_data","tv_original",iEp))
+ e.bTVOriginal = true;
+
+ if (Pl("track_episodes","watched",iEp))
+ e.bWatched = true;
+
+ Swprintf(e.siEp, L"%d", iEp);
+
+ return true;
+}
+
+inline void FromProlog(const int iEp, DlvData& d) noexcept
+{
+ if (WcharPtr wiki; Pl("episode_data","episode_wiki",iEp,&wiki))
+ Wcscpy(d.wiki, wiki);
+ if (WcharPtr screenwriter; Pl("episode_data","episode_datum",iEp,"Screenwriter",&screenwriter))
+ Wcscpy(d.screenwriter, screenwriter);
+ if (WcharPtr date; Pl("episode_data","episode_datum",iEp,"Date",&date))
+ Wcscpy(d.date, date);
+ if (WcharPtr source; Pl("episode_data","episode_datum",iEp,"Source",&source))
+ Wcscpy(d.source, source);
+ if (WcharPtr hint; Pl("episode_data","episode_datum",iEp,"Hint",&hint))
+ Wcscpy(d.hint, hint);
+}
+
+#endif
diff --git a/c/episodelistview.cpp b/c/episodelistview.cpp
index 3856ce0..e591952 100644
--- a/c/episodelistview.cpp
+++ b/c/episodelistview.cpp
@@ -4,6 +4,7 @@
#include <SWI-Prolog.h>
#include "resource.h"
+#include "data.h"
#include "datalistview.h"
#include "episodelistview.h"
#include "listview.h"
@@ -49,10 +50,18 @@ LRESULT EpisodeListView::HandleNotify(const LPARAM lParam)
const NMLISTVIEW* const nm = reinterpret_cast<NMLISTVIEW*>(lParam);
switch (nm->hdr.code) {
+ case LVN_GETDISPINFO: /* Display item. */
+ {
+ NMLVDISPINFO* const nm = reinterpret_cast<NMLVDISPINFO*>(lParam);
+ ElvData& e = m_vData.at(nm->item.lParam-1);
+ wchar_t* vs[] = {e.siEp, e.title, e.sRating}; /* ELVSIEPISODE, ELVSITITLE, ELVSIRATING */
+ nm->item.pszText = vs[nm->item.iSubItem];
+ return 0;
+ }
+
case LVN_ITEMCHANGED: /* Select/focus episode. */
if ((nm->uChanged & LVIF_STATE) && (nm->uNewState & LVIS_FOCUSED)) {
extern DataListView* const g_dlv;
- UpdateItem(nm->iItem, nm->lParam);
g_dlv->ShowEpisode(nm->lParam);
}
return 0;
@@ -174,8 +183,7 @@ void EpisodeListView::RestoreFocus()
if (iItem != -1) goto s;
return;
-s: UpdateItem(iItem, iEpisode);
- g_dlv->ShowEpisode(iEpisode);
+s: g_dlv->ShowEpisode(iEpisode);
ListView_SetItemState(hWnd, -1, LVIF_STATE, LVIS_SELECTED);
SetTop(iItem > 5? iItem-5: 0);
ListView_SetItemState(hWnd, iItem,
@@ -210,7 +218,7 @@ void EpisodeListView::SelectUnwatched(int dir)
lvfi.lParam = lviFocus.lParam;
do {
- if (!Pl("track_episodes",dir > 0? "next_unwatched": "previous_unwatched",
+ if (!Pl("track_episodes",dir > 0? "push_unwatched": "previous_unwatched",
lvfi.lParam,&iEpNew))
return;
@@ -285,7 +293,8 @@ int CALLBACK EpisodeListView::SortProc(const LPARAM iItem1, const LPARAM iItem2,
void EpisodeListView::Update()
{
- if (!Pl("episode_data","ensure_episode_data")) return;
+ if (!Pl("episode_data","ensure_episode_data"))
+ return;
/* Save scrolling position. */
int iEpTop = 0;
@@ -314,41 +323,53 @@ void EpisodeListView::Update()
iEpFocus = lvi.lParam;
}
- SendMessage(hWnd, WM_SETREDRAW, FALSE, 0);
- ListView_DeleteAllItems(hWnd);
-
- int cEp;
- if (!Pl("episode_data","episode_count",&cEp)) return;
-
- int cItem = 0;
{
- wchar_t siEp[16];
+ int cEp;
+ if (!Pl("episode_data","episode_count",&cEp))
+ return;
+
+ int cItem = 0;
LVITEM lviEpisode = {LVIF_TEXT|LVIF_PARAM};
+
+ /* Retrieve episode data and add list view items. */
+ SendMessage(hWnd, WM_SETREDRAW, FALSE, 0);
+ m_vData.clear();
+ ListView_DeleteAllItems(hWnd);
for (int iEp = 1; iEp <= cEp; iEp++) {
- extern char g_limitScreenwriter[];
- extern int g_bViewTVOriginal, g_bViewWatched;
+ ElvData e;
- if (g_limitScreenwriter[0]
- && !Pl("episode_data","episode_datum",
- iEp,"Screenwriter",g_limitScreenwriter))
- continue;
+ if (!FromProlog(iEp, e))
+ goto push;
- if (!g_bViewWatched)
- if (Pl("track_episodes","watched",iEp)) continue;
+ if (extern char g_limitScreenwriter[];
+ g_limitScreenwriter[0] && !Pl("episode_data","episode_datum",iEp,
+ "Screenwriter",g_limitScreenwriter))
+ goto push;
- if (!g_bViewTVOriginal)
- if (Pl("episode_data","tv_original",iEp)) continue;
+ if (extern int g_bViewWatched; !g_bViewWatched && e.bWatched)
+ goto push;
- swprintf_s(siEp, sizeof(siEp)/sizeof(*siEp), L"%d", iEp);
+ if (extern int g_bViewTVOriginal; !g_bViewTVOriginal && e.bTVOriginal)
+ goto push;
/* Insert item. */
lviEpisode.iItem = cItem++;
lviEpisode.iSubItem = ELVSIEPISODE;
- lviEpisode.pszText = siEp;
+ lviEpisode.pszText = LPSTR_TEXTCALLBACK;
lviEpisode.lParam = iEp;
ListView_InsertItem(hWnd, &lviEpisode);
- UpdateItem(lviEpisode.iItem, lviEpisode.lParam);
+
+ ListView_SetItemText(hWnd, lviEpisode.iItem, ELVSITITLE, LPSTR_TEXTCALLBACK);
+ ListView_SetItemText(hWnd, lviEpisode.iItem, ELVSIRATING, LPSTR_TEXTCALLBACK);
+
+ push: m_vData.push_back(std::move(e));
}
+
+ /* Show number of displayed items in status bar. */
+ extern HWND g_hWndStatus;
+ wchar_t disp[16];
+ Swprintf(disp, L"%d", cItem);
+ SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(1,0), reinterpret_cast<LPARAM>(disp));
}
Sort();
@@ -361,8 +382,7 @@ void EpisodeListView::Update()
int iItemSel;
lvfi.lParam = iEpSel;
if ((iItemSel = ListView_FindItem(hWnd, -1, &lvfi)) != -1)
- ListView_SetItemState(hWnd, iItemSel,
- LVIS_SELECTED, LVIS_SELECTED);
+ ListView_SetItemState(hWnd, iItemSel, LVIS_SELECTED, LVIS_SELECTED);
}
if (iItemMark != -1)
ListView_SetSelectionMark(hWnd, iItemMark);
@@ -373,11 +393,9 @@ void EpisodeListView::Update()
int i = 0;
do
lvfi.lParam = iEpFocus+i;
- while ((iItemFocus = ListView_FindItem(hWnd, -1, &lvfi)) == -1
- && i++ < 100);
+ while ((iItemFocus = ListView_FindItem(hWnd, -1, &lvfi)) == -1 && i++ < 100);
if (iItemFocus != -1)
- ListView_SetItemState(hWnd, iItemFocus,
- LVIS_FOCUSED, LVIS_FOCUSED);
+ ListView_SetItemState(hWnd, iItemFocus, LVIS_FOCUSED, LVIS_FOCUSED);
}
/* Try to reset scrolling position. Note that this must be
@@ -387,42 +405,14 @@ void EpisodeListView::Update()
int i = 0;
do
lvfi.lParam = iEpTop+i;
- while ((iItemTopNew = ListView_FindItem(hWnd, -1, &lvfi)) == -1
- && i++ < 100)
- ;
+ while ((iItemTopNew = ListView_FindItem(hWnd, -1, &lvfi)) == -1 && i++ < 100);
if (iItemTopNew != -1)
SetTop(iItemTopNew);
}
- /* Show number of displayed items in status bar. */
- extern HWND g_hWndStatus;
- wchar_t disp[16];
- swprintf_s(disp, sizeof(disp)/sizeof(*disp), L"%d", cItem);
- SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(1,0), reinterpret_cast<LPARAM>(disp));
-
SendMessage(hWnd, WM_SETREDRAW, TRUE, 0);
}
-void EpisodeListView::UpdateItem(const int iItem, const LPARAM lParam)
-{
- WcharPtr name;
- if (!Pl("episode_data","episode_title",lParam,&name)) {
- if (!Pl("episode_data","update_episode_data")) goto r;
- if (!Pl("episode_data","episode_title",lParam,&name)) goto r;
- }
- ListView_SetItemText(hWnd, iItem, ELVSITITLE, name);
-
-r: int rating;
- if (!Pl("episode_data","episode_rating",lParam,&rating)) {
- ListView_SetItemText(hWnd, iItem, ELVSIRATING, const_cast<wchar_t*>(L""));
- return;
- }
-
- wchar_t sRating[3];
- Swprintf(sRating, L"%d", rating);
- ListView_SetItemText(hWnd, iItem, ELVSIRATING, sRating);
-}
-
LRESULT CALLBACK EpisodeListView::WndProc(const HWND hWnd, const UINT uMsg,
const WPARAM wParam, const LPARAM lParam)
{
@@ -438,7 +428,8 @@ LRESULT CALLBACK EpisodeListView::WndProc(const HWND hWnd, const UINT uMsg,
MSG* const msg = reinterpret_cast<MSG*>(lParam);
if (lParam && msg->message == WM_KEYDOWN && msg->wParam == VK_RETURN)
return DLGC_WANTMESSAGE;
- return lResult;
+ else
+ return lResult;
}
}
diff --git a/c/episodelistview.h b/c/episodelistview.h
index 532c76c..7e0c539 100644
--- a/c/episodelistview.h
+++ b/c/episodelistview.h
@@ -1,9 +1,11 @@
#ifndef EPISODELISTVIEW_H
#define EPISODELISTVIEW_H
+#include <vector>
#include <windows.h>
#include <commctrl.h>
+#include "data.h"
#include "listview.h"
#define ELVSIEPISODE 0
@@ -26,12 +28,11 @@ struct EpisodeListView : public ListView
void Sort();
/* Update episode list. */
void Update();
- /* Update episode name and rating. */
- void UpdateItem(int iItem, LPARAM lParam);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) override;
private:
int m_iSortCol;
static int CALLBACK SortProc(LPARAM lParam1, LPARAM lParam2, LPARAM extra);
+ std::vector<ElvData> m_vData;
};
#endif
diff --git a/c/main.cpp b/c/main.cpp
index 74c99ca..14b4904 100644
--- a/c/main.cpp
+++ b/c/main.cpp
@@ -481,7 +481,6 @@ void HandleContextMenu(const HWND, const WORD command)
if (ID_SUBGROUP(command) == IDG_CTX_RATE) {
/* Process rate commands. */
Pl("episode_data","rate_episode",lvi.lParam,ID_RATING(command));
- g_elv->UpdateItem(lvi.iItem, lvi.lParam);
} else {
/* Process other commands. */
switch (command) {
@@ -503,9 +502,8 @@ void HandleContextMenu(const HWND, const WORD command)
Pl("track_episodes","update_tracked_episodes");
break;
- case IDM_LOOKUP:
+ case IDM_LOOKUP: /* TODO: Remove IDM_LOOKUP. */
Pl("episode_data","retract_episode",lvi.lParam);
- g_elv->UpdateItem(lvi.iItem, lvi.lParam);
g_dlv->ShowEpisode(lvi.lParam);
break;
diff --git a/c/test.cpp b/c/test.cpp
index 9230974..67a32c9 100644
--- a/c/test.cpp
+++ b/c/test.cpp
@@ -23,12 +23,37 @@ TESTS
if (dst[9] != 0)
FAIL("dst is not NUL-terminated");
}
+
+ TEST(EpisodeDataFromWeb)
+ {
+ ElvData e;
+ DlvData d;
+ FromWeb(10, e, d);
+ if (wcscmp(e.title, L"Pro Soccer Player Blackmail Case") != 0)
+ FAIL("title is not correct");
+ if (wcscmp(d.date, L"March 11, 1996") != 0)
+ FAIL("date is not correct");
+ }
+
+ TEST(EpisodeDataFromProlog)
+ {
+ ElvData e;
+ DlvData d;
+ FromProlog(10, e);
+ FromProlog(10, d);
+ if (wcscmp(e.title, L"Pro Soccer Player Blackmail Case") != 0)
+ FAIL("title is not correct");
+ if (wcscmp(d.date, L"March 11, 1996") != 0)
+ FAIL("date is not correct");
+ }
};
int RunTests()
{
const Test tests[] = {
StrcpyWithSmallerDestination{},
+ //EpisodeDataFromWeb{},
+ EpisodeDataFromProlog{},
};
printf("Results (%llu tests):\n", sizeof(tests)/sizeof(*tests));
diff --git a/pl/episode_data.pl b/pl/episode_data.pl
index 167594d..5b83546 100644
--- a/pl/episode_data.pl
+++ b/pl/episode_data.pl
@@ -115,6 +115,10 @@ maybe_assert_episode_datum(Ep, Key, Value) :-
episode_number(Ep) --> integer(Ep).
episode_number(Ep) --> integer(Ep), "WPS", integer(_).
+% Temporary helper.
+fetch_episode_data(Ep, Title, W, Date, Source, Hint) :-
+ fetch_episode_info(Ep, ['Title'-Title, 'Wiki'-W, 'Date'-Date, 'Source'-Source, 'Hint'-Hint]).
+
fetch_episode_info(Ep, ['Title'-Title, 'Wiki'-W, 'Date'-Date, 'Source'-Source, 'Hint'-Hint]) :-
cached_html('https://www.detectiveconanworld.com/wiki/Anime', H),
xpath(H, //tr(td(index(3),@style='background:#f2fde9;')), R),