#include #include #include #include #include "resource.h" #include "defs.h" HWND HElv; int ISort; LVITEM LviElvFocus; /* Focused episode. */ static int CALLBACK ElvSort(LPARAM, LPARAM, LPARAM); HWND ElvCreate() { LVCOLUMN lvc; term_t t; HElv = LvCreate((HMENU)IDC_EPISODELISTVIEW, 0); lvc.mask = LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM; lvc.iSubItem = ELVSIEPISODE; lvc.pszText = TEXT("#"); lvc.cx = Dpi(42); ListView_InsertColumn(HElv, ELVSIEPISODE, &lvc); lvc.iSubItem = ELVSITITLE; lvc.pszText = TEXT("Title"); lvc.cx = 500; ListView_InsertColumn(HElv, ELVSITITLE, &lvc); lvc.iSubItem = ELVSIRATING; lvc.pszText = TEXT("/"); lvc.cx = Dpi(30); ListView_InsertColumn(HElv, ELVSIRATING, &lvc); t = T(1); P("cfg","get_sort",1,t); GI(t,&ISort) ISort = 1; return HElv; } void ElvDoSort() { ListView_SortItemsEx(HElv, ElvSort, ISort); } LRESULT ElvHandleNotify(LPARAM lParam) { LPNMLISTVIEW lpNmLv; lpNmLv = (LPNMLISTVIEW)lParam; switch (lpNmLv->hdr.code) { case LVN_ITEMCHANGED: /* Select/focus episode. */ if ((lpNmLv->uChanged & LVIF_STATE) && (lpNmLv->uNewState & LVIS_FOCUSED)) { LviElvFocus.iItem = lpNmLv->iItem; LviElvFocus.lParam = lpNmLv->lParam; ElvUpdateItem(&LviElvFocus); DlvShowEpisode(lpNmLv->lParam); } break; case LVN_COLUMNCLICK: /* Sort by column. */ { int iColumn; term_t t; t = T(1); iColumn = lpNmLv->iSubItem+1; ISort = abs(ISort) == iColumn? -ISort: iColumn; PI(t,ISort) goto s; P("cfg","set_sort",1,t); s: ElvDoSort(); ElvShowFocus(); break; } case LVN_KEYDOWN: /* Navigate episodes by keyboard. */ { LPNMLVKEYDOWN lpNmLvKd; lpNmLvKd = (LPNMLVKEYDOWN)lParam; switch (lpNmLvKd->wVKey) { case VK_LEFT: ElvSelectUnwatched(-1); break; case VK_RIGHT: ElvSelectUnwatched(1); break; } break; } case NM_CUSTOMDRAW: /* Make unwatched episodes bold. */ { LPNMLVCUSTOMDRAW lpLvCd; lpLvCd = (LPNMLVCUSTOMDRAW)lParam; switch (lpLvCd->nmcd.dwDrawStage) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW; break; case CDDS_ITEMPREPAINT: { term_t t; extern HFONT HfBold; t = T(1); PI(t,lpLvCd->nmcd.lItemlParam) break; P("track_episodes","watched",1,t) { SelectObject(lpLvCd->nmcd.hdc, HfBold); return CDRF_NEWFONT; } break; } } break; } case NM_DBLCLK: /* Open clicked episode. */ { term_t t; t = T(1); PI(t,LviElvFocus.lParam) break; P("local_episodes","open_episode_locally",1,t) P("local_episodes","open_episode_online",1,t); break; } case NM_RETURN: /* Open all selected episodes. */ { LVITEM lvi; term_t t; extern HWND HElv; lvi.mask = LVIF_PARAM; lvi.iItem = -1; t = T(1); while ((lvi.iItem = ListView_GetNextItem( HElv, lvi.iItem, LVNI_SELECTED)) != -1) { if (!ListView_GetItem(HElv, &lvi)) goto b; PI(t,lvi.lParam) goto b; P("local_episodes","open_episode",1,t); } b: break; } case NM_RCLICK: { DWORD dwPos; extern HWND HWnd; extern HMENU HPopupMenu; dwPos = GetMessagePos(); TrackPopupMenu(HPopupMenu, TPM_RIGHTBUTTON, LOWORD(dwPos), HIWORD(dwPos), 0, HWnd, NULL); break; } } return 0; } void ElvRedraw() { RedrawWindow(HElv, NULL, NULL, RDW_ERASE|RDW_FRAME|RDW_INVALIDATE|RDW_ALLCHILDREN); } void ElvSetTop(int iItem) { int iLast; iLast = ListView_GetItemCount(HElv)-1; ListView_EnsureVisible(HElv, iLast, TRUE); ListView_EnsureVisible(HElv, iItem, TRUE); } /* Select most recent episode. */ void ElvSelectRecent() { int i, iEpisode, iItem; LVFINDINFO lvfi; term_t t; t = T(1); iItem = 0; P("track_episodes","most_recently_watched",1,t) goto s; GI(t,&iEpisode) return; lvfi.flags = LVFI_PARAM; lvfi.lParam = iEpisode; i = 0; while ((iItem = ListView_FindItem(HElv, -1, &lvfi)) == -1 && i++ < 100) lvfi.lParam = ++iEpisode; if (iItem != -1) goto s; iEpisode -= 100; lvfi.lParam = iEpisode; i = 0; while ((iItem = ListView_FindItem(HElv, -1, &lvfi)) == -1 && i++ < 100) lvfi.lParam = --iEpisode; if (iItem != -1) goto s; return; s: ListView_SetItemState(HElv, -1, LVIF_STATE, LVIS_SELECTED); ElvSetTop(iItem > 5? iItem-5: 0); ListView_SetItemState(HElv, iItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED); LviElvFocus.iItem = iItem; LviElvFocus.lParam = iEpisode; ElvUpdateItem(&LviElvFocus); DlvShowEpisode(iEpisode); } /* Select next/previous unwatched episode. */ void ElvSelectUnwatched(int iDir) { int bShift, i, iEpNew, iItemNew; LVFINDINFO lvfi; LVITEM lviFocus; term_t t; /* Get focused episode. */ lviFocus.mask = LVIF_PARAM; if ((lviFocus.iItem = ListView_GetNextItem(HElv, -1, LVNI_FOCUSED)) != -1 && ListView_GetItem(HElv, &lviFocus)) ; else return; i = 0; lvfi.flags = LVFI_PARAM; lvfi.lParam = lviFocus.lParam; do { t = T(2); PI(t,lvfi.lParam) return; P("track_episodes",iDir > 0? "next_unwatched": "previous_unwatched",2,t) return; GI(t+1,&iEpNew) return; lvfi.lParam = iEpNew; if ((iItemNew = ListView_FindItem(HElv, -1, &lvfi)) != -1) { ListView_SetItemState(HElv,-1,LVIF_STATE,LVIS_SELECTED); ListView_SetSelectionMark(HElv, iItemNew); ListView_SetItemState(HElv, iItemNew, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED); ElvRedraw(); ListView_EnsureVisible(HElv, iItemNew, TRUE); return; } } while (i++ < 1000); } void ElvShowFocus() { int iEpFocus; iEpFocus = ListView_GetNextItem(HElv, -1, LVNI_FOCUSED); if (iEpFocus == -1) return; ListView_EnsureVisible(HElv, iEpFocus, TRUE); } /* Sort list view items, iSort being the 1-based index of the column * to sort by. If iSort is negative, the order is descending. */ int CALLBACK ElvSort(LPARAM iItem1, LPARAM iItem2, LPARAM iSort) { int iOrder; LVITEM lvi1, lvi2; lvi1.mask = lvi2.mask = LVIF_PARAM; lvi1.iItem = iItem1; lvi2.iItem = iItem2; if (!ListView_GetItem(HElv, &lvi1)) return 0; if (!ListView_GetItem(HElv, &lvi2)) return 0; iOrder = Cmp(iSort, 0); switch (abs(iSort)-1) { case ELVSIEPISODE: return iOrder*Cmp(lvi1.lParam, lvi2.lParam); break; case ELVSIRATING: { int iRating1, iRating2; term_t t, t2; iRating1 = iSort > 0? 99: -1; iRating2 = iSort > 0? 99: -1; t = T(2); PI(t,lvi1.lParam) goto e; P("episode_data","episode_rating",2,t) goto e; GI(t+1,&iRating1); e: t2 = T(2); PI(t2,lvi2.lParam) goto f; P("episode_data","episode_rating",2,t2) goto f; GI(t2+1,&iRating2); f: if (iRating1 == iRating2) return Cmp(lvi1.lParam, lvi2.lParam); return iOrder*Cmp(iRating1, iRating2); break; } case ELVSITITLE: { char *sz1, *sz2; int cch, cch1, cch2; term_t t, t2; t = T(2); PI(t,lvi1.lParam) return 0; P("episode_data","episode_title",2,t) return 0; GAC(t+1,&sz1) return 0; t2 = T(2); PI(t2,lvi2.lParam) return 0; P("episode_data","episode_title",2,t2) return 0; GAC(t2+1,&sz2) return 0; cch1 = strlen(sz1); cch2 = strlen(sz2); cch = cch1 > cch2? cch2: cch1; return iOrder*_strnicmp(sz1, sz2, cch); break; } default: return 0; } } /* Update episode list. */ void ElvUpdate() { int cEp, i, iEp, iEpFocus, iItem, iItemMark, iItemTopNew; LVITEM lvi, lviEpisode, lviTop; LVFINDINFO lvfi; term_t t; extern HWND HWndStatus; static TCHAR tszDisp[16], tszEpisode[16], tszTotal[16]; static int aEpSel[2048]; lviEpisode.mask = LVIF_TEXT|LVIF_PARAM; /* Save scrolling position. */ lviTop.iItem = ListView_GetTopIndex(HElv); lviTop.mask = LVIF_PARAM; ListView_GetItem(HElv, &lviTop); /* Save selected episodes. */ i = 0; lvi.mask = LVIF_PARAM; lvi.iItem = -1; while ((lvi.iItem = ListView_GetNextItem( HElv, lvi.iItem, LVNI_SELECTED)) != -1 && i < 2048) if (ListView_GetItem(HElv, &lvi)) aEpSel[i++] = lvi.lParam; aEpSel[i] = 0; iItemMark = ListView_GetSelectionMark(HElv); /* Save focus. */ iEpFocus = 0; if ((lvi.iItem = ListView_GetNextItem(HElv, -1, LVNI_FOCUSED)) != -1 && ListView_GetItem(HElv, &lvi)) iEpFocus = lvi.lParam; SendMessage(HElv, WM_SETREDRAW, FALSE, 0); ListView_DeleteAllItems(HElv); t = T(1); P("episode_data","ensure_episode_data",0,t) return; P("episode_data","episode_count",1,t) return; GI(t,&cEp) return; for (iEp = 1, iItem = 0; iEp <= cEp; iEp++) { extern char SzLimitScreenwriter[]; extern int BViewTVOriginal, BViewWatched; if (SzLimitScreenwriter[0]) { atom_t a1, a2; term_t t; t = T(3); a1 = A("Screenwriter"); a2 = A(SzLimitScreenwriter); PI(t,iEp) goto a; PA(t+1,a1) goto a; PA(t+2,a2) goto a; P("episode_data","episode_datum",3,t) continue; } a: if (!BViewWatched) { term_t t; t = T(1); PI(t,iEp) goto b; P("track_episodes","watched",1,t) goto b; continue; } b: if (!BViewTVOriginal) { term_t t; t = T(1); PI(t,iEp) goto c; P("episode_data","tv_original",1,t) goto c; continue; } /* Format episode number string. */ c: _stprintf_s(tszEpisode, sizeof(tszEpisode), TEXT("%d"), iEp); /* Insert item. */ lviEpisode.iItem = iItem++; lviEpisode.iSubItem = ELVSIEPISODE; lviEpisode.pszText = tszEpisode; lviEpisode.lParam = iEp; ListView_InsertItem(HElv, &lviEpisode); ElvUpdateItem(&lviEpisode); } ElvDoSort(); lvfi.flags = LVFI_PARAM; /* Reset selection. */ for (i = 0; aEpSel[i]; i++) { int iItemSel; lvfi.lParam = aEpSel[i]; if ((iItemSel = ListView_FindItem(HElv, -1, &lvfi)) != -1) ListView_SetItemState(HElv, iItemSel, LVIS_SELECTED, LVIS_SELECTED); } if (iItemMark != -1) ListView_SetSelectionMark(HElv, iItemMark); /* Reset focus. */ if (iEpFocus) { int iItemFocus; i = 0; do lvfi.lParam = iEpFocus+i; while ((iItemFocus = ListView_FindItem(HElv, -1, &lvfi)) == -1 && i++ < 100); if (iItemFocus != -1) ListView_SetItemState(HElv, iItemFocus, LVIS_FOCUSED, LVIS_FOCUSED); } /* Try to reset scrolling position. Note that this must be * done last, as focusing an item scrolls it into view. */ i = 0; do lvfi.lParam = lviTop.lParam+i; while ((iItemTopNew = ListView_FindItem(HElv, -1, &lvfi)) == -1 && i++ < 100); if (iItemTopNew != -1) ElvSetTop(iItemTopNew); _stprintf_s(tszDisp, sizeof(tszDisp), TEXT("%d"), iItem); SendMessage(HWndStatus, SB_SETTEXT, MAKEWPARAM(1,0), (LPARAM)tszDisp); _stprintf_s(tszTotal, sizeof(tszTotal), TEXT("%d"), cEp); SendMessage(HWndStatus, SB_SETTEXT, MAKEWPARAM(2,0), (LPARAM)tszTotal); SendMessage(HElv, WM_SETREDRAW, TRUE, 0); } /* Update episode name and rating. */ void ElvUpdateItem(LPLVITEM lpLvi) { char *szName; int iRating; TCHAR *tszName; term_t t, t2; static TCHAR tszRating[3]; t = T(2); tszName = NULL; PI(t,lpLvi->lParam) goto r; P("episode_data","episode_title",2,t) { P("episode_data","update_episode_data",0,t) goto r; P("episode_data","episode_title",2,t) goto r; } GAC(t+1,&szName) goto r; tszName = TszFromSz(szName, CP_UTF8); if (tszName) ListView_SetItemText(HElv, lpLvi->iItem, ELVSITITLE, tszName); r: t2 = T(2); PI(t2,lpLvi->lParam) goto f; P("episode_data","episode_rating",2,t2) { ListView_SetItemText(HElv, lpLvi->iItem, ELVSIRATING, TEXT("")); goto f; } GI(t2+1,&iRating) goto f; _stprintf_s(tszRating, sizeof(tszRating), TEXT("%d"), iRating); ListView_SetItemText(HElv, lpLvi->iItem, ELVSIRATING, tszRating); f: if (tszName) free(tszName); }