diff options
Diffstat (limited to 'c')
-rw-r--r-- | c/episodelistview.cpp | 12 | ||||
-rw-r--r-- | c/ext.cpp | 132 | ||||
-rw-r--r-- | c/ext.h | 8 | ||||
-rw-r--r-- | c/resource.rc | 2 | ||||
-rw-r--r-- | c/test.cpp | 3 | ||||
-rw-r--r-- | c/util.h | 22 | ||||
-rw-r--r-- | c/win.h | 9 |
7 files changed, 165 insertions, 23 deletions
diff --git a/c/episodelistview.cpp b/c/episodelistview.cpp index 6caff99..14aa192 100644 --- a/c/episodelistview.cpp +++ b/c/episodelistview.cpp @@ -7,6 +7,7 @@ #include "data.h" #include "datalistview.h" #include "episodelistview.h" +#include "ext.h" #include "listview.h" #include "pl.h" #include "util.h" @@ -68,25 +69,20 @@ void EpisodeListView::HandleContextMenu(const WORD command) /* Process other commands. */ switch (command) { case IDM_WATCH_LOCALLY: - if (!Pl("local_episode","open_episode_locally",lvi.lParam)) + if(!OpenLocally(lvi.lParam)) cNotFound++; break; case IDM_WATCH_ONLINE: - Pl("local_episode","open_episode_online",lvi.lParam); + OpenOnline(lvi.lParam); break; case IDM_TOGGLE: e.bWatched = !e.bWatched; break; - case IDM_FORGET: - Pl("track_episodes","forget_episode",lvi.lParam); - Pl("track_episodes","update_tracked_episodes"); - break; - case IDM_WIKI: - Pl("episode_data","open_episode_wiki",lvi.lParam); + OpenWiki(lvi.lParam); break; } } diff --git a/c/ext.cpp b/c/ext.cpp new file mode 100644 index 0000000..f485dce --- /dev/null +++ b/c/ext.cpp @@ -0,0 +1,132 @@ +#include <ctype.h> +#include <string> +#include <string_view> + +#include "data.h" + +extern CfgA& g_cfg; +extern FileView<ElvDataA> g_fvElv; +extern FileView<DlvDataA> g_fvDlv; + +void OpenOnline(int iEp) +{ + wchar_t url[sizeof(g_cfg.url)+4]; + Swprintf(url, L"%s%d", g_cfg.url, iEp); + INT_PTR r = reinterpret_cast<INT_PTR>( + ShellExecute(nullptr, L"open", url, nullptr, nullptr, SW_SHOWNORMAL)); + if (r <= 32) + throw Win32Error(); +} + +void OpenWiki(int iEp) +{ + const DlvDataA& d = g_fvDlv.At(iEp-1); + wchar_t url[sizeof(d.wiki)+35]; + Swprintf(url, L"https://www.detectiveconanworld.com%s", d.wiki); + INT_PTR r = reinterpret_cast<INT_PTR>( + ShellExecute(nullptr, L"open", url, nullptr, nullptr, SW_SHOWNORMAL)); + if (r <= 32) + throw Win32Error(); +} + +static inline bool MatchFileName(wchar_t (&file)[MAX_PATH], const wchar_t* const siEp) noexcept +{ + /* This is a hand-written parser that matches file names of + * the type "Detective Conan - 010.mkv". */ + + wchar_t* f = file; + + /* Match Detective Conan prefix. */ + if (*f != L'D' && *f != L'd') + return false; + f++; + + if (wcsncmp(f, L"etective", 8) != 0) + return false; + f += 8; + + if (*f != L' ' && *f != L'_' && *f != L'-') + return false; + f++; + + if (*f != L'C' && *f != L'c') + return false; + f++; + + if (wcsncmp(f, L"onan", 4) != 0) + return false; + f += 4; + + /* Match garbage before episode number. */ + while (*f == L' ' || *f == L'_' || *f == L'-' || *f == L'0') + f++; + + /* Match episode number. */ + size_t lenEp = wcslen(siEp); + if (wcsncmp(f, siEp, lenEp) != 0) + return false; + f += lenEp; + + if (isdigit(*f)) + return false; + + return true; +} + +static bool FindMatchingFile(wchar_t (&file)[MAX_PATH], const wchar_t* const root, + const wchar_t* const siEp, const int level = 0) +{ + /* Don't recurse too much. */ + if (level > 3) + return false; + + wchar_t pat[MAX_PATH]; + Swprintf(pat, L"%s\\*", root); + + WIN32_FIND_DATA fdata; + HANDLE hf = FindFirstFile(pat, &fdata); + if (hf == INVALID_HANDLE_VALUE) + throw Win32Error().what(); + + do { + if (fdata.cFileName[0] == L'.') + continue; + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + /* Recurse into directory. */ + wchar_t root2[MAX_PATH]; + Swprintf(root2, L"%s\\%s", root, fdata.cFileName); + try { + if (FindMatchingFile(file, root2, siEp, level+1)) + return true; + } catch (...) { + FindClose(hf); + throw; + } + } + else /* Try to match file name. */ + if (MatchFileName(fdata.cFileName, siEp)) { + Swprintf(file, L"%s\\%s", root, fdata.cFileName); + return true; + } + } while (FindNextFile(hf, &fdata)); + + DWORD e = GetLastError(); + FindClose(hf); + if (e != ERROR_NO_MORE_FILES) + throw Win32Error(e); + + return false; +} + +bool OpenLocally(int iEp) +{ + wchar_t file[MAX_PATH]; + if (FindMatchingFile(file, g_cfg.root, g_fvElv.At(iEp-1).siEp)) { + INT_PTR r = reinterpret_cast<INT_PTR>( + ShellExecute(nullptr, L"open", file, nullptr, nullptr, SW_SHOWNORMAL)); + if (r <= 32) + throw Win32Error(); + return true; + } else + return false; +} @@ -0,0 +1,8 @@ +#ifndef EXT_H +#define EXT_H + +bool OpenOnline(int iEp); +bool OpenWiki(int iEp); +bool OpenLocally(int iEp); + +#endif diff --git a/c/resource.rc b/c/resource.rc index 102f627..2b7a9a7 100644 --- a/c/resource.rc +++ b/c/resource.rc @@ -34,8 +34,6 @@ BEGIN MENUITEM "&Watch Locally", IDM_WATCH_LOCALLY MENUITEM "Watch &Online", IDM_WATCH_ONLINE MENUITEM "&Toggle", IDM_TOGGLE - MENUITEM "&Forget", IDM_FORGET - MENUITEM "&Lookup", IDM_LOOKUP MENUITEM "Wi&ki", IDM_WIKI POPUP "&Rate" BEGIN @@ -3,6 +3,7 @@ #include "data.h" #include "episodelistview.h" +#include "ext.h" #include "pl.h" #include "util.h" #include "win.h" @@ -16,7 +17,7 @@ struct Test Test(const char* name) : name(name) {} }; -#define TESTS struct UNUSED +#define TESTS struct _ #define TEST(id) }; struct id : public Test { id() : Test(#id) #define FAIL(...) do { Sprintf(error, __VA_ARGS__); return; } while (0) @@ -7,11 +7,22 @@ #include <SWI-Prolog.h> #include <windows.h> +#define CONCAT_IMPL(a, b) a##b +#define CONCAT(a, b) CONCAT_IMPL(a, b) +#define _ CONCAT(unused_, __LINE__) + inline size_t Min(size_t a, size_t b) { return a < b? a: b; } +inline int Cmp(const int a, const int b) +{ + if (a == b) return 0; + if (a > b) return 1; + return -1; +} + /* Buf is a span-like structure of a buffer and its size. */ template <typename T> struct Buf @@ -58,15 +69,4 @@ inline char* Strcpy(Buf<char> dst, const char* const src) return dst; } -inline int Cmp(const int a, const int b) -{ - if (a == b) return 0; - if (a > b) return 1; - return -1; -} - -#define CAT(a, b) a##b -#define APPLY(a, ...) a(__VA_ARGS__) -#define UNUSED APPLY(CAT, unused_, __COUNTER__) - #endif @@ -7,12 +7,19 @@ /* Run given procedure at creation of next window. */ void WithNextWindow(void (*proc)(HWND)); + /* Display message box centered in main window. */ int EBMessageBox(std::wstring_view text, std::wstring_view data, UINT uType); + /* Show message box for current exception. */ -void ShowException(const wchar_t* fmt, const wchar_t* title, UINT uType = MB_ICONWARNING) noexcept; +void ShowException( + const wchar_t* fmt = L"An error occurred: %s", + const wchar_t* title = L"Error", + UINT uType = MB_ICONWARNING) noexcept; + /* Retrieve mouse position relative to given window's client area. */ int GetRelativeCursorPos(HWND hWnd, POINT* pt) noexcept; + /* Cached values from GetSystemMetrics. */ template <int I> auto Metric = GetSystemMetrics(I); |