/* * $Id$ * * (C) 2003-2006 Gabest * (C) 2006-2010 see AUTHORS * * This file is part of mplayerc. * * Mplayerc is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Mplayerc is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "stdafx.h" #include "mplayerc.h" #include "MainFrm.h" #include "PlayerSubresyncBar.h" // CPlayerSubresyncBar IMPLEMENT_DYNAMIC(CPlayerSubresyncBar, CSizingControlBarG) CPlayerSubresyncBar::CPlayerSubresyncBar() { m_rt = 0; m_fUnlink = false; m_lastSegment = -1; } CPlayerSubresyncBar::~CPlayerSubresyncBar() { } BOOL CPlayerSubresyncBar::Create(CWnd* pParentWnd, CCritSec* pSubLock) { if(!CSizingControlBarG::Create(_T("Subresync"), pParentWnd, ID_VIEW_SUBRESYNC)) return FALSE; m_pSubLock = pSubLock; m_list.CreateEx( WS_EX_DLGMODALFRAME|WS_EX_CLIENTEDGE, WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_TABSTOP|LVS_REPORT/*|LVS_SHOWSELALWAYS*/|LVS_AUTOARRANGE|LVS_NOSORTHEADER, CRect(0,0,100,100), this, IDC_SUBRESYNCLIST); m_list.SetExtendedStyle(m_list.GetExtendedStyle()|LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER); return TRUE; } BOOL CPlayerSubresyncBar::PreCreateWindow(CREATESTRUCT& cs) { if(!CSizingControlBarG::PreCreateWindow(cs)) return FALSE; return TRUE; } BOOL CPlayerSubresyncBar::PreTranslateMessage(MSG* pMsg) { if(IsWindow(pMsg->hwnd) && IsVisible() && pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) { if(IsShortCut(pMsg) || IsDialogMessage(pMsg)) return TRUE; } return CSizingControlBarG::PreTranslateMessage(pMsg); } void CPlayerSubresyncBar::SetTime(__int64 rt) { m_rt = rt; int curSegment; if(!m_sts.SearchSubs((int)(rt/10000), 25, &curSegment)) { curSegment = -1; } if(m_lastSegment != curSegment) m_list.Invalidate(); m_lastSegment = curSegment; } void CPlayerSubresyncBar::SetSubtitle(ISubStream* pSubStream, double fps) { m_pSubStream = pSubStream; m_mode = NONE; m_lastSegment = -1; m_sts.Empty(); ResetSubtitle(); if(!m_pSubStream) return; CLSID clsid; m_pSubStream->GetClassID(&clsid); if(clsid == __uuidof(CVobSubFile)) { CVobSubFile* pVSF = (CVobSubFile*)(ISubStream*)m_pSubStream; m_mode = VOBSUB; _ASSERT(pVSF->m_iLang >= 0); CAtlArray& sp = pVSF->m_langs[pVSF->m_iLang].subpos; for(int i = 0, j = sp.GetCount(); i < j; i++) { CString str; str.Format(_T("%d,%d,%d,%d"), sp[i].vobid, sp[i].cellid, sp[i].fForced, i); m_sts.Add(TToW(str), false, (int)sp[i].start, (int)sp[i].stop); } m_sts.CreateDefaultStyle(DEFAULT_CHARSET); pVSF->m_fOnlyShowForcedSubs = false; for(int i = 0, j = m_list.GetHeaderCtrl()->GetItemCount(); i < j; i++) m_list.DeleteColumn(0); m_list.InsertColumn(COL_START, _T("Time"), LVCFMT_LEFT, 80); m_list.InsertColumn(COL_END, _T("End"), LVCFMT_LEFT, 80); m_list.InsertColumn(COL_PREVSTART, _T("Preview"), LVCFMT_LEFT, 80); m_list.InsertColumn(COL_PREVEND, _T("End"), LVCFMT_LEFT, 80); m_list.InsertColumn(COL_VOBID, _T("Vob ID"), LVCFMT_CENTER, 60); m_list.InsertColumn(COL_CELLID, _T("Cell ID"), LVCFMT_CENTER, 60); m_list.InsertColumn(COL_FORCED, _T("Forced"), LVCFMT_CENTER, 60); } else if(clsid == __uuidof(CRenderedTextSubtitle)) { CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream; m_mode = TEXTSUB; m_sts.Copy(*pRTS); m_sts.ConvertToTimeBased(fps); m_sts.Sort(true); /*!!m_fUnlink*/ #ifndef UNICODE if(!m_sts.IsEntryUnicode(0)) { CFont* f = m_list.GetFont(); LOGFONT lf; f->GetLogFont(&lf); lf.lfCharSet = m_sts.GetCharSet(0); m_font.DeleteObject(); m_font.CreateFontIndirect(&lf); m_list.SetFont(&m_font); } #endif for(int i = 0, j = m_list.GetHeaderCtrl()->GetItemCount(); i < j; i++) m_list.DeleteColumn(0); m_list.InsertColumn(COL_START, _T("Time"), LVCFMT_LEFT, 90); m_list.InsertColumn(COL_END, _T("End"), LVCFMT_LEFT, 4); m_list.InsertColumn(COL_PREVSTART, _T("Preview"), LVCFMT_LEFT, 80); m_list.InsertColumn(COL_PREVEND, _T("End"), LVCFMT_LEFT, 4); m_list.InsertColumn(COL_TEXT, _T("Text"), LVCFMT_LEFT, 275); m_list.InsertColumn(COL_STYLE, _T("Style"), LVCFMT_LEFT, 80); m_list.InsertColumn(COL_FONT, _T("Font"), LVCFMT_LEFT, 60); m_list.InsertColumn(COL_CHARSET, _T("CharSet"), LVCFMT_CENTER, 20); m_list.InsertColumn(COL_UNICODE, _T("Unicode"), LVCFMT_CENTER, 40); m_list.InsertColumn(COL_LAYER, _T("Layer"), LVCFMT_CENTER, 50); m_list.InsertColumn(COL_ACTOR, _T("Actor"), LVCFMT_LEFT, 80); m_list.InsertColumn(COL_EFFECT, _T("Effect"), LVCFMT_LEFT, 80); } m_subtimes.SetCount(m_sts.GetCount()); for(int i = 0, j = m_sts.GetCount(); i < j; i++) { m_subtimes[i].orgstart = m_sts[i].start; m_subtimes[i].orgend = m_sts[i].end; } ResetSubtitle(); } void CPlayerSubresyncBar::ResetSubtitle() { m_list.DeleteAllItems(); if(m_mode == VOBSUB || m_mode == TEXTSUB) { TCHAR buff[32]; int prevstart = INT_MIN; for(int i = 0, j = m_sts.GetCount(); i < j; i++) { m_subtimes[i].newstart = m_subtimes[i].orgstart; m_subtimes[i].newend = m_subtimes[i].orgend; FormatTime(i, buff, 0, false); m_list.InsertItem(i, buff, COL_START); FormatTime(i, buff, 0, true); m_list.SetItemText(i, COL_END, buff); if(prevstart > m_subtimes[i].orgstart) m_list.SetItemData(i, (DWORD_PTR)TSEP); prevstart = m_subtimes[i].orgstart; SetCheck(i, false, false); } UpdatePreview(); m_list.SetColumnWidth(COL_START, LVSCW_AUTOSIZE); m_list.SetColumnWidth(COL_PREVSTART, LVSCW_AUTOSIZE); } UpdateStrings(); } void CPlayerSubresyncBar::SaveSubtitle() { CMainFrame* pFrame = ((CMainFrame*)AfxGetMainWnd()); if(!pFrame) return; CLSID clsid; m_pSubStream->GetClassID(&clsid); if(clsid == __uuidof(CVobSubFile) && m_mode == VOBSUB) { CVobSubFile* pVSF = (CVobSubFile*)(ISubStream*)m_pSubStream; CAutoLock cAutoLock(m_pSubLock); _ASSERT(pVSF->m_iLang >= 0); CAtlArray& sp = pVSF->m_langs[pVSF->m_iLang].subpos; for(int i = 0, j = sp.GetCount(); i < j; i++) { sp[i].fValid = false; } for(int i = 0, j = m_sts.GetCount(); i < j; i++) { int vobid, cellid, forced, spnum, c; if(_stscanf(m_sts.GetStr(i), _T("%d%c%d%c%d%c%d"), &vobid, &c, &cellid, &c, &forced, &c, &spnum) != 7) continue; sp[spnum].start = m_sts[i].start; sp[spnum].stop = m_sts[i].end; sp[spnum].fValid = true; } } else if(clsid == __uuidof(CRenderedTextSubtitle) && m_mode == TEXTSUB) { CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream; CAutoLock cAutoLock(m_pSubLock); pRTS->Copy(m_sts); } else { return; } pFrame->InvalidateSubtitle(); } void CPlayerSubresyncBar::UpdatePreview() { if(m_mode == VOBSUB || m_mode == TEXTSUB) { if(0/*m_fUnlink*/) { for(int i = 0, j = m_sts.GetCount(); i < j; i++) { bool fStartMod, fEndMod, fStartAdj, fEndAdj; GetCheck(i, fStartMod, fEndMod, fStartAdj, fEndAdj); m_sts[i].start = (fStartMod||fStartAdj) ? m_subtimes[i].newstart : m_subtimes[i].orgstart; m_sts[i].end = (fEndMod||fEndAdj) ? m_subtimes[i].newend : m_subtimes[i].orgend; } } else { CAtlArray schk; for(int i = 0, j = m_sts.GetCount(); i < j;) { schk.RemoveAll(); int start = i, end; for(end = i; end < j; end++) { int data = (int)m_list.GetItemData(end); if((data&TSEP) && end > i) break; if(data&(TSMOD|TSADJ)) schk.Add(end); } if(schk.GetCount() == 0) { for(; start < end; start++) { m_sts[start].start = m_subtimes[start].orgstart; m_sts[start].end = m_subtimes[start].orgend; } } else if(schk.GetCount() == 1) { int k = schk[0]; int dt = m_subtimes[k].newstart - m_subtimes[k].orgstart; for(; start < end; start++) { m_sts[start].start = m_subtimes[start].orgstart + dt; m_sts[start].end = (m_list.GetItemData(start)&TEMOD) ? m_subtimes[start].newend : (m_subtimes[start].orgend + dt); } } else if(schk.GetCount() >= 2) { int i0 = 0; int i1 = 0; int ti0 = 0; int ds = 0; double m = 0; int k, l; for(k = 0, l = schk.GetCount()-1; k < l; k++) { i0 = schk[k]; i1 = schk[k+1]; ti0 = m_subtimes[i0].orgstart; ds = m_subtimes[i1].orgstart - ti0; if (ds == 0) SetSTS0(start, i1, ti0); else { m = double(m_subtimes[i1].newstart - m_subtimes[i0].newstart) / ds; SetSTS1(start, i1, ti0, m, i0); } } ASSERT(k > 0); if (ds == 0) SetSTS0(start, end, ti0); else SetSTS1(start, end, ti0, m, i0); } i = end; } } m_sts.CreateSegments(); for(int i = 0, j = m_sts.GetCount(); i < j; i++) { TCHAR buff[32]; FormatTime(i, buff, 2, false); m_list.SetItemText(i, COL_PREVSTART, buff); FormatTime(i, buff, 2, true); m_list.SetItemText(i, COL_PREVEND, buff); } if(IsWindowVisible()) { SaveSubtitle(); } } } void CPlayerSubresyncBar::SetSTS0( int &start, int end, int ti0 ) { for(; start < end; start++) { m_sts[start].start = ti0; int endpos; if (m_list.GetItemData(start)&TEMOD) endpos = m_subtimes[start].newend; else endpos = ti0 + m_subtimes[start].orgend - m_subtimes[start].orgstart; m_sts[start].end = endpos; } } void CPlayerSubresyncBar::SetSTS1( int &start, int end, int ti0, double m, int i0 ) { for(; start < end; start++) { m_sts[start].start = int((m_subtimes[start].orgstart - ti0)*m + m_subtimes[i0].newstart); int endpos; if (m_list.GetItemData(start)&TEMOD) endpos = m_subtimes[start].newend; else { int diff = m_subtimes[start].orgend - m_subtimes[start].orgstart; if (m_mode == VOBSUB) endpos = m_sts[start].start + diff; else endpos = m_sts[start].start + int(diff*m); } m_sts[start].end = endpos; } } void CPlayerSubresyncBar::UpdateStrings() { CString str; if(m_mode == TEXTSUB) { for(int i = 0, j = m_sts.GetCount(); i < j; i++) { STSStyle stss; m_sts.GetStyle(i, stss); m_list.SetItemText(i, COL_TEXT, m_sts.GetStr(i, true)); m_list.SetItemText(i, COL_STYLE, m_sts[i].style); m_list.SetItemText(i, COL_FONT, stss.fontName); str.Format(_T("%d"), stss.charSet); m_list.SetItemText(i, COL_CHARSET, str); m_list.SetItemText(i, COL_UNICODE, m_sts.IsEntryUnicode(i) ? _T("yes") : _T("no")); str.Format(_T("%d"), m_sts[i].layer); m_list.SetItemText(i, COL_LAYER, str); m_list.SetItemText(i, COL_ACTOR, m_sts[i].actor); m_list.SetItemText(i, COL_EFFECT, m_sts[i].effect); } } else if(m_mode == VOBSUB) { for(int i = 0, j = m_sts.GetCount(); i < j; i++) { int vobid, cellid, forced, c; if(_stscanf(m_sts.GetStr(i), _T("%d%c%d%c%d"), &vobid, &c, &cellid, &c, &forced) != 5) continue; if(vobid < 0) str = _T("-"); else str.Format(_T("%d"), vobid); m_list.SetItemText(i, COL_VOBID, str); if(cellid < 0) str = _T("-"); else str.Format(_T("%d"), cellid); m_list.SetItemText(i, COL_CELLID, str); str = forced?_T("Yes"):_T(""); m_list.SetItemText(i, COL_FORCED, str); } } } void CPlayerSubresyncBar::GetCheck(int iItem, bool& fStartMod, bool& fEndMod, bool& fStartAdj, bool& fEndAdj) { if(0 <= iItem && iItem < m_sts.GetCount()) { int nCheck = (int)m_list.GetItemData(iItem); fStartMod = !!(nCheck&TSMOD); fEndMod = !!(nCheck&TEMOD); fStartAdj = !!(nCheck&TSADJ); fEndAdj = !!(nCheck&TEADJ); } } void CPlayerSubresyncBar::SetCheck(int iItem, bool fStart, bool fEnd) { if(0 <= iItem && iItem < m_sts.GetCount()) { SubTime& st = m_subtimes[iItem]; int nCheck = (int)m_list.GetItemData(iItem) & TSEP; if(fStart) nCheck |= TSMOD; else if(abs(st.orgstart-st.newstart)) nCheck |= TSADJ; if(fEnd) nCheck |= TEMOD; else if(abs(st.orgend-st.newend)) nCheck |= TEADJ; m_list.SetItemData(iItem, (DWORD)nCheck); TCHAR buff[32]; FormatTime(iItem, buff, fStart, false); m_list.SetItemText(iItem, COL_START, buff); FormatTime(iItem, buff, fEnd, true); m_list.SetItemText(iItem, COL_END, buff); } } bool CPlayerSubresyncBar::ModStart(int iItem, int t, bool fReset) { bool fRet = false; bool fStartMod, fEndMod, fStartAdj, fEndAdj; GetCheck(iItem, fStartMod, fEndMod, fStartAdj, fEndAdj); SubTime& st = m_subtimes[iItem]; // if(fStartMod || fStartAdj || st.orgstart != t || fReset) { fRet = (st.newstart != t); st.newstart = t; if(!fEndMod) st.newend = st.newstart + (st.orgend - st.orgstart); else if(fReset) st.newstart = st.newend - (st.orgend - st.orgstart); SetCheck(iItem, !fReset, fEndMod); } return(fRet); } bool CPlayerSubresyncBar::ModEnd(int iItem, int t, bool fReset) { bool fRet = false; bool fStartMod, fEndMod, fStartAdj, fEndAdj; GetCheck(iItem, fStartMod, fEndMod, fStartAdj, fEndAdj); SubTime& st = m_subtimes[iItem]; // if(fEndMod || fEndAdj || st.orgend != t || fReset) { fRet = (st.newend != t); st.newend = t; if(!fStartMod) st.newstart = st.newend - (st.orgend - st.orgstart); else if(fReset) st.newend = st.newstart + (st.orgend - st.orgstart); SetCheck(iItem, fStartMod, !fReset); } return(fRet); } void CPlayerSubresyncBar::FormatTime(int iItem, TCHAR* buff, int time, bool fEnd) { int t = !fEnd ?(time == 2 ? m_sts[iItem].start : time == 1 ? m_subtimes[iItem].newstart : m_subtimes[iItem].orgstart) : (time == 2 ? m_sts[iItem].end : time == 1 ? m_subtimes[iItem].newend : m_subtimes[iItem].orgend); _stprintf(buff, t >= 0 ? _T("%02d:%02d:%02d.%03d") : _T("-%02d:%02d:%02d.%03d"), abs(t)/60/60/1000, (abs(t)/60/1000)%60, (abs(t)/1000)%60, abs(t)%1000); } BEGIN_MESSAGE_MAP(CPlayerSubresyncBar, CSizingControlBarG) ON_WM_SIZE() ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_SUBRESYNCLIST, OnBeginlabeleditList) ON_NOTIFY(LVN_DOLABELEDIT, IDC_SUBRESYNCLIST, OnDolabeleditList) ON_NOTIFY(LVN_ENDLABELEDIT, IDC_SUBRESYNCLIST, OnEndlabeleditList) ON_NOTIFY(NM_RCLICK, IDC_SUBRESYNCLIST, OnRclickList) ON_NOTIFY(NM_DBLCLK, IDC_SUBRESYNCLIST, OnNMDblclkList) ON_NOTIFY(LVN_KEYDOWN, IDC_SUBRESYNCLIST, OnLvnKeydownList) ON_NOTIFY(NM_CUSTOMDRAW, IDC_SUBRESYNCLIST, OnCustomdrawList) END_MESSAGE_MAP() // CPlayerSubresyncBar message handlers void CPlayerSubresyncBar::OnSize(UINT nType, int cx, int cy) { CSizingControlBarG::OnSize(nType, cx, cy); if(::IsWindow(m_list.m_hWnd)) { CRect r; GetClientRect(r); r.DeflateRect(2, 2); m_list.MoveWindow(r); } } static bool ParseTime(CString str, int& ret, bool fWarn = true) { int sign = 1, h, m, s, ms; TCHAR c; str.Trim(); if(str.GetLength() > 0 && str[0] == '-') sign = -1; int n = _stscanf(str, _T("%d%c%d%c%d%c%d"), &h, &c, &m, &c, &s, &c, &ms); h = abs(h); if(n == 7 && 0 <= h && h < 24 && 0 <= m && m < 60 && 0 <= s && s < 60 && 0 <= ms && ms < 1000) { ret = sign*(h*60*60*1000+m*60*1000+s*1000+ms); return(true); } if(fWarn) AfxMessageBox(_T("The correct time format is [-]hh:mm:ss.ms\n(e.g. 01:23:45.678)")); return(false); } void CPlayerSubresyncBar::OnBeginlabeleditList(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR; LV_ITEM* pItem = &pDispInfo->item; *pResult = FALSE; if(pItem->iItem >= 0) { if((pItem->iSubItem == COL_START || pItem->iSubItem == COL_END || pItem->iSubItem == COL_TEXT || pItem->iSubItem == COL_STYLE || pItem->iSubItem == COL_LAYER || pItem->iSubItem == COL_ACTOR || pItem->iSubItem == COL_EFFECT) && m_mode == TEXTSUB) { *pResult = TRUE; } else if((pItem->iSubItem == COL_START) && m_mode == VOBSUB) { *pResult = TRUE; } } } void CPlayerSubresyncBar::OnDolabeleditList(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR; LV_ITEM* pItem = &pDispInfo->item; *pResult = FALSE; if(pItem->iItem >= 0) { if((pItem->iSubItem == COL_START || pItem->iSubItem == COL_END || pItem->iSubItem == COL_TEXT || pItem->iSubItem == COL_STYLE || pItem->iSubItem == COL_LAYER || pItem->iSubItem == COL_ACTOR || pItem->iSubItem == COL_EFFECT) && m_mode == TEXTSUB) { m_list.ShowInPlaceEdit(pItem->iItem, pItem->iSubItem); *pResult = TRUE; } else if((pItem->iSubItem == COL_START) && m_mode == VOBSUB) { m_list.ShowInPlaceEdit(pItem->iItem, pItem->iSubItem); *pResult = TRUE; } } } void CPlayerSubresyncBar::OnEndlabeleditList(NMHDR* pNMHDR, LRESULT* pResult) { LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR; LV_ITEM* pItem = &pDispInfo->item; *pResult = FALSE; if(!m_list.m_fInPlaceDirty) return; bool fNeedsUpdate = false; if(pItem->iItem >= 0 && pItem->pszText && (m_mode == VOBSUB || m_mode == TEXTSUB)) { switch(pItem->iSubItem) { case COL_START: { int t; if(ParseTime(pItem->pszText, t)) { fNeedsUpdate = ModStart(pItem->iItem, t); *pResult = TRUE; } } break; case COL_END: if(m_mode == TEXTSUB) { int t; if(ParseTime(pItem->pszText, t)) { fNeedsUpdate = ModEnd(pItem->iItem, t); *pResult = TRUE; } } break; case COL_TEXT: if(m_mode == TEXTSUB) { CString str = m_sts.GetStr(pItem->iItem, true); if(str != pItem->pszText) { fNeedsUpdate = true; m_sts.SetStr(pItem->iItem, CString(pItem->pszText), true); m_list.SetItemText(pItem->iItem, pItem->iSubItem, m_sts.GetStr(pItem->iItem, true)); } } break; case COL_STYLE: if(m_mode == TEXTSUB) { CString str(pItem->pszText); str.Trim(); if(!str.IsEmpty() && m_sts[pItem->iItem].style != str) { fNeedsUpdate = true; if(!m_sts.m_styles.Lookup(str)) m_sts.AddStyle(str, DNew STSStyle()); m_sts[pItem->iItem].style = str; m_list.SetItemText(pItem->iItem, pItem->iSubItem, pItem->pszText); } } break; case COL_LAYER: if(m_mode == TEXTSUB) { int l; if(_stscanf_s(pItem->pszText, _T("%d"), &l) == 1) { fNeedsUpdate = true; m_sts[pItem->iItem].layer = l; CString str; str.Format(_T("%d"), l); m_list.SetItemText(pItem->iItem, pItem->iSubItem, str); } } break; case COL_ACTOR: if(m_mode == TEXTSUB) { CString str(pItem->pszText); str.Trim(); if(!str.IsEmpty()) { fNeedsUpdate = true; m_sts[pItem->iItem].actor = str; m_list.SetItemText(pItem->iItem, pItem->iSubItem, str); } } break; case COL_EFFECT: if(m_mode == TEXTSUB) { CString str(pItem->pszText); str.Trim(); if(!str.IsEmpty()) { fNeedsUpdate = true; m_sts[pItem->iItem].effect = str; m_list.SetItemText(pItem->iItem, pItem->iSubItem, str); } } break; } } if(fNeedsUpdate) { UpdatePreview(); } } static int uintcomp(const void* i1, const void* i2) { return(*((UINT*)i2) - *((UINT*)i1)); } void CPlayerSubresyncBar::OnRclickList(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)pNMHDR; if(lpnmlv->iItem >= 0 && lpnmlv->iSubItem >= 0) { enum { TOGSEP=1, DUPITEM, DELITEM, RESETS, SETOS, SETCS, RESETE, SETOE, SETCE, STYLEFIRST, STYLELAST=STYLEFIRST+1000, STYLEEDIT, UNICODEYES, UNICODENO, LAYERDEC, LAYERINC, ACTORFIRST, ACTORLAST=ACTORFIRST+1000, EFFECTFIRST, EFFECTLAST=EFFECTFIRST+1000 }; CStringArray styles; CStringArray actors; CStringArray effects; CMenu m; m.CreatePopupMenu(); if(m_mode == VOBSUB || m_mode == TEXTSUB) { m.AppendMenu(MF_STRING|MF_ENABLED, TOGSEP, ResStr(IDS_SUBRESYNC_SEPARATOR)); m.AppendMenu(MF_SEPARATOR); if(m_mode == TEXTSUB) m.AppendMenu(MF_STRING|MF_ENABLED, DUPITEM, ResStr(IDS_SUBRESYNC_DUPLICATE)); m.AppendMenu(MF_STRING|MF_ENABLED, DELITEM, ResStr(IDS_SUBRESYNC_DELETE)); } switch(lpnmlv->iSubItem) { case COL_START: if(m_mode == VOBSUB || m_mode == TEXTSUB) { m.AppendMenu(MF_SEPARATOR); m.AppendMenu(MF_STRING|MF_ENABLED, RESETS, ResStr(IDS_SUBRESYNC_RESET) + _T("\tF1")); m.AppendMenu(MF_STRING|MF_ENABLED, SETOS, ResStr(IDS_SUBRESYNC_ORIGINAL) + _T("\tF3")); m.AppendMenu(MF_STRING|MF_ENABLED, SETCS, ResStr(IDS_SUBRESYNC_CURRENT) + _T("\tF5")); } break; case COL_END: if(m_mode == TEXTSUB) { m.AppendMenu(MF_SEPARATOR); m.AppendMenu(MF_STRING|MF_ENABLED, RESETE, ResStr(IDS_SUBRESYNC_RESET) + _T("\tF2")); m.AppendMenu(MF_STRING|MF_ENABLED, SETOE, ResStr(IDS_SUBRESYNC_ORIGINAL) + _T("\tF4")); m.AppendMenu(MF_STRING|MF_ENABLED, SETCE, ResStr(IDS_SUBRESYNC_CURRENT) + _T("\tF6")); } break; case COL_STYLE: if(m_mode == TEXTSUB) { m.AppendMenu(MF_SEPARATOR); int id = STYLEFIRST; POSITION pos = m_sts.m_styles.GetStartPosition(); while(pos && id <= STYLELAST) { CString key; STSStyle* val; m_sts.m_styles.GetNextAssoc(pos, key, val); styles.Add(key); m.AppendMenu(MF_STRING|MF_ENABLED, id++, key); } if(id > STYLEFIRST && m_list.GetSelectedCount() == 1) { m.AppendMenu(MF_SEPARATOR); m.AppendMenu(MF_STRING|MF_ENABLED, STYLEEDIT, ResStr(IDS_SUBRESYNC_EDIT)); } } break; case COL_UNICODE: if(m_mode == TEXTSUB) { m.AppendMenu(MF_SEPARATOR); m.AppendMenu(MF_STRING|MF_ENABLED, UNICODEYES, ResStr(IDS_SUBRESYNC_YES)); m.AppendMenu(MF_STRING|MF_ENABLED, UNICODENO, ResStr(IDS_SUBRESYNC_NO)); } break; case COL_LAYER: if(m_mode == TEXTSUB) { m.AppendMenu(MF_SEPARATOR); m.AppendMenu(MF_STRING|MF_ENABLED, LAYERDEC, ResStr(IDS_SUBRESYNC_DECREASE)); m.AppendMenu(MF_STRING|MF_ENABLED, LAYERINC, ResStr(IDS_SUBRESYNC_INCREASE)); } break; case COL_ACTOR: if(m_mode == TEXTSUB) { CMapStringToPtr actormap; for(int i = 0, j = m_sts.GetCount(); i < j; i++) actormap[m_sts[i].actor] = NULL; actormap.RemoveKey(_T("")); if(actormap.GetCount() > 0) { m.AppendMenu(MF_SEPARATOR); int id = ACTORFIRST; POSITION pos = actormap.GetStartPosition(); while(pos && id <= ACTORLAST) { CString key; void* val; actormap.GetNextAssoc(pos, key, val); actors.Add(key); m.AppendMenu(MF_STRING|MF_ENABLED, id++, key); } } } break; case COL_EFFECT: if(m_mode == TEXTSUB) { CMapStringToPtr effectmap; for(int i = 0, j = m_sts.GetCount(); i < j; i++) effectmap[m_sts[i].effect] = NULL; effectmap.RemoveKey(_T("")); if(effectmap.GetCount() > 0) { m.AppendMenu(MF_SEPARATOR); int id = EFFECTFIRST; POSITION pos = effectmap.GetStartPosition(); while(pos && id <= EFFECTLAST) { CString key; void* val; effectmap.GetNextAssoc(pos, key, val); effects.Add(key); m.AppendMenu(MF_STRING|MF_ENABLED, id++, key); } } } break; } CPoint p = lpnmlv->ptAction; ::MapWindowPoints(pNMHDR->hwndFrom, HWND_DESKTOP, &p, 1); UINT id = m.TrackPopupMenu(TPM_LEFTBUTTON|TPM_RETURNCMD, p.x, p.y, this); bool fNeedsUpdate = false; POSITION pos = m_list.GetFirstSelectedItemPosition(); while(pos) { int iItem = m_list.GetNextSelectedItem(pos); SubTime& st = m_subtimes[iItem]; switch(id) { case TOGSEP: m_list.SetItemData(iItem, m_list.GetItemData(iItem)^TSEP); m_list.Invalidate(); fNeedsUpdate = true; break; case DUPITEM: { CUIntArray items; pos = m_list.GetFirstSelectedItemPosition(); while(pos) items.Add(m_list.GetNextSelectedItem(pos)); qsort(items.GetData(), items.GetCount(), sizeof(UINT), uintcomp); for(int i = 0; i < items.GetCount(); i++) { iItem = items[i]; STSEntry stse = m_sts[iItem]; m_sts.InsertAt(iItem+1, stse); SubTime st = m_subtimes[iItem]; m_subtimes.InsertAt(iItem+1, st); CHeaderCtrl* pHeader = (CHeaderCtrl*)m_list.GetDlgItem(0); int nColumnCount = pHeader->GetItemCount(); CStringArray sa; sa.SetSize(nColumnCount); for(int col = 0; col < nColumnCount; col++) sa[col] = m_list.GetItemText(iItem, col); DWORD data = m_list.GetItemData(iItem); m_list.InsertItem(iItem+1, sa[0]); m_list.SetItemData(iItem+1, data); for(int col = 1; col < nColumnCount; col++) m_list.SetItemText(iItem+1, col, sa[col]); } } fNeedsUpdate = true; break; case DELITEM: { CUIntArray items; pos = m_list.GetFirstSelectedItemPosition(); while(pos) items.Add(m_list.GetNextSelectedItem(pos)); qsort(items.GetData(), items.GetCount(), sizeof(UINT), uintcomp); for(int i = 0; i < items.GetCount(); i++) { iItem = items[i]; m_sts.RemoveAt(iItem); m_subtimes.RemoveAt(iItem); m_list.DeleteItem(iItem); } iItem = items[items.GetCount()-1]; if(iItem >= m_list.GetItemCount()) iItem = m_list.GetItemCount()-1; m_list.SetSelectionMark(iItem); } fNeedsUpdate = true; break; case RESETS: /*if(*/ ModStart(iItem, st.orgstart, true);/*)*/ fNeedsUpdate = true; break; case SETOS: /*if(*/ ModStart(iItem, st.orgstart);/*)*/ fNeedsUpdate = true; break; case SETCS: /*if(*/ ModStart(iItem, (int)(m_rt/10000));/*)*/ fNeedsUpdate = true; break; case RESETE: /*if(*/ ModEnd(iItem, st.orgend, true);/*)*/ fNeedsUpdate = true; break; case SETOE: /*if(*/ ModEnd(iItem, st.orgend);/*)*/ fNeedsUpdate = true; break; case SETCE: /*if(*/ ModEnd(iItem, (int)(m_rt/10000));/*)*/ fNeedsUpdate = true; break; default: if(STYLEFIRST <= id && id <= STYLELAST) { CString s = styles[id - STYLEFIRST]; if(m_sts[iItem].style != s) fNeedsUpdate = true; m_sts[iItem].style = s; m_list.SetItemText(iItem, lpnmlv->iSubItem, s); } else if(id == STYLEEDIT) { CAutoPtrArray pages; CAtlArray styles; STSStyle* stss = m_sts.GetStyle(iItem); int iSelPage = 0; POSITION pos = m_sts.m_styles.GetStartPosition(); for(int i = 0; pos; i++) { CString key; STSStyle* val; m_sts.m_styles.GetNextAssoc(pos, key, val); CAutoPtr page(DNew CPPageSubStyle()); page->InitStyle(key, *val); pages.Add(page); styles.Add(val); if(stss == val) iSelPage = i; } CPropertySheet dlg(_T("Styles..."), this, iSelPage); for(int i = 0; i < (int)pages.GetCount(); i++) dlg.AddPage(pages[i]); if(dlg.DoModal() == IDOK) { for(int j = 0; j < (int)pages.GetCount(); j++) { stss = styles[j]; pages[j]->GetStyle(*stss); for(int i = 0; i < m_sts.GetCount(); i++) { if(m_sts.GetStyle(i) == stss) { CString str; m_list.SetItemText(i, COL_TEXT, m_sts.GetStr(i, true)); m_list.SetItemText(i, COL_FONT, stss->fontName); str.Format(_T("%d"), stss->charSet); m_list.SetItemText(i, COL_CHARSET, str); str.Format(_T("%d"), m_sts[i].layer); } } } fNeedsUpdate = true; } } else if(id == UNICODEYES || id == UNICODENO) { m_sts.ConvertUnicode(iItem, id == UNICODEYES); m_list.SetItemText(iItem, COL_TEXT, m_sts.GetStr(iItem, true)); m_list.SetItemText(iItem, COL_UNICODE, m_sts.IsEntryUnicode(iItem) ? _T("yes") : _T("no")); fNeedsUpdate = true; } else if(id == LAYERDEC || id == LAYERINC) { int d = (id == LAYERDEC) ? -1 : (id == LAYERINC) ? +1 : 0; if(d != 0) fNeedsUpdate = true; m_sts[iItem].layer += d; CString s; s.Format(_T("%d"), m_sts[iItem].layer); m_list.SetItemText(iItem, lpnmlv->iSubItem, s); } else if(ACTORFIRST <= id && id <= ACTORLAST) { CString s = actors[id - ACTORFIRST]; if(m_sts[iItem].actor != s) fNeedsUpdate = true; m_sts[iItem].actor = s; m_list.SetItemText(iItem, lpnmlv->iSubItem, s); } else if(EFFECTFIRST <= id && id <= EFFECTLAST) { CString s = effects[id - EFFECTFIRST]; if(m_sts[iItem].effect != s) fNeedsUpdate = true; m_sts[iItem].effect = s; m_list.SetItemText(iItem, lpnmlv->iSubItem, s); } break; } } if(fNeedsUpdate) { UpdatePreview(); } } *pResult = 0; } void CPlayerSubresyncBar::OnNMDblclkList(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)pNMHDR; if(lpnmlv->iItem >= 0 && lpnmlv->iSubItem >= 0 && (m_mode == VOBSUB || m_mode == TEXTSUB)) { if(CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd()) { int t = 0; if(!ParseTime(m_list.GetItemText(lpnmlv->iItem, lpnmlv->iSubItem), t, false)) t = m_sts[lpnmlv->iItem].start; REFERENCE_TIME rt = lpnmlv->iSubItem == COL_START ? ((REFERENCE_TIME)t*10000) : lpnmlv->iSubItem == COL_END ? ((REFERENCE_TIME)t*10000) : lpnmlv->iSubItem == COL_PREVSTART ? ((REFERENCE_TIME)t*10000) : lpnmlv->iSubItem == COL_PREVEND ? ((REFERENCE_TIME)t*10000) : ((REFERENCE_TIME)t*10000); pFrame->SeekTo(rt); } } *pResult = 0; } void CPlayerSubresyncBar::OnLvnKeydownList(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLVKEYDOWN pLVKeyDown = reinterpret_cast(pNMHDR); UNUSED_ALWAYS(pLVKeyDown); *pResult = 0; } static CUIntArray m_itemGroups; static int m_totalGroups; void CPlayerSubresyncBar::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult) { NMLVCUSTOMDRAW* pLVCD = reinterpret_cast(pNMHDR); *pResult = CDRF_DODEFAULT; if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage) { m_itemGroups.SetSize(m_list.GetItemCount()); m_totalGroups = 0; for(int i = 0, j = m_list.GetItemCount(); i < j; i++) { if(m_list.GetItemData(i)&TSEP) m_totalGroups++; m_itemGroups[i] = m_totalGroups; } *pResult = CDRF_NOTIFYPOSTPAINT|CDRF_NOTIFYITEMDRAW; } else if(CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage) { pLVCD->nmcd.uItemState &= ~CDIS_FOCUS; *pResult = CDRF_NOTIFYPOSTPAINT|CDRF_NOTIFYSUBITEMDRAW; } else if((CDDS_ITEMPREPAINT|CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage) { COLORREF clrText; COLORREF clrTextBk; if((pLVCD->iSubItem == COL_START || pLVCD->iSubItem == COL_END || pLVCD->iSubItem == COL_TEXT || pLVCD->iSubItem == COL_STYLE || pLVCD->iSubItem == COL_LAYER || pLVCD->iSubItem == COL_ACTOR || pLVCD->iSubItem == COL_EFFECT) && m_mode == TEXTSUB) { clrText = 0; } else if((pLVCD->iSubItem == COL_START) && m_mode == VOBSUB) { clrText = 0; } else { clrText = 0x606060; } clrTextBk = 0xffffff; // if(m_totalGroups > 0) clrTextBk -= ((m_itemGroups[pLVCD->nmcd.dwItemSpec]&1) ? 0x100010 : 0x200020); if(m_sts[pLVCD->nmcd.dwItemSpec].start <= m_rt/10000 && m_rt/10000 < m_sts[pLVCD->nmcd.dwItemSpec].end) { clrText |= 0xFF; } int nCheck = (int)m_list.GetItemData(pLVCD->nmcd.dwItemSpec); if((nCheck&1) && (pLVCD->iSubItem == COL_START || pLVCD->iSubItem == COL_PREVSTART)) { clrTextBk = 0xffddbb; } else if((nCheck&4) && (/*pLVCD->iSubItem == COL_START ||*/ pLVCD->iSubItem == COL_PREVSTART)) { clrTextBk = 0xffeedd; } if((nCheck&2) && (pLVCD->iSubItem == COL_END || pLVCD->iSubItem == COL_PREVEND)) { clrTextBk = 0xffddbb; } else if((nCheck&8) && (/*pLVCD->iSubItem == COL_END ||*/ pLVCD->iSubItem == COL_PREVEND)) { clrTextBk = 0xffeedd; } pLVCD->clrText = clrText; pLVCD->clrTextBk = clrTextBk; *pResult = CDRF_NOTIFYPOSTPAINT; } else if((CDDS_ITEMPOSTPAINT|CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage) { // *pResult = CDRF_DODEFAULT; } else if(CDDS_ITEMPOSTPAINT == pLVCD->nmcd.dwDrawStage) { int nItem = static_cast(pLVCD->nmcd.dwItemSpec); LVITEM rItem; ZeroMemory(&rItem, sizeof(LVITEM)); rItem.mask = LVIF_IMAGE | LVIF_STATE; rItem.iItem = nItem; rItem.stateMask = LVIS_SELECTED; m_list.GetItem(&rItem); { CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc); CRect rcItem; m_list.GetItemRect(nItem, &rcItem, LVIR_BOUNDS); { bool fSeparator = nItem < m_list.GetItemCount()-1 && (m_list.GetItemData(nItem+1)&TSEP); CPen p(PS_INSIDEFRAME, 1, fSeparator ? 0x404040 : 0xe0e0e0); CPen* old = pDC->SelectObject(&p); pDC->MoveTo(CPoint(rcItem.left, rcItem.bottom-1)); pDC->LineTo(CPoint(rcItem.right, rcItem.bottom-1)); pDC->SelectObject(old); } { CPen p(PS_INSIDEFRAME, 1, 0xe0e0e0); CPen* old = pDC->SelectObject(&p); CHeaderCtrl* pHeader = (CHeaderCtrl*)m_list.GetDlgItem(0); int nColumnCount = pHeader->GetItemCount(); // Get the column offset int offset = rcItem.left; for(int i = 0; i < nColumnCount; i++) { offset += m_list.GetColumnWidth(i); pDC->MoveTo(CPoint(offset, rcItem.top)); pDC->LineTo(CPoint(offset, rcItem.bottom)); } pDC->SelectObject(old); } *pResult = CDRF_SKIPDEFAULT; } } else if(CDDS_POSTPAINT == pLVCD->nmcd.dwDrawStage) { } } bool CPlayerSubresyncBar::IsShortCut(MSG* pMsg) { if(pMsg->message == WM_KEYDOWN && VK_F1 <= pMsg->wParam && pMsg->wParam <= VK_F6) { int iItem = -1; bool fNeedsUpdate = false; bool fStep = false; POSITION pos = m_list.GetFirstSelectedItemPosition(); while(pos) { iItem = m_list.GetNextSelectedItem(pos); SubTime& st = m_subtimes[iItem]; switch(pMsg->wParam) { case VK_F1: /*if(*/ ModStart(iItem, st.orgstart, true);/*)*/ fNeedsUpdate = true; break; case VK_F3: /*if(*/ ModStart(iItem, st.orgstart);/*)*/ fNeedsUpdate = true; break; case VK_F5: /*if(*/ ModStart(iItem, (int)(m_rt/10000));/*)*/ fNeedsUpdate = true; break; case VK_F2: /*if(*/ ModEnd(iItem, st.orgend, true);/*)*/ fNeedsUpdate = true; break; case VK_F4: /*if(*/ ModEnd(iItem, st.orgend);/*)*/ fNeedsUpdate = true; break; case VK_F6: /*if(*/ ModEnd(iItem, (int)(m_rt/10000));/*)*/ fNeedsUpdate = fStep = true; break; } } if(fNeedsUpdate) { if(fStep && m_list.GetSelectedCount() == 1 && iItem < m_list.GetItemCount()-1) { m_list.SetItemState(iItem, 0, LVIS_SELECTED); m_list.SetItemState(iItem+1, LVIS_SELECTED, LVIS_SELECTED); m_list.SetSelectionMark(iItem+1); m_list.EnsureVisible(iItem+1, false); } UpdatePreview(); return true; } } return false; } int CPlayerSubresyncBar::FindNearestSub(__int64& rtPos, bool bForward) { long lCurTime = rtPos / 10000 + (bForward ? 1 : -1); if (m_subtimes.GetCount() == 0) { rtPos = max (0, rtPos + (bForward ? 1 : -1) * 50000000); return -2; } if (lCurTime < m_subtimes[0].newstart) { rtPos = m_subtimes[0].newstart * 10000; return 0; } for(int i = 1, j = m_sts.GetCount(); i < j; i++) { if ((lCurTime >= m_subtimes[i-1].newstart) && (lCurTime < m_subtimes[i].newstart)) { rtPos = bForward ? (__int64)m_subtimes[i].newstart * 10000 : (__int64)m_subtimes[i-1].newstart * 10000; return bForward ? i : i-1; } } return -1; } bool CPlayerSubresyncBar::ShiftSubtitle(int nItem, long lValue, __int64& rtPos) { bool bRet = false; if ((nItem == 0) || (m_subtimes[nItem-1].newend < m_subtimes[nItem].newstart + lValue)) { for (int i= nItem; i