#include #include #include #include #include #include #include #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); }