diff options
author | John Ankarström <john@ankarstrom.se> | 2022-08-15 22:24:31 +0200 |
---|---|---|
committer | John Ankarström <john@ankarstrom.se> | 2022-08-15 22:24:31 +0200 |
commit | 246fd1369dce903dad48730478bebbe9733ac88e (patch) | |
tree | 50a6dc779453ef430a13fd13c9ca21a4d2c3f2a1 | |
parent | 8877627ee3fcd5a8f0ce2fcccec7688973e9cb2d (diff) | |
download | EpisodeBrowser-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.h | 74 | ||||
-rw-r--r-- | c/episodelistview.cpp | 115 | ||||
-rw-r--r-- | c/episodelistview.h | 5 | ||||
-rw-r--r-- | c/main.cpp | 4 | ||||
-rw-r--r-- | c/test.cpp | 25 | ||||
-rw-r--r-- | pl/episode_data.pl | 4 |
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 @@ -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; @@ -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), |