From 295d423cc47f9ee8a72134dc544892a03b279311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Sun, 10 Jul 2022 23:23:09 +0200 Subject: Convert to C++. I already hit upon some object-oriented programming patterns in *listview.c, so I felt that it would be natural to use this as an opportunity to learn C++. --- c/main.cpp | 593 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 593 insertions(+) create mode 100644 c/main.cpp (limited to 'c/main.cpp') diff --git a/c/main.cpp b/c/main.cpp new file mode 100644 index 0000000..6799280 --- /dev/null +++ b/c/main.cpp @@ -0,0 +1,593 @@ +#include +#include +#include +#include + +#include "resource.h" +#include "defs.h" + +EpisodeListView g_elv; +DataListView g_dlv; + +atom_t g_aThread; +char g_szLimitScreenwriter[64] = {0}; +HFONT g_hfNormal; +HFONT g_fBold; +HMENU g_hPopupMenu; +HWND g_hFocus; +HWND g_hWnd; +HWND g_hWndStatus; +int g_bViewTVOriginal = 1; +int g_bViewWatched = 1; +int g_bThread = 0; +int g_iDPI = -1; +static int g_bThemes; +static int g_cxVScroll; +static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +static INT_PTR CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM); +static HWND CreateStatusBar(HWND, HINSTANCE); +static int Attach(void); +static void SetupFonts(void); +static void UpdateTheme(void); + +int WINAPI +WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, INT nCmdShow) +{ + char *argv[2]; + HMODULE hModule; + HWND hWnd; + LPTSTR tszErr; + MSG msg; + INITCOMMONCONTROLSEX icc; + WNDCLASSEX wc; + + /* Set constant values. */ + hModule = LoadLibrary(TEXT("uxtheme.dll")); + g_bThemes = hModule && GetProcAddress(hModule,"SetWindowTheme"); + if (hModule) FreeLibrary(hModule); + g_cxVScroll = GetSystemMetrics(SM_CXVSCROLL); + + /* Initialize Prolog. */ + argv[0] = (char *)"EpisodeBrowser"; + argv[1] = NULL; + tszErr = TEXT("Could not initialize Prolog."); + if (!PL_initialise(1, argv)) goto f; + tszErr = TEXT("Could not attach databases."); + if (!Attach()) goto f; + + /* Initialize common controls, load menu and register window class. */ + icc.dwSize = sizeof(icc); + icc.dwICC = ICC_WIN95_CLASSES; + tszErr = TEXT("Could not initialize common controls."); + if (!InitCommonControlsEx(&icc)) goto f; + + g_hPopupMenu = LoadMenu(NULL, MAKEINTRESOURCE(IDR_POPUPMENU)); + g_hPopupMenu = GetSubMenu(g_hPopupMenu, 0); + + memset(&wc, 0, sizeof(WNDCLASSEX)); + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = WndProc; + 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); + tszErr = TEXT("Could not register window class."); + if (!RegisterClassEx(&wc)) goto f; + + /* Create window. */ + hWnd = CreateWindowEx( + 0, + TEXT("Episode Browser"), + TEXT("Episode Browser"), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, + NULL, NULL, hInstance, NULL + ); + tszErr = TEXT("Could not create main window."); + if (!hWnd) goto f; + g_hWndStatus = CreateStatusBar(hWnd, hInstance); + ShowWindow(hWnd, nCmdShow); + + /* Populate episode list view. */ + Pl("track_episodes", "update_tracked_episodes", ""); + g_elv.Update(); + g_elv.SelectFocus(); + + while (GetMessage(&msg, NULL, 0, 0) > 0) { + if (IsDialogMessage(hWnd, &msg)) continue; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + PL_halt(0); + return 0; +f: MessageBox(NULL, tszErr, TEXT("Error"), MB_ICONERROR); + PL_halt(1); + return 1; +} + +LRESULT CALLBACK +WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_CREATE: + { + HMODULE hModule; + UINT (*GetDpiForWindow)(HWND); + g_hWnd = hWnd; + g_iDPI = 96; + hModule = LoadLibrary(TEXT("User32.dll")); + if (hModule && (GetDpiForWindow = (UINT (*)(HWND))GetProcAddress(hModule, "GetDpiForWindow"))) { + g_iDPI = GetDpiForWindow(g_hWnd); + FreeLibrary(hModule); + } + SetWindowPos(hWnd, NULL, -1, -1, Dpi(510), Dpi(400), SWP_NOMOVE); + if (Pl("cfg","get_view_watched","i",&g_bViewWatched)) + CheckMenuItem(GetMenu(hWnd), IDM_VIEW_WATCHED, + g_bViewWatched? MF_CHECKED: MF_UNCHECKED); + if (Pl("cfg","get_view_tv_original","i",&g_bViewTVOriginal)) + CheckMenuItem(GetMenu(hWnd), IDM_VIEW_TV_ORIGINAL, + g_bViewTVOriginal? MF_CHECKED: MF_UNCHECKED); + { + char *sz; + if (!Pl("cfg","get_limit_screenwriter","s",&sz)) + goto s; + strcpy_s(g_szLimitScreenwriter, + sizeof(g_szLimitScreenwriter), sz); + CheckMenuItem(GetMenu(hWnd), IDM_VIEW_OTHERS, + g_szLimitScreenwriter[0]? MF_UNCHECKED: MF_CHECKED); + } + s: SetupFonts(); + g_dlv.Create(); + g_elv.Create(); + UpdateTheme(); + SetFocus(g_elv.HWnd()); + break; + } + case WM_CLOSE: + DestroyWindow(hWnd); + break; + case WM_DESTROY: + { + LVITEM lvi; + HWND hElv; + hElv = g_elv.HWnd(); + lvi.mask = LVIF_PARAM; + if ((lvi.iItem=ListView_GetNextItem(hElv,-1,LVNI_FOCUSED)) != -1 + && ListView_GetItem(hElv, &lvi)) + Pl("cfg","set_focus","I",lvi.lParam); + PostQuitMessage(0); + break; + } + case WM_SIZE: + SendMessage(g_hWndStatus, WM_SIZE, wParam, lParam); + UpdateLayout(); + break; + case WM_GETMINMAXINFO: + { + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + lpMMI->ptMinTrackSize.y = g_dlv.Height(0)+80; + break; + } + case WM_THEMECHANGED: + UpdateTheme(); + break; + case 0x02E0: /* WM_DPICHANGED */ + { + LPRECT lpr; + lpr = (LPRECT)lParam; + g_iDPI = HIWORD(wParam); + SetWindowPos(hWnd, NULL, + lpr->left, lpr->top, + lpr->right-lpr->left, + lpr->bottom-lpr->top, + SWP_NOZORDER | SWP_NOACTIVATE); + UpdateLayout(); + break; + } + case WM_ACTIVATE: + switch (wParam) { + case WA_INACTIVE: + g_hFocus = GetFocus(); + break; + case WA_ACTIVE: + case WA_CLICKACTIVE: + SetFocus(g_hFocus); + Pl("track_episodes","update_tracked_episodes",""); + g_elv.Redraw(); + } + break; + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case IDC_EPISODELISTVIEW: + return g_elv.HandleNotify(lParam); + } + break; + case WM_TIMER: + switch (wParam) { + case IDT_TIMER: + { + static int i = 0; + if (Pl("episode_data","thread_running","A",g_aThread)) { + i = (i+1)%4; + SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(1,0), + (LPARAM)(i==0? TEXT("."): + i==1? TEXT(".."): + i==2? TEXT("..."): + TEXT(""))); + } else { + i = 0; + KillTimer(hWnd, IDT_TIMER); + g_elv.Update(); + } + break; + } + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDM_FILE_EXIT: + PostMessage(hWnd, WM_CLOSE, 0, 0); + break; + case IDM_FILE_REFRESH: + g_elv.Update(); + break; + case IDM_FILE_FETCH_DATA: + if (g_bThread) break; + Pl("episode_data","thread_create","Sa","update_episode_data",&g_aThread); + goto t; + case IDM_FILE_FETCH_SCREENWRITERS: + if (g_bThread) break; + Pl("episode_data","thread_create","Sa","update_screenwriters",&g_aThread); + t: KillTimer(hWnd, IDT_TIMER); + SetTimer(hWnd, IDT_TIMER, 500, NULL); + SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(1,0), (LPARAM)TEXT(".")); + g_bThread = 1; + break; + case IDM_FILE_ABOUT: + DialogBox( + GetModuleHandle(NULL), + MAKEINTRESOURCE(IDD_ABOUT), + hWnd, + AboutDlgProc + ); + break; + case IDM_VIEW_WATCHED: + { + int iEpFocus; + HWND hElv; + hElv = g_elv.HWnd(); + CheckMenuItem(GetMenu(hWnd), IDM_VIEW_WATCHED, + g_bViewWatched? MF_UNCHECKED: MF_CHECKED); + g_bViewWatched = !g_bViewWatched; + g_elv.Update(); + Pl("cfg","set_view_watched","I",g_bViewWatched); + iEpFocus = ListView_GetNextItem(hElv, -1, LVNI_FOCUSED); + if (iEpFocus == -1) break; + ListView_EnsureVisible(hElv, iEpFocus, TRUE); + break; + } + case IDM_VIEW_TV_ORIGINAL: + { + int iEpFocus; + HWND hElv; + hElv = g_elv.HWnd(); + CheckMenuItem(GetMenu(hWnd), IDM_VIEW_TV_ORIGINAL, + g_bViewTVOriginal? MF_UNCHECKED: MF_CHECKED); + g_bViewTVOriginal = !g_bViewTVOriginal; + g_elv.Update(); + Pl("cfg","set_view_tv_original","I",g_bViewTVOriginal); + iEpFocus = ListView_GetNextItem(hElv, -1, LVNI_FOCUSED); + if (iEpFocus == -1) break; + ListView_EnsureVisible(hElv, iEpFocus, TRUE); + break; + } + case IDM_VIEW_OTHERS: /* Show/hide other screenwriters. */ + { + int iEpFocus; + HWND hElv; + hElv = g_elv.HWnd(); + if (g_szLimitScreenwriter[0]) { + CheckMenuItem(GetMenu(hWnd), IDM_VIEW_OTHERS, + MF_CHECKED); + g_szLimitScreenwriter[0] = 0; + } else { + char *sz; + LVITEM lvi; + iEpFocus = ListView_GetNextItem(hElv, -1, LVNI_FOCUSED); + if (iEpFocus == -1) break; + lvi.iItem = iEpFocus; + lvi.mask = LVIF_PARAM; + if (!ListView_GetItem(hElv, &lvi)) break; + if (!Pl("episode_data","episode_datum","ISs", + lvi.lParam,"Screenwriter",&sz)) + break; + strcpy_s(g_szLimitScreenwriter, + sizeof(g_szLimitScreenwriter), sz); + CheckMenuItem(GetMenu(hWnd), IDM_VIEW_OTHERS, + MF_UNCHECKED); + } + g_elv.Update(); + Pl("cfg","set_limit_screenwriter","S", + g_szLimitScreenwriter); + iEpFocus = ListView_GetNextItem(hElv, -1, LVNI_FOCUSED); + if (iEpFocus == -1) break; + ListView_EnsureVisible(hElv, iEpFocus, TRUE); + break; + } + case IDM_WATCH_LOCALLY: + case IDM_WATCH_ONLINE: + case IDM_TOGGLE: + case IDM_FORGET: + case IDM_LOOKUP: + case IDM_WIKI: + case IDM_RATE10: + case IDM_RATE9: + case IDM_RATE8: + case IDM_RATE7: + case IDM_RATE6: + case IDM_RATE5: + case IDM_RATE4: + case IDM_RATE3: + case IDM_RATE2: + case IDM_RATE1: + case IDM_RATE0: + { + int iRating; + LVITEM lvi; + HWND hElv; + hElv = g_elv.HWnd(); + + /* Look through selected items, applying the + * selected command to each one. */ + + lvi.mask = LVIF_PARAM; + lvi.iItem = -1; + while ((lvi.iItem = ListView_GetNextItem( + hElv, lvi.iItem, LVNI_SELECTED)) != -1) { + if (!ListView_GetItem(hElv, &lvi)) goto b; + + switch (LOWORD(wParam)) { + case IDM_WATCH_LOCALLY: + Pl("local_episode","open_episode_locally","I",lvi.lParam); + break; + case IDM_WATCH_ONLINE: + Pl("local_episode","open_episode_online","I",lvi.lParam); + break; + case IDM_TOGGLE: + Pl("track_episodes","toggle_episode","I",lvi.lParam); + g_elv.Redraw(); + break; + case IDM_FORGET: + Pl("track_episodes","forget_episode","I",lvi.lParam); + Pl("track_episodes","update_tracked_episodes",""); + g_elv.Redraw(); + break; + case IDM_LOOKUP: + Pl("episode_data","retract_episode","I",lvi.lParam); + g_elv.UpdateItem(&lvi); + g_elv.Redraw(); + g_dlv.ShowEpisode(lvi.lParam); + break; + case IDM_WIKI: + Pl("episode_data","open_episode_wiki","I",lvi.lParam); + break; + case IDM_RATE10: + iRating = 10; + goto r; + case IDM_RATE9: + iRating = 9; + goto r; + case IDM_RATE8: + iRating = 8; + goto r; + case IDM_RATE7: + iRating = 7; + goto r; + case IDM_RATE6: + iRating = 6; + goto r; + case IDM_RATE5: + iRating = 5; + goto r; + case IDM_RATE4: + iRating = 4; + goto r; + case IDM_RATE3: + iRating = 3; + goto r; + case IDM_RATE2: + iRating = 2; + goto r; + case IDM_RATE1: + iRating = 1; + goto r; + case IDM_RATE0: + iRating = 0; + r: Pl("episode_data","rate_episode","II",lvi.lParam,iRating); + g_elv.UpdateItem(&lvi); + break; + } + } + switch (LOWORD(wParam)) { + case IDM_RATE10: + case IDM_RATE9: + case IDM_RATE8: + case IDM_RATE7: + case IDM_RATE6: + case IDM_RATE5: + case IDM_RATE4: + case IDM_RATE3: + case IDM_RATE2: + case IDM_RATE1: + case IDM_RATE0: + g_elv.Redraw(); + g_elv.DoSort(); + g_elv.ShowFocus(); + } + b: break; + } + } + 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_CLOSE: + EndDialog(hWnd, IDOK); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hWnd, IDOK); + break; + } + break; + default: + return FALSE; + } + + return TRUE; +} + +HWND +CreateStatusBar(HWND hWndParent, HINSTANCE hInstance) +{ + HWND hWnd; + hWnd = CreateWindowEx( + 0, + STATUSCLASSNAME, + (LPCTSTR) NULL, + WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP, + 0, 0, 0, 0, + hWndParent, + (HMENU)ID_STATUS, + hInstance, + NULL + ); + return hWnd; +} + +/***/ + +/* Attach persistent databases. */ +int +Attach() +{ + if (!Pl("track_episodes","attach","")) return 0; + if (!Pl("episode_data","attach","")) return 0; + 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_hfNormal = CreateFontIndirect(&m.lfMessageFont); + FreeLibrary(hModule); + } else + g_hfNormal = static_cast(GetStockObject(DEFAULT_GUI_FONT)); + + GetObject(g_hfNormal, sizeof(LOGFONT), &lf); + lf.lfWeight = FW_BOLD; + g_fBold = CreateFontIndirect(&lf); +} + +/***/ + +void +UpdateLayout() +{ + int cxColumn, cyDlv, yStatus; + RECT rc, rcStatus; + HWND hDlv, hElv; + + hDlv = g_dlv.HWnd(); + hElv = g_elv.HWnd(); + + GetClientRect(g_hWnd, &rc); + GetClientRect(g_hWndStatus, &rcStatus); + yStatus = rcStatus.bottom-rcStatus.top; + + /* Resize data list view. */ + SendMessage(hDlv, WM_SETREDRAW, FALSE, 0); + SendMessage(hElv, WM_SETREDRAW, FALSE, 0); + cyDlv = rc.bottom-yStatus-g_dlv.Height(DLVSIKEY); + MoveWindow(hDlv, 0, cyDlv, rc.right, rc.bottom-yStatus-cyDlv, TRUE); + ListView_SetColumnWidth(hDlv, DLVSIKEY, LVSCW_AUTOSIZE); + cxColumn = ListView_GetColumnWidth(hDlv, 0)+4; + ListView_SetColumnWidth(hDlv, DLVSIKEY, cxColumn); + ListView_SetColumnWidth(hDlv, DLVSIVALUE, rc.right-cxColumn-g_cxVScroll-4); + + /* Resize episode list view. */ + MoveWindow(hElv, 0, 0, rc.right, cyDlv+1, TRUE); + ListView_SetColumnWidth(hElv, ELVSIEPISODE, LVSCW_AUTOSIZE); + cxColumn = ListView_GetColumnWidth(hElv, ELVSIEPISODE)+4; + ListView_SetColumnWidth(hElv, ELVSIEPISODE, cxColumn); + cxColumn += ListView_GetColumnWidth(hElv, ELVSIRATING); + ListView_SetColumnWidth(hElv, ELVSITITLE, rc.right-cxColumn-g_cxVScroll-4); + SendMessage(hElv, WM_SETREDRAW, TRUE, 0); + SendMessage(hDlv, WM_SETREDRAW, TRUE, 0); + + /* Resize status bar parts. */ + { + int aParts[] = {rc.right-Dpi(55), rc.right}; + SendMessage(g_hWndStatus, SB_SETPARTS, + (WPARAM)sizeof(aParts), (LPARAM)aParts); + } +} + +/* Try to style application according to current Windows theme. */ +void +UpdateTheme() +{ + DWORD dwStyle; + LPTSTR tszTheme; + WORD wAction; + HWND hDlv, hElv; + + hDlv = g_dlv.HWnd(); + hElv = g_elv.HWnd(); + + if (!g_bThemes) return; + if (IsThemeActive()) { + dwStyle = LVS_EX_DOUBLEBUFFER; + tszTheme = TEXT("Explorer"); + wAction = UIS_SET; + } else { + dwStyle = 0; + tszTheme = NULL; + wAction = UIS_CLEAR; + } + + /* Use modern "Explorer" theme. */ + SetWindowTheme(hElv, tszTheme, NULL); + SetWindowTheme(hDlv, tszTheme, NULL); + + /* The modern theme requires double buffering. */ + ListView_SetExtendedListViewStyleEx(hElv, LVS_EX_DOUBLEBUFFER, dwStyle); + ListView_SetExtendedListViewStyleEx(hDlv, LVS_EX_DOUBLEBUFFER, dwStyle); + + /* Hide focus rectangles. */ + SendMessage(hElv, WM_UPDATEUISTATE, MAKEWPARAM(wAction, UISF_HIDEFOCUS), 0); + SendMessage(hDlv, WM_UPDATEUISTATE, MAKEWPARAM(wAction, UISF_HIDEFOCUS), 0); +} -- cgit v1.2.3