1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675 |
- /*
- * Ctrl_seq.cpp
- * ------------
- * Purpose: Order list for the pattern editor upper panel.
- * Notes : (currently none)
- * Authors: Olivier Lapicque
- * OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Mainfrm.h"
- #include "InputHandler.h"
- #include "Moddoc.h"
- #include "../soundlib/mod_specifications.h"
- #include "Globals.h"
- #include "Ctrl_pat.h"
- #include "PatternClipboard.h"
- #include "../common/mptStringBuffer.h"
- OPENMPT_NAMESPACE_BEGIN
- enum SequenceAction : SEQUENCEINDEX
- {
- kAddSequence = MAX_SEQUENCES,
- kDuplicateSequence,
- kDeleteSequence,
- kSplitSequence,
- kMaxSequenceActions
- };
- // Little helper function to avoid copypasta
- static bool IsSelectionKeyPressed() { return CMainFrame::GetInputHandler()->SelectionPressed(); }
- static bool IsCtrlKeyPressed() { return CMainFrame::GetInputHandler()->CtrlPressed(); }
- //////////////////////////////////////////////////////////////
- // CPatEdit
- BOOL CPatEdit::PreTranslateMessage(MSG *pMsg)
- {
- if(((pMsg->message == WM_KEYDOWN) || (pMsg->message == WM_KEYUP)) && (pMsg->wParam == VK_TAB))
- {
- if((pMsg->message == WM_KEYUP) && (m_pParent))
- {
- m_pParent->SwitchToView();
- }
- return TRUE;
- }
- return CEdit::PreTranslateMessage(pMsg);
- }
- //////////////////////////////////////////////////////////////
- // COrderList
- BEGIN_MESSAGE_MAP(COrderList, CWnd)
- //{{AFX_MSG_MAP(COrderList)
- ON_WM_PAINT()
- ON_WM_ERASEBKGND()
- ON_WM_MOUSEMOVE()
- ON_WM_LBUTTONDOWN()
- ON_WM_LBUTTONDBLCLK()
- ON_WM_LBUTTONUP()
- ON_WM_RBUTTONDOWN()
- ON_WM_MBUTTONDOWN()
- ON_WM_SETFOCUS()
- ON_WM_KILLFOCUS()
- ON_WM_HSCROLL()
- ON_WM_SIZE()
- ON_COMMAND(ID_ORDERLIST_INSERT, &COrderList::OnInsertOrder)
- ON_COMMAND(ID_ORDERLIST_INSERT_SEPARATOR, &COrderList::OnInsertSeparatorPattern)
- ON_COMMAND(ID_ORDERLIST_DELETE, &COrderList::OnDeleteOrder)
- ON_COMMAND(ID_ORDERLIST_RENDER, &COrderList::OnRenderOrder)
- ON_COMMAND(ID_ORDERLIST_EDIT_COPY, &COrderList::OnEditCopy)
- ON_COMMAND(ID_ORDERLIST_EDIT_CUT, &COrderList::OnEditCut)
- ON_COMMAND(ID_ORDERLIST_EDIT_COPY_ORDERS, &COrderList::OnEditCopyOrders)
-
- ON_COMMAND(ID_PATTERN_PROPERTIES, &COrderList::OnPatternProperties)
- ON_COMMAND(ID_PLAYER_PLAY, &COrderList::OnPlayerPlay)
- ON_COMMAND(ID_PLAYER_PAUSE, &COrderList::OnPlayerPause)
- ON_COMMAND(ID_PLAYER_PLAYFROMSTART, &COrderList::OnPlayerPlayFromStart)
- ON_COMMAND(IDC_PATTERN_PLAYFROMSTART, &COrderList::OnPatternPlayFromStart)
- ON_COMMAND(ID_ORDERLIST_NEW, &COrderList::OnCreateNewPattern)
- ON_COMMAND(ID_ORDERLIST_COPY, &COrderList::OnDuplicatePattern)
- ON_COMMAND(ID_ORDERLIST_MERGE, &COrderList::OnMergePatterns)
- ON_COMMAND(ID_PATTERNCOPY, &COrderList::OnPatternCopy)
- ON_COMMAND(ID_PATTERNPASTE, &COrderList::OnPatternPaste)
- ON_COMMAND(ID_SETRESTARTPOS, &COrderList::OnSetRestartPos)
- ON_COMMAND(ID_ORDERLIST_LOCKPLAYBACK, &COrderList::OnLockPlayback)
- ON_COMMAND(ID_ORDERLIST_UNLOCKPLAYBACK, &COrderList::OnUnlockPlayback)
- ON_COMMAND_RANGE(ID_SEQUENCE_ITEM, ID_SEQUENCE_ITEM + kMaxSequenceActions - 1, &COrderList::OnSelectSequence)
- ON_MESSAGE(WM_MOD_DRAGONDROPPING, &COrderList::OnDragonDropping)
- ON_MESSAGE(WM_HELPHITTEST, &COrderList::OnHelpHitTest)
- ON_MESSAGE(WM_MOD_KEYCOMMAND, &COrderList::OnCustomKeyMsg)
- ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &COrderList::OnToolTipText)
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
- COrderList::COrderList(CCtrlPatterns &parent, CModDoc &document)
- : m_nOrderlistMargins(TrackerSettings::Instance().orderlistMargins)
- , m_modDoc(document)
- , m_pParent(parent)
- {
- EnableActiveAccessibility();
- }
- bool COrderList::EnsureEditable(ORDERINDEX ord)
- {
- auto &sndFile = m_modDoc.GetSoundFile();
- if(ord >= Order().size())
- {
- if(ord < sndFile.GetModSpecifications().ordersMax)
- {
- try
- {
- Order().resize(ord + 1);
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- return false;
- }
- } else
- {
- return false;
- }
- }
- return true;
- }
- ModSequence &COrderList::Order() { return m_modDoc.GetSoundFile().Order(); }
- const ModSequence &COrderList::Order() const { return m_modDoc.GetSoundFile().Order(); }
- void COrderList::SetScrollPos(int pos)
- {
- // Work around 16-bit limitations of WM_HSCROLL
- SCROLLINFO si;
- MemsetZero(si);
- si.cbSize = sizeof(si);
- si.fMask = SIF_TRACKPOS;
- GetScrollInfo(SB_HORZ, &si);
- si.nPos = pos;
- SetScrollInfo(SB_HORZ, &si);
- }
- int COrderList::GetScrollPos(bool getTrackPos)
- {
- // Work around 16-bit limitations of WM_HSCROLL
- SCROLLINFO si;
- MemsetZero(si);
- si.cbSize = sizeof(si);
- si.fMask = SIF_TRACKPOS;
- GetScrollInfo(SB_HORZ, &si);
- return getTrackPos ? si.nTrackPos : si.nPos;
- }
- bool COrderList::IsOrderInMargins(int order, int startOrder)
- {
- const ORDERINDEX nMargins = GetMargins();
- return ((startOrder != 0 && order - startOrder < nMargins) ||
- order - startOrder >= GetLength() - nMargins);
- }
- void COrderList::EnsureVisible(ORDERINDEX order)
- {
- // nothing needs to be done
- if(!IsOrderInMargins(order, m_nXScroll) || order == ORDERINDEX_INVALID)
- return;
- if(order < m_nXScroll)
- {
- if(order < GetMargins())
- m_nXScroll = 0;
- else
- m_nXScroll = order - GetMargins();
- } else
- {
- m_nXScroll = order + 2 * GetMargins() - 1;
- if(m_nXScroll < GetLength())
- m_nXScroll = 0;
- else
- m_nXScroll -= GetLength();
- }
- }
- bool COrderList::IsPlaying() const
- {
- return (CMainFrame::GetMainFrame()->GetModPlaying() == &m_modDoc);
- }
- ORDERINDEX COrderList::GetOrderFromPoint(const CPoint &pt) const
- {
- if(m_cxFont)
- return mpt::saturate_cast<ORDERINDEX>(m_nXScroll + pt.x / m_cxFont);
- return 0;
- }
- CRect COrderList::GetRectFromOrder(ORDERINDEX ord) const
- {
- return CRect{CPoint{(ord - m_nXScroll) * m_cxFont, 0}, CSize{m_cxFont, m_cyFont}};
- }
- BOOL COrderList::Init(const CRect &rect, HFONT hFont)
- {
- CreateEx(WS_EX_STATICEDGE, NULL, _T(""), WS_CHILD | WS_VISIBLE, rect, &m_pParent, IDC_ORDERLIST);
- m_hFont = hFont;
- SendMessage(WM_SETFONT, (WPARAM)m_hFont);
- SetScrollPos(0);
- EnableScrollBarCtrl(SB_HORZ, TRUE);
- SetCurSel(0);
- EnableToolTips();
- return TRUE;
- }
- void COrderList::UpdateScrollInfo()
- {
- CRect rcClient;
- GetClientRect(&rcClient);
- if((m_cxFont > 0) && (rcClient.right > 0))
- {
- CRect rect;
- SCROLLINFO info;
- UINT nPage;
- int nMax = Order().GetLengthTailTrimmed();
- GetScrollInfo(SB_HORZ, &info, SIF_PAGE | SIF_RANGE);
- info.fMask = SIF_PAGE | SIF_RANGE;
- info.nMin = 0;
- nPage = rcClient.right / m_cxFont;
- if(nMax <= (int)nPage)
- nMax = nPage + 1;
- if((nMax != info.nMax) || (nPage != info.nPage))
- {
- info.nPage = nPage;
- info.nMax = nMax;
- SetScrollInfo(SB_HORZ, &info, TRUE);
- }
- }
- }
- int COrderList::GetFontWidth()
- {
- if((m_cxFont <= 0) && (m_hWnd) && (m_hFont))
- {
- CClientDC dc(this);
- HGDIOBJ oldfont = dc.SelectObject(m_hFont);
- CSize sz = dc.GetTextExtent(_T("000+"), 4);
- if(oldfont)
- dc.SelectObject(oldfont);
- return sz.cx;
- }
- return m_cxFont;
- }
- void COrderList::InvalidateSelection()
- {
- ORDERINDEX ordLo = m_nScrollPos, count = 1;
- static ORDERINDEX m_nScrollPos2Old = m_nScrollPos2nd;
- if(m_nScrollPos2Old != ORDERINDEX_INVALID)
- {
- // there were multiple orders selected - remove them all
- ORDERINDEX ordHi = m_nScrollPos;
- if(m_nScrollPos2Old < m_nScrollPos)
- ordLo = m_nScrollPos2Old;
- else
- ordHi = m_nScrollPos2Old;
- count = ordHi - ordLo + 1;
- }
- m_nScrollPos2Old = m_nScrollPos2nd;
- CRect rcClient, rect;
- GetClientRect(&rcClient);
- rect.left = rcClient.left + (ordLo - m_nXScroll) * m_cxFont;
- rect.top = rcClient.top;
- rect.right = rect.left + m_cxFont * count;
- rect.bottom = rcClient.bottom;
- rect &= rcClient;
- if(rect.right > rect.left)
- InvalidateRect(rect, FALSE);
- if(m_playPos != ORDERINDEX_INVALID)
- {
- rect.left = rcClient.left + (m_playPos - m_nXScroll) * m_cxFont;
- rect.top = rcClient.top;
- rect.right = rect.left + m_cxFont;
- rect &= rcClient;
- if(rect.right > rect.left)
- InvalidateRect(rect, FALSE);
- m_playPos = ORDERINDEX_INVALID;
- }
- }
- ORDERINDEX COrderList::GetLength()
- {
- CRect rcClient;
- GetClientRect(&rcClient);
- if(m_cxFont > 0)
- return mpt::saturate_cast<ORDERINDEX>(rcClient.right / m_cxFont);
- else
- {
- const int fontWidth = GetFontWidth();
- return (fontWidth > 0) ? mpt::saturate_cast<ORDERINDEX>(rcClient.right / fontWidth) : 0;
- }
- }
- OrdSelection COrderList::GetCurSel(bool ignoreSelection) const
- {
- // returns the currently selected order(s)
- OrdSelection result;
- result.firstOrd = result.lastOrd = m_nScrollPos;
- // ignoreSelection: true if only first selection marker is important.
- if(!ignoreSelection && m_nScrollPos2nd != ORDERINDEX_INVALID)
- {
- if(m_nScrollPos2nd < m_nScrollPos) // ord2 < ord1
- result.firstOrd = m_nScrollPos2nd;
- else
- result.lastOrd = m_nScrollPos2nd;
- }
- ORDERINDEX lastIndex = std::max(Order().GetLengthTailTrimmed(), m_modDoc.GetSoundFile().GetModSpecifications().ordersMax) - 1u;
- LimitMax(result.firstOrd, lastIndex);
- LimitMax(result.lastOrd, lastIndex);
- return result;
- }
- void COrderList::SetSelection(ORDERINDEX firstOrd, ORDERINDEX lastOrd)
- {
- SetCurSel(firstOrd, true, false, true);
- SetCurSel(lastOrd != ORDERINDEX_INVALID ? lastOrd : firstOrd, false, true, true);
- }
- bool COrderList::SetCurSel(ORDERINDEX sel, bool setPlayPos, bool shiftClick, bool ignoreCurSel)
- {
- CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- ORDERINDEX &ord = shiftClick ? m_nScrollPos2nd : m_nScrollPos;
- const ORDERINDEX lastIndex = std::max(Order().GetLength(), sndFile.GetModSpecifications().ordersMax) - 1u;
- if((sel < 0) || (sel > lastIndex) || (!m_pParent) || (!pMainFrm))
- return false;
- if(!ignoreCurSel && sel == ord && (sel == sndFile.m_PlayState.m_nCurrentOrder))
- return true;
- const ORDERINDEX shownLength = GetLength();
- InvalidateSelection();
- ord = sel;
- if(!EnsureEditable(ord))
- return false;
- if(!m_bScrolling)
- {
- const ORDERINDEX margins = GetMargins(GetMarginsMax(shownLength));
- if(ord < m_nXScroll + margins)
- {
- // Must move first shown sequence item to left in order to show the new active order.
- m_nXScroll = (ord > margins) ? (ord - margins) : 0;
- SetScrollPos(m_nXScroll);
- Invalidate(FALSE);
- } else
- {
- ORDERINDEX maxsel = shownLength;
- if(maxsel)
- maxsel--;
- if(ord - m_nXScroll >= maxsel - margins)
- {
- // Must move first shown sequence item to right in order to show the new active order.
- m_nXScroll = ord - (maxsel - margins);
- SetScrollPos(m_nXScroll);
- Invalidate(FALSE);
- }
- }
- }
- InvalidateSelection();
- PATTERNINDEX n = Order()[m_nScrollPos];
- if(setPlayPos && !shiftClick && sndFile.Patterns.IsValidPat(n))
- {
- const bool isPlaying = IsPlaying();
- bool changedPos = false;
- if(isPlaying && sndFile.m_SongFlags[SONG_PATTERNLOOP])
- {
- pMainFrm->ResetNotificationBuffer();
- // Update channel parameters and play time
- CriticalSection cs;
- m_modDoc.SetElapsedTime(m_nScrollPos, 0, !sndFile.m_SongFlags[SONG_PAUSED | SONG_STEP]);
- changedPos = true;
- } else if(m_pParent.GetFollowSong())
- {
- FlagSet<SongFlags> pausedFlags = sndFile.m_SongFlags & (SONG_PAUSED | SONG_STEP | SONG_PATTERNLOOP);
- // Update channel parameters and play time
- CriticalSection cs;
- sndFile.SetCurrentOrder(m_nScrollPos);
- m_modDoc.SetElapsedTime(m_nScrollPos, 0, !sndFile.m_SongFlags[SONG_PAUSED | SONG_STEP]);
- sndFile.m_SongFlags.set(pausedFlags);
- if(isPlaying)
- pMainFrm->ResetNotificationBuffer();
- changedPos = true;
- }
- if(changedPos && Order().IsPositionLocked(m_nScrollPos))
- {
- // Users wants to go somewhere else, so let them do that.
- OnUnlockPlayback();
- }
- m_pParent.SetCurrentPattern(n);
- } else if(setPlayPos && !shiftClick && n != Order().GetIgnoreIndex() && n != Order().GetInvalidPatIndex())
- {
- m_pParent.SetCurrentPattern(n);
- }
- UpdateInfoText();
- if(m_nScrollPos == m_nScrollPos2nd)
- m_nScrollPos2nd = ORDERINDEX_INVALID;
- return true;
- }
- PATTERNINDEX COrderList::GetCurrentPattern() const
- {
- const ModSequence &order = Order();
- if(m_nScrollPos < order.size())
- {
- return order[m_nScrollPos];
- }
- return 0;
- }
- BOOL COrderList::PreTranslateMessage(MSG *pMsg)
- {
- //handle Patterns View context keys that we want to take effect in the orderlist.
- if((pMsg->message == WM_SYSKEYUP) || (pMsg->message == WM_KEYUP) ||
- (pMsg->message == WM_SYSKEYDOWN) || (pMsg->message == WM_KEYDOWN))
- {
- CInputHandler *ih = CMainFrame::GetInputHandler();
- //Translate message manually
- UINT nChar = (UINT)pMsg->wParam;
- UINT nRepCnt = LOWORD(pMsg->lParam);
- UINT nFlags = HIWORD(pMsg->lParam);
- KeyEventType kT = ih->GetKeyEventType(nFlags);
- if(ih->KeyEvent(kCtxCtrlOrderlist, nChar, nRepCnt, nFlags, kT) != kcNull)
- return true; // Mapped to a command, no need to pass message on.
- //HACK: masquerade as kCtxViewPatternsNote context until we implement appropriate
- // command propagation to kCtxCtrlOrderlist context.
- if(ih->KeyEvent(kCtxViewPatternsNote, nChar, nRepCnt, nFlags, kT) != kcNull)
- return true; // Mapped to a command, no need to pass message on.
- // Handle Application (menu) key
- if(pMsg->message == WM_KEYDOWN && nChar == VK_APPS)
- {
- const auto selection = GetCurSel();
- auto pt = (GetRectFromOrder(selection.firstOrd) | GetRectFromOrder(selection.lastOrd)).CenterPoint();
- CRect clientRect;
- GetClientRect(clientRect);
- if(!clientRect.PtInRect(pt))
- pt = clientRect.CenterPoint();
- OnRButtonDown(0, pt);
- }
- }
- return CWnd::PreTranslateMessage(pMsg);
- }
- LRESULT COrderList::OnCustomKeyMsg(WPARAM wParam, LPARAM lParam)
- {
- bool isPlaying = IsPlaying();
- switch(wParam)
- {
- case kcEditCopy:
- OnEditCopy(); return wParam;
- case kcEditCut:
- OnEditCut(); return wParam;
- case kcEditPaste:
- OnPatternPaste(); return wParam;
- case kcOrderlistEditCopyOrders:
- OnEditCopyOrders(); return wParam;
- // Orderlist navigation
- case kcOrderlistNavigateLeftSelect:
- case kcOrderlistNavigateLeft:
- SetCurSelTo2ndSel(wParam == kcOrderlistNavigateLeftSelect); SetCurSel(m_nScrollPos - 1, wParam == kcOrderlistNavigateLeft || !isPlaying); return wParam;
- case kcOrderlistNavigateRightSelect:
- case kcOrderlistNavigateRight:
- SetCurSelTo2ndSel(wParam == kcOrderlistNavigateRightSelect); SetCurSel(m_nScrollPos + 1, wParam == kcOrderlistNavigateRight || !isPlaying); return wParam;
- case kcOrderlistNavigateFirstSelect:
- case kcOrderlistNavigateFirst:
- SetCurSelTo2ndSel(wParam == kcOrderlistNavigateFirstSelect); SetCurSel(0, wParam == kcOrderlistNavigateFirst || !isPlaying); return wParam;
- case kcEditSelectAll:
- SetCurSel(0, !isPlaying);
- [[fallthrough]];
- case kcOrderlistNavigateLastSelect:
- case kcOrderlistNavigateLast:
- {
- SetCurSelTo2ndSel(wParam == kcOrderlistNavigateLastSelect || wParam == kcEditSelectAll);
- ORDERINDEX nLast = Order().GetLengthTailTrimmed();
- if(nLast > 0) nLast--;
- SetCurSel(nLast, wParam == kcOrderlistNavigateLast || !isPlaying);
- }
- return wParam;
- // Orderlist edit
- case kcOrderlistEditDelete:
- OnDeleteOrder(); return wParam;
- case kcOrderlistEditInsert:
- OnInsertOrder(); return wParam;
- case kcOrderlistEditInsertSeparator:
- OnInsertSeparatorPattern(); return wParam;
- case kcOrderlistSwitchToPatternView:
- OnSwitchToView(); return wParam;
- case kcOrderlistEditPattern:
- OnLButtonDblClk(0, CPoint(0, 0)); OnSwitchToView(); return wParam;
- // Enter pattern number
- case kcOrderlistPat0:
- case kcOrderlistPat1:
- case kcOrderlistPat2:
- case kcOrderlistPat3:
- case kcOrderlistPat4:
- case kcOrderlistPat5:
- case kcOrderlistPat6:
- case kcOrderlistPat7:
- case kcOrderlistPat8:
- case kcOrderlistPat9:
- EnterPatternNum(static_cast<UINT>(wParam) - kcOrderlistPat0); return wParam;
- case kcOrderlistPatMinus:
- EnterPatternNum(10); return wParam;
- case kcOrderlistPatPlus:
- EnterPatternNum(11); return wParam;
- case kcOrderlistPatIgnore:
- EnterPatternNum(12); return wParam;
- case kcOrderlistPatInvalid:
- EnterPatternNum(13); return wParam;
- // kCtxViewPatternsNote messages
- case kcSwitchToOrderList:
- OnSwitchToView();
- return wParam;
- case kcChangeLoopStatus:
- m_pParent.OnModCtrlMsg(CTRLMSG_PAT_LOOP, -1); return wParam;
- case kcToggleFollowSong:
- m_pParent.OnModCtrlMsg(CTRLMSG_PAT_FOLLOWSONG, 1); return wParam;
- case kcChannelUnmuteAll:
- case kcUnmuteAllChnOnPatTransition:
- return m_pParent.SendMessage(WM_MOD_KEYCOMMAND, wParam, lParam);
- case kcOrderlistLockPlayback:
- OnLockPlayback(); return wParam;
- case kcOrderlistUnlockPlayback:
- OnUnlockPlayback(); return wParam;
- case kcDuplicatePattern:
- OnDuplicatePattern(); return wParam;
- case kcMergePatterns:
- OnMergePatterns(); return wParam;
- case kcNewPattern:
- OnCreateNewPattern(); return wParam;
- }
- return kcNull;
- }
- // Helper function to enter pattern index into the orderlist.
- // Call with param 0...9 (enter digit), 10 (decrease) or 11 (increase).
- void COrderList::EnterPatternNum(int enterNum)
- {
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- if(!EnsureEditable(m_nScrollPos))
- return;
- PATTERNINDEX curIndex = Order()[m_nScrollPos];
- const PATTERNINDEX maxIndex = std::max(PATTERNINDEX(1), sndFile.Patterns.GetNumPatterns()) - 1;
- const PATTERNINDEX firstInvalid = sndFile.GetModSpecifications().hasIgnoreIndex ? sndFile.Order.GetIgnoreIndex() : sndFile.Order.GetInvalidPatIndex();
- if(enterNum >= 0 && enterNum <= 9) // enter 0...9
- {
- if(curIndex >= sndFile.Patterns.Size())
- curIndex = 0;
- curIndex = curIndex * 10 + static_cast<PATTERNINDEX>(enterNum);
- static_assert(MAX_PATTERNS < 10000);
- if((curIndex >= 1000) && (curIndex > maxIndex)) curIndex %= 1000;
- if((curIndex >= 100) && (curIndex > maxIndex)) curIndex %= 100;
- if((curIndex >= 10) && (curIndex > maxIndex)) curIndex %= 10;
- } else if(enterNum == 10) // decrease pattern index
- {
- if(curIndex == 0)
- {
- curIndex = sndFile.Order.GetInvalidPatIndex();
- } else if(curIndex > maxIndex && curIndex <= firstInvalid)
- {
- curIndex = maxIndex;
- } else
- {
- do
- {
- curIndex--;
- } while(curIndex > 0 && curIndex < firstInvalid && !sndFile.Patterns.IsValidPat(curIndex));
- }
- } else if(enterNum == 11) // increase pattern index
- {
- if(curIndex >= sndFile.Order.GetInvalidPatIndex())
- {
- curIndex = 0;
- } else if(curIndex >= maxIndex && curIndex < firstInvalid)
- {
- curIndex = firstInvalid;
- } else
- {
- do
- {
- curIndex++;
- } while(curIndex <= maxIndex && !sndFile.Patterns.IsValidPat(curIndex));
- }
- } else if(enterNum == 12) // ignore index (+++)
- {
- if(sndFile.GetModSpecifications().hasIgnoreIndex)
- {
- curIndex = sndFile.Order.GetIgnoreIndex();
- }
- } else if(enterNum == 13) // invalid index (---)
- {
- curIndex = sndFile.Order.GetInvalidPatIndex();
- }
- // apply
- if(curIndex != Order()[m_nScrollPos])
- {
- Order()[m_nScrollPos] = curIndex;
- m_modDoc.SetModified();
- m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
- InvalidateSelection();
- m_pParent.SetCurrentPattern(curIndex);
- }
- }
- void COrderList::OnEditCut()
- {
- OnEditCopy();
- OnDeleteOrder();
- }
- void COrderList::OnCopy(bool onlyOrders)
- {
- const OrdSelection ordsel = GetCurSel();
- BeginWaitCursor();
- PatternClipboard::Copy(m_modDoc.GetSoundFile(), ordsel.firstOrd, ordsel.lastOrd, onlyOrders);
- PatternClipboardDialog::UpdateList();
- EndWaitCursor();
- }
- void COrderList::UpdateView(UpdateHint hint, CObject *pObj)
- {
- if(pObj != this && hint.ToType<SequenceHint>().GetType()[HINT_MODTYPE | HINT_MODSEQUENCE])
- {
- Invalidate(FALSE);
- UpdateInfoText();
- }
- if(hint.GetType()[HINT_MPTOPTIONS])
- {
- m_nOrderlistMargins = TrackerSettings::Instance().orderlistMargins;
- }
- }
- void COrderList::OnSwitchToView()
- {
- m_pParent.PostViewMessage(VIEWMSG_SETFOCUS);
- }
- void COrderList::UpdateInfoText()
- {
- if(::GetFocus() != m_hWnd)
- return;
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- const auto &order = Order();
- const ORDERINDEX length = order.GetLengthTailTrimmed();
- CString s;
- if(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_HEXDISPLAY)
- s.Format(_T("Position %02Xh of %02Xh"), m_nScrollPos, length);
- else
- s.Format(_T("Position %u of %u (%02Xh of %02Xh)"), m_nScrollPos, length, m_nScrollPos, length);
- if(order.IsValidPat(m_nScrollPos))
- {
- if(const auto patName = sndFile.Patterns[order[m_nScrollPos]].GetName(); !patName.empty())
- s += _T(": ") + mpt::ToCString(sndFile.GetCharsetInternal(), patName);
- }
- CMainFrame::GetMainFrame()->SetInfoText(s);
- CMainFrame::GetMainFrame()->NotifyAccessibilityUpdate(*this);
- }
- // Accessible description for screen readers
- HRESULT COrderList::get_accName(VARIANT, BSTR *pszName)
- {
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- const auto &order = Order();
- CString s;
- const bool singleSel = m_nScrollPos2nd == ORDERINDEX_INVALID || m_nScrollPos2nd == m_nScrollPos;
- const auto firstOrd = singleSel ? m_nScrollPos : std::min(m_nScrollPos, m_nScrollPos2nd), lastOrd = singleSel ? m_nScrollPos : std::max(m_nScrollPos, m_nScrollPos2nd);
- if(singleSel)
- s = MPT_CFORMAT("Order {}, ")(m_nScrollPos);
- else
- s = MPT_CFORMAT("Order selection {} to {}: ")(firstOrd, lastOrd);
- bool first = true;
- for(ORDERINDEX o = firstOrd; o <= lastOrd; o++)
- {
- if(!first)
- s += _T(", ");
- first = false;
- PATTERNINDEX pat = order[o];
- if(pat == ModSequence::GetIgnoreIndex())
- s += _T(" Skip");
- else if(pat == ModSequence::GetInvalidPatIndex())
- s += _T(" Stop");
- else
- s += MPT_CFORMAT("Pattern {}")(pat);
- if(sndFile.Patterns.IsValidPat(pat))
- {
- if(const auto patName = sndFile.Patterns[pat].GetName(); !patName.empty())
- s += _T(" (") + mpt::ToCString(sndFile.GetCharsetInternal(), patName) + _T(")");
- }
- }
-
- *pszName = s.AllocSysString();
- return S_OK;
- }
- /////////////////////////////////////////////////////////////////
- // COrderList messages
- void COrderList::OnPaint()
- {
- TCHAR s[64];
- CPaintDC dc(this);
- HGDIOBJ oldfont = dc.SelectObject(m_hFont);
- HGDIOBJ oldpen = dc.SelectStockObject(DC_PEN);
- const auto separatorColor = GetSysColor(COLOR_WINDOW) ^ 0x808080;
- const auto colorText = GetSysColor(COLOR_WINDOWTEXT), colorInvalid = GetSysColor(COLOR_GRAYTEXT), colorTextSel = GetSysColor(COLOR_HIGHLIGHTTEXT);
- const auto windowBrush = GetSysColorBrush(COLOR_WINDOW), highlightBrush = GetSysColorBrush(COLOR_HIGHLIGHT), faceBrush = GetSysColorBrush(COLOR_BTNFACE);
- SetDCPenColor(dc, separatorColor);
- // First time?
- if(m_cxFont <= 0 || m_cyFont <= 0)
- {
- CSize sz = dc.GetTextExtent(_T("000+"), 4);
- m_cxFont = sz.cx;
- m_cyFont = sz.cy;
- }
- if(m_cxFont > 0 && m_cyFont > 0)
- {
- CRect rcClient;
- GetClientRect(&rcClient);
- CRect rect = rcClient;
- UpdateScrollInfo();
- dc.SetBkMode(TRANSPARENT);
- const OrdSelection selection = GetCurSel();
- const int lineWidth1 = Util::ScalePixels(1, m_hWnd);
- const int lineWidth2 = Util::ScalePixels(2, m_hWnd);
- const bool isFocussed = (::GetFocus() == m_hWnd);
- const auto &order = Order();
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- ORDERINDEX maxEntries = sndFile.GetModSpecifications().ordersMax;
- if(order.size() > maxEntries)
- {
- // Only computed if potentially needed.
- maxEntries = std::max(maxEntries, order.GetLengthTailTrimmed());
- }
- // Scrolling the shown orders(the showns rectangles)?
- for(size_t pos = m_nXScroll; rect.left < rcClient.right; pos++, rect.left += m_cxFont)
- {
- const ORDERINDEX ord = mpt::saturate_cast<ORDERINDEX>(pos);
- dc.SetTextColor(colorText);
- const bool inSelection = (ord >= selection.firstOrd && ord <= selection.lastOrd);
- const bool highLight = (isFocussed && inSelection);
- if((rect.right = rect.left + m_cxFont) > rcClient.right)
- rect.right = rcClient.right;
- rect.right--;
- HBRUSH background;
- if(highLight)
- background = highlightBrush; // Currently selected order item
- else if(order.IsPositionLocked(ord))
- background = faceBrush; // "Playback lock" indicator - grey out all order items which aren't played.
- else
- background = windowBrush; // Normal, unselected item.
- ::FillRect(dc, &rect, background);
- // Drawing the shown pattern-indicator or drag position.
- if(ord == (m_bDragging ? m_nDropPos : m_nScrollPos))
- {
- rect.InflateRect(-1, -1);
- dc.DrawFocusRect(&rect);
- rect.InflateRect(1, 1);
- }
- MoveToEx(dc, rect.right, rect.top, NULL);
- LineTo(dc, rect.right, rect.bottom);
- // Drawing the 'ctrl-transition' indicator
- if(ord == sndFile.m_PlayState.m_nSeqOverride && sndFile.m_PlayState.m_nSeqOverride != ORDERINDEX_INVALID)
- {
- dc.FillSolidRect(CRect{rect.left + 4, rect.bottom - 4 - lineWidth1, rect.right - 4, rect.bottom - 4}, separatorColor);
- }
- // Drawing 'playing'-indicator.
- if(ord == sndFile.GetCurrentOrder() && CMainFrame::GetMainFrame()->IsPlaying())
- {
- dc.FillSolidRect(CRect{rect.left + 4, rect.top + 2, rect.right - 4, rect.top + 2 + lineWidth1}, separatorColor);
- m_playPos = ord;
- }
- // Drawing drop indicator
- if(m_bDragging && ord == m_nDropPos && !inSelection)
- {
- const bool dropLeft = (m_nDropPos < selection.firstOrd) || TrackerSettings::Instance().orderListOldDropBehaviour;
- dc.FillSolidRect(CRect{dropLeft ? (rect.left + 2) : (rect.right - 2 - lineWidth2), rect.top + 2, dropLeft ? (rect.left + 2 + lineWidth2) : (rect.right - 2), rect.bottom - 2}, separatorColor);
- }
- s[0] = _T('\0');
- const PATTERNINDEX pat = (ord < order.size()) ? order[ord] : PATTERNINDEX_INVALID;
- if(ord < maxEntries && (rect.left + m_cxFont - 4) <= rcClient.right)
- {
- if(pat == order.GetInvalidPatIndex())
- _tcscpy(s, _T("---"));
- else if(pat == order.GetIgnoreIndex())
- _tcscpy(s, _T("+++"));
- else
- wsprintf(s, _T("%u"), pat);
- }
- COLORREF textCol;
- if(highLight)
- textCol = colorTextSel; // Highlighted pattern
- else if(sndFile.Patterns.IsValidPat(pat))
- textCol = colorText; // Normal pattern
- else
- textCol = colorInvalid; // Non-existent pattern
- dc.SetTextColor(textCol);
- dc.DrawText(s, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
- }
- }
- if(oldpen)
- dc.SelectObject(oldpen);
- if(oldfont)
- dc.SelectObject(oldfont);
- }
- void COrderList::OnSetFocus(CWnd *pWnd)
- {
- CWnd::OnSetFocus(pWnd);
- InvalidateSelection();
- UpdateInfoText();
- CMainFrame::GetMainFrame()->m_pOrderlistHasFocus = this;
- }
- void COrderList::OnKillFocus(CWnd *pWnd)
- {
- CWnd::OnKillFocus(pWnd);
- InvalidateSelection();
- CMainFrame::GetMainFrame()->m_pOrderlistHasFocus = nullptr;
- }
- void COrderList::OnLButtonDown(UINT nFlags, CPoint pt)
- {
- CRect rect;
- GetClientRect(&rect);
- if(pt.y < rect.bottom)
- {
- SetFocus();
- if(IsCtrlKeyPressed())
- {
- // Queue pattern
- QueuePattern(pt);
- } else
- {
- // mark pattern (+skip to)
- const int oldXScroll = m_nXScroll;
- ORDERINDEX ord = GetOrderFromPoint(pt);
- OrdSelection selection = GetCurSel();
- // check if cursor is in selection - if it is, only react on MouseUp as the user might want to drag those orders
- if(m_nScrollPos2nd == ORDERINDEX_INVALID || ord < selection.firstOrd || ord > selection.lastOrd)
- {
- m_nScrollPos2nd = ORDERINDEX_INVALID;
- SetCurSel(ord, true, IsSelectionKeyPressed());
- }
- m_bDragging = !IsOrderInMargins(m_nScrollPos, oldXScroll) || !IsOrderInMargins(m_nScrollPos2nd, oldXScroll);
- m_nMouseDownPos = ord;
- if(m_bDragging)
- {
- m_nDragOrder = m_nDropPos = GetCurSel(true).firstOrd;
- SetCapture();
- }
- }
- } else
- {
- CWnd::OnLButtonDown(nFlags, pt);
- }
- }
- void COrderList::OnLButtonUp(UINT nFlags, CPoint pt)
- {
- CRect rect;
- GetClientRect(&rect);
- // Copy or move orders?
- const bool copyOrders = IsSelectionKeyPressed();
- if(m_bDragging)
- {
- m_bDragging = false;
- ReleaseCapture();
- if(rect.PtInRect(pt))
- {
- ORDERINDEX n = GetOrderFromPoint(pt);
- const OrdSelection selection = GetCurSel();
- if(n != ORDERINDEX_INVALID && n == m_nDropPos && (n < selection.firstOrd || n > selection.lastOrd))
- {
- const bool multiSelection = (selection.firstOrd != selection.lastOrd);
- const bool moveBack = m_nDropPos < m_nDragOrder;
- ORDERINDEX moveCount = (selection.lastOrd - selection.firstOrd), movePos = selection.firstOrd;
- if(!moveBack && !TrackerSettings::Instance().orderListOldDropBehaviour)
- m_nDropPos++;
- bool modified = false;
- for(int i = 0; i <= moveCount; i++)
- {
- if(!m_modDoc.MoveOrder(movePos, m_nDropPos, true, copyOrders))
- break;
- modified = true;
- if(moveBack != copyOrders && multiSelection)
- {
- movePos++;
- m_nDropPos++;
- }
- if(moveBack && copyOrders && multiSelection)
- {
- movePos += 2;
- m_nDropPos++;
- }
- }
- if(multiSelection)
- {
- // adjust selection
- m_nScrollPos2nd = m_nDropPos - 1;
- m_nDropPos -= moveCount + (moveBack ? 0 : 1);
- SetCurSel((moveBack && !copyOrders) ? m_nDropPos - 1 : m_nDropPos);
- } else
- {
- SetCurSel((m_nDragOrder < m_nDropPos && !copyOrders) ? m_nDropPos - 1 : m_nDropPos);
- }
- // Did we actually change anything?
- if(modified)
- m_modDoc.SetModified();
- } else
- {
- if(pt.y < rect.bottom && n == m_nMouseDownPos && !copyOrders)
- {
- // Remove selection if we didn't drag anything but multiselect was active
- m_nScrollPos2nd = ORDERINDEX_INVALID;
- SetFocus();
- SetCurSel(n);
- }
- }
- }
- Invalidate(FALSE);
- } else
- {
- CWnd::OnLButtonUp(nFlags, pt);
- }
- }
- void COrderList::OnMouseMove(UINT nFlags, CPoint pt)
- {
- if((m_bDragging) && (m_cxFont))
- {
- CRect rect;
- GetClientRect(&rect);
- ORDERINDEX n = ORDERINDEX_INVALID;
- if(rect.PtInRect(pt))
- {
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- n = GetOrderFromPoint(pt);
- if(n >= Order().size() && n >= sndFile.GetModSpecifications().ordersMax)
- n = ORDERINDEX_INVALID;
- }
- if(n != m_nDropPos)
- {
- if(n != ORDERINDEX_INVALID)
- {
- m_nMouseDownPos = ORDERINDEX_INVALID;
- m_nDropPos = n;
- Invalidate(FALSE);
- SetCursor(CMainFrame::curDragging);
- } else
- {
- m_nDropPos = ORDERINDEX_INVALID;
- SetCursor(CMainFrame::curNoDrop);
- }
- }
- } else
- {
- CWnd::OnMouseMove(nFlags, pt);
- }
- }
- void COrderList::OnSelectSequence(UINT nid)
- {
- SelectSequence(static_cast<SEQUENCEINDEX>(nid - ID_SEQUENCE_ITEM));
- }
- void COrderList::OnRButtonDown(UINT nFlags, CPoint pt)
- {
- CRect rect;
- GetClientRect(&rect);
- if(m_bDragging)
- {
- m_nDropPos = ORDERINDEX_INVALID;
- OnLButtonUp(nFlags, pt);
- }
- if(pt.y >= rect.bottom)
- return;
- bool multiSelection = (m_nScrollPos2nd != ORDERINDEX_INVALID);
- if(!multiSelection)
- SetCurSel(GetOrderFromPoint(pt), false, false, false);
- SetFocus();
- HMENU hMenu = ::CreatePopupMenu();
- if(!hMenu)
- return;
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- // Check if at least one pattern in the current selection exists
- bool patExists = false;
- OrdSelection selection = GetCurSel();
- LimitMax(selection.lastOrd, Order().GetLastIndex());
- for(ORDERINDEX ord = selection.firstOrd; ord <= selection.lastOrd && !patExists; ord++)
- {
- patExists = Order().IsValidPat(ord);
- }
- const DWORD greyed = patExists ? 0 : MF_GRAYED;
- CInputHandler *ih = CMainFrame::GetInputHandler();
- if(multiSelection)
- {
- // Several patterns are selected.
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_INSERT, ih->GetKeyTextFromCommand(kcOrderlistEditInsert, _T("&Insert Patterns")));
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_DELETE, ih->GetKeyTextFromCommand(kcOrderlistEditDelete, _T("&Remove Patterns")));
- AppendMenu(hMenu, MF_SEPARATOR, NULL, _T(""));
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_COPY, ih->GetKeyTextFromCommand(kcEditCopy, _T("&Copy Patterns")));
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_COPY_ORDERS, ih->GetKeyTextFromCommand(kcOrderlistEditCopyOrders, _T("&Copy Orders")));
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_CUT, ih->GetKeyTextFromCommand(kcEditCut, _T("&C&ut Patterns")));
- AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERNPASTE, ih->GetKeyTextFromCommand(kcEditPaste, _T("P&aste Patterns")));
- AppendMenu(hMenu, MF_SEPARATOR, NULL, _T(""));
- AppendMenu(hMenu, MF_STRING | greyed, ID_ORDERLIST_COPY, ih->GetKeyTextFromCommand(kcDuplicatePattern, _T("&Duplicate Patterns")));
- AppendMenu(hMenu, MF_STRING | greyed, ID_ORDERLIST_MERGE, ih->GetKeyTextFromCommand(kcMergePatterns, _T("&Merge Patterns")));
- } else
- {
- // Only one pattern is selected
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_INSERT, ih->GetKeyTextFromCommand(kcOrderlistEditInsert, _T("&Insert Pattern")));
- if(sndFile.GetModSpecifications().hasIgnoreIndex)
- {
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_INSERT_SEPARATOR, ih->GetKeyTextFromCommand(kcOrderlistEditInsertSeparator, _T("&Insert Separator")));
- }
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_DELETE, ih->GetKeyTextFromCommand(kcOrderlistEditDelete, _T("&Remove Pattern")));
- AppendMenu(hMenu, MF_SEPARATOR, NULL, _T(""));
- AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_NEW, ih->GetKeyTextFromCommand(kcNewPattern, _T("Create &New Pattern")));
- AppendMenu(hMenu, MF_STRING | greyed, ID_ORDERLIST_COPY, ih->GetKeyTextFromCommand(kcDuplicatePattern, _T("&Duplicate Pattern")));
- AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERNCOPY, _T("&Copy Pattern"));
- AppendMenu(hMenu, MF_STRING, ID_PATTERNPASTE, ih->GetKeyTextFromCommand(kcEditPaste, _T("P&aste Pattern")));
- const bool hasPatternProperties = sndFile.GetModSpecifications().patternRowsMin != sndFile.GetModSpecifications().patternRowsMax;
- if(hasPatternProperties || sndFile.GetModSpecifications().hasRestartPos)
- {
- AppendMenu(hMenu, MF_SEPARATOR, NULL, _T(""));
- if(hasPatternProperties)
- AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERN_PROPERTIES, _T("&Pattern Properties..."));
- if(sndFile.GetModSpecifications().hasRestartPos)
- AppendMenu(hMenu, MF_STRING | greyed | ((Order().GetRestartPos() == m_nScrollPos) ? MF_CHECKED : 0), ID_SETRESTARTPOS, _T("R&estart Position"));
- }
- if(sndFile.GetModSpecifications().sequencesMax > 1)
- {
- AppendMenu(hMenu, MF_SEPARATOR, NULL, _T(""));
- HMENU menuSequence = ::CreatePopupMenu();
- AppendMenu(hMenu, MF_POPUP, (UINT_PTR)menuSequence, _T("&Sequences"));
- const SEQUENCEINDEX numSequences = sndFile.Order.GetNumSequences();
- for(SEQUENCEINDEX i = 0; i < numSequences; i++)
- {
- CString str;
- if(sndFile.Order(i).GetName().empty())
- str = MPT_CFORMAT("Sequence {}")(i + 1);
- else
- str = MPT_CFORMAT("{}: {}")(i + 1, mpt::ToCString(sndFile.Order(i).GetName()));
- const UINT flags = (sndFile.Order.GetCurrentSequenceIndex() == i) ? MF_STRING | MF_CHECKED : MF_STRING;
- AppendMenu(menuSequence, flags, ID_SEQUENCE_ITEM + i, str);
- }
- if(sndFile.Order.GetNumSequences() < sndFile.GetModSpecifications().sequencesMax)
- {
- AppendMenu(menuSequence, MF_STRING, ID_SEQUENCE_ITEM + kDuplicateSequence, _T("&Duplicate current sequence"));
- AppendMenu(menuSequence, MF_STRING, ID_SEQUENCE_ITEM + kAddSequence, _T("&Create empty sequence"));
- }
- if(sndFile.Order.GetNumSequences() > 1)
- AppendMenu(menuSequence, MF_STRING, ID_SEQUENCE_ITEM + kDeleteSequence, _T("D&elete current sequence"));
- else
- AppendMenu(menuSequence, MF_STRING, ID_SEQUENCE_ITEM + kSplitSequence, _T("&Split sub songs into sequences"));
- }
- }
- AppendMenu(hMenu, MF_SEPARATOR, NULL, _T(""));
- AppendMenu(hMenu, ((selection.firstOrd == sndFile.m_lockOrderStart && selection.lastOrd == sndFile.m_lockOrderEnd) ? (MF_STRING | MF_CHECKED) : MF_STRING), ID_ORDERLIST_LOCKPLAYBACK, ih->GetKeyTextFromCommand(kcOrderlistLockPlayback, _T("&Lock Playback to Selection")));
- AppendMenu(hMenu, (sndFile.m_lockOrderStart == ORDERINDEX_INVALID ? (MF_STRING | MF_GRAYED) : MF_STRING), ID_ORDERLIST_UNLOCKPLAYBACK, ih->GetKeyTextFromCommand(kcOrderlistUnlockPlayback, _T("&Unlock Playback")));
- AppendMenu(hMenu, MF_SEPARATOR, NULL, _T(""));
- AppendMenu(hMenu, MF_STRING | greyed, ID_ORDERLIST_RENDER, _T("Render to &Wave"));
- ClientToScreen(&pt);
- ::TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, m_hWnd, NULL);
- ::DestroyMenu(hMenu);
- }
- void COrderList::OnLButtonDblClk(UINT, CPoint)
- {
- auto &sndFile = m_modDoc.GetSoundFile();
- m_nScrollPos2nd = ORDERINDEX_INVALID;
- SetFocus();
- if(!EnsureEditable(m_nScrollPos))
- return;
- PATTERNINDEX pat = Order()[m_nScrollPos];
- if(sndFile.Patterns.IsValidPat(pat))
- m_pParent.SetCurrentPattern(pat);
- else if(pat != sndFile.Order.GetIgnoreIndex())
- OnCreateNewPattern();
- }
- void COrderList::OnMButtonDown(UINT nFlags, CPoint pt)
- {
- MPT_UNREFERENCED_PARAMETER(nFlags);
- QueuePattern(pt);
- }
- void COrderList::OnHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar *)
- {
- UINT nNewPos = m_nXScroll;
- UINT smin, smax;
- GetScrollRange(SB_HORZ, (LPINT)&smin, (LPINT)&smax);
- m_bScrolling = true;
- switch(nSBCode)
- {
- case SB_LINELEFT: if (nNewPos) nNewPos--; break;
- case SB_LINERIGHT: if (nNewPos < smax) nNewPos++; break;
- case SB_PAGELEFT: if (nNewPos > 4) nNewPos -= 4; else nNewPos = 0; break;
- case SB_PAGERIGHT: if (nNewPos + 4 < smax) nNewPos += 4; else nNewPos = smax; break;
- case SB_THUMBPOSITION:
- case SB_THUMBTRACK: nNewPos = GetScrollPos(true); break;
- case SB_LEFT: nNewPos = 0; break;
- case SB_RIGHT: nNewPos = smax; break;
- case SB_ENDSCROLL: m_bScrolling = false; break;
- }
- if (nNewPos > smax) nNewPos = smax;
- if (nNewPos != m_nXScroll)
- {
- m_nXScroll = static_cast<ORDERINDEX>(nNewPos);
- SetScrollPos(m_nXScroll);
- Invalidate(FALSE);
- }
- }
- void COrderList::OnSize(UINT nType, int cx, int cy)
- {
- int nPos;
- int smin, smax;
- CWnd::OnSize(nType, cx, cy);
- UpdateScrollInfo();
- GetScrollRange(SB_HORZ, &smin, &smax);
- nPos = GetScrollPos();
- if(nPos > smax)
- nPos = smax;
- if(m_nXScroll != nPos)
- {
- m_nXScroll = mpt::saturate_cast<ORDERINDEX>(nPos);
- SetScrollPos(m_nXScroll);
- Invalidate(FALSE);
- }
- }
- void COrderList::OnInsertOrder()
- {
- // insert the same order(s) after the currently selected order(s)
- ModSequence &order = Order();
- const OrdSelection selection = GetCurSel();
- const ORDERINDEX insertCount = order.insert(selection.lastOrd + 1, selection.lastOrd - selection.firstOrd + 1);
- if(!insertCount)
- return;
- std::copy(order.begin() + selection.firstOrd, order.begin() + selection.firstOrd + insertCount, order.begin() + selection.lastOrd + 1);
- InsertUpdatePlaystate(selection.firstOrd, selection.lastOrd);
- m_nScrollPos = std::min(ORDERINDEX(selection.lastOrd + 1), order.GetLastIndex());
- if(insertCount > 1)
- m_nScrollPos2nd = std::min(ORDERINDEX(m_nScrollPos + insertCount - 1), order.GetLastIndex());
- else
- m_nScrollPos2nd = ORDERINDEX_INVALID;
- InvalidateSelection();
- EnsureVisible(m_nScrollPos2nd);
- // first inserted order has higher priority than the last one
- EnsureVisible(m_nScrollPos);
- Invalidate(FALSE);
- m_modDoc.SetModified();
- m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
- }
- void COrderList::OnInsertSeparatorPattern()
- {
- // Insert a separator pattern after the current pattern, don't move order list cursor
- ModSequence &order = Order();
- const OrdSelection selection = GetCurSel(true);
- ORDERINDEX insertPos = selection.firstOrd;
- if(!EnsureEditable(insertPos))
- return;
- if(order[insertPos] != order.GetInvalidPatIndex())
- {
- // If we're not inserting at a stop (---) index, we move on by one position.
- insertPos++;
- order.insert(insertPos, 1, order.GetIgnoreIndex());
- } else
- {
- order[insertPos] = order.GetIgnoreIndex();
- }
- InsertUpdatePlaystate(insertPos, insertPos);
- Invalidate(FALSE);
- m_modDoc.SetModified();
- m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
- }
- void COrderList::OnRenderOrder()
- {
- OrdSelection selection = GetCurSel();
- m_modDoc.OnFileWaveConvert(selection.firstOrd, selection.lastOrd);
- }
- void COrderList::OnDeleteOrder()
- {
- OrdSelection selection = GetCurSel();
- // remove selection
- m_nScrollPos2nd = ORDERINDEX_INVALID;
- Order().Remove(selection.firstOrd, selection.lastOrd);
- m_modDoc.SetModified();
- Invalidate(FALSE);
- m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
- DeleteUpdatePlaystate(selection.firstOrd, selection.lastOrd);
- SetCurSel(selection.firstOrd, true, false, true);
- }
- void COrderList::OnPatternProperties()
- {
- ModSequence &order = Order();
- const auto ord = GetCurSel(true).firstOrd;
- if(order.IsValidPat(ord))
- m_pParent.PostViewMessage(VIEWMSG_PATTERNPROPERTIES, order[ord]);
- }
- void COrderList::OnPlayerPlay()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_PLAYER_PLAY);
- }
- void COrderList::OnPlayerPause()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_PLAYER_PAUSE);
- }
- void COrderList::OnPlayerPlayFromStart()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_PLAYER_PLAYFROMSTART);
- }
- void COrderList::OnPatternPlayFromStart()
- {
- m_pParent.PostMessage(WM_COMMAND, IDC_PATTERN_PLAYFROMSTART);
- }
- void COrderList::OnCreateNewPattern()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_ORDERLIST_NEW);
- }
- void COrderList::OnDuplicatePattern()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_ORDERLIST_COPY);
- }
- void COrderList::OnMergePatterns()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_ORDERLIST_MERGE);
- }
- void COrderList::OnPatternCopy()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_PATTERNCOPY);
- }
- void COrderList::OnPatternPaste()
- {
- m_pParent.PostMessage(WM_COMMAND, ID_PATTERNPASTE);
- }
- void COrderList::OnSetRestartPos()
- {
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- bool modified = false;
- if(m_nScrollPos == Order().GetRestartPos())
- {
- // Unset position
- modified = (m_nScrollPos != 0);
- Order().SetRestartPos(0);
- } else if(sndFile.GetModSpecifications().hasRestartPos)
- {
- // Set new position
- modified = true;
- Order().SetRestartPos(m_nScrollPos);
- }
- if(modified)
- {
- m_modDoc.SetModified();
- m_modDoc.UpdateAllViews(nullptr, SequenceHint().RestartPos(), this);
- }
- }
- LRESULT COrderList::OnHelpHitTest(WPARAM, LPARAM)
- {
- return HID_BASE_COMMAND + IDC_ORDERLIST;
- }
- LRESULT COrderList::OnDragonDropping(WPARAM doDrop, LPARAM lParam)
- {
- const DRAGONDROP *pDropInfo = (const DRAGONDROP *)lParam;
- CPoint pt;
- if((!pDropInfo) || (&m_modDoc.GetSoundFile() != pDropInfo->sndFile) || (!m_cxFont))
- return FALSE;
- BOOL canDrop = FALSE;
- switch(pDropInfo->dropType)
- {
- case DRAGONDROP_ORDER:
- if(pDropInfo->dropItem >= Order().size())
- break;
- case DRAGONDROP_PATTERN:
- canDrop = TRUE;
- break;
- }
- if(!canDrop || !doDrop)
- return canDrop;
- GetCursorPos(&pt);
- ScreenToClient(&pt);
- if(pt.x < 0)
- pt.x = 0;
- ORDERINDEX posDest = mpt::saturate_cast<ORDERINDEX>(m_nXScroll + (pt.x / m_cxFont));
- if(posDest >= Order().size())
- return FALSE;
- switch(pDropInfo->dropType)
- {
- case DRAGONDROP_PATTERN:
- Order()[posDest] = static_cast<PATTERNINDEX>(pDropInfo->dropItem);
- break;
- case DRAGONDROP_ORDER:
- Order()[posDest] = Order()[pDropInfo->dropItem];
- break;
- }
- if(canDrop)
- {
- Invalidate(FALSE);
- m_modDoc.SetModified();
- m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), this);
- SetCurSel(posDest, true);
- }
- return canDrop;
- }
- ORDERINDEX COrderList::SetMargins(int i)
- {
- m_nOrderlistMargins = i;
- return GetMargins();
- }
- void COrderList::SelectSequence(const SEQUENCEINDEX seq)
- {
- CriticalSection cs;
- CMainFrame::GetMainFrame()->ResetNotificationBuffer();
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- const bool editSequence = seq >= sndFile.Order.GetNumSequences();
- if(seq == kSplitSequence)
- {
- if(!sndFile.Order.CanSplitSubsongs())
- {
- Reporting::Information(U_("No sub songs have been found in this sequence."));
- return;
- }
- if(Reporting::Confirm(U_("The order list contains separator items.\nDo you want to split the sequence at the separators into multiple song sequences?")) != cnfYes)
- return;
- if(!sndFile.Order.SplitSubsongsToMultipleSequences())
- return;
- } else if(seq == kDeleteSequence)
- {
- SEQUENCEINDEX curSeq = sndFile.Order.GetCurrentSequenceIndex();
- mpt::ustring str = MPT_UFORMAT("Remove sequence {}: {}?")(curSeq + 1, mpt::ToUnicode(Order().GetName()));
- if(Reporting::Confirm(str) == cnfYes)
- sndFile.Order.RemoveSequence(curSeq);
- else
- return;
- } else if(seq == kAddSequence || seq == kDuplicateSequence)
- {
- const bool duplicate = (seq == kDuplicateSequence);
- const SEQUENCEINDEX newIndex = sndFile.Order.GetCurrentSequenceIndex() + 1u;
- std::vector<SEQUENCEINDEX> newOrder(sndFile.Order.GetNumSequences());
- std::iota(newOrder.begin(), newOrder.end(), SEQUENCEINDEX(0));
- newOrder.insert(newOrder.begin() + newIndex, duplicate ? sndFile.Order.GetCurrentSequenceIndex() : SEQUENCEINDEX_INVALID);
- if(m_modDoc.ReArrangeSequences(newOrder))
- {
- sndFile.Order.SetSequence(newIndex);
- if(const auto name = sndFile.Order().GetName(); duplicate && !name.empty())
- sndFile.Order().SetName(name + U_(" (Copy)"));
- m_modDoc.UpdateAllViews(nullptr, SequenceHint(SEQUENCEINDEX_INVALID).Names().Data());
- }
- } else if(seq == sndFile.Order.GetCurrentSequenceIndex())
- return;
- else if(seq < sndFile.Order.GetNumSequences())
- sndFile.Order.SetSequence(seq);
- ORDERINDEX posCandidate = Order().GetLengthTailTrimmed() - 1;
- SetCurSel(std::min(m_nScrollPos, posCandidate), true, false, true);
- m_pParent.SetCurrentPattern(Order()[m_nScrollPos]);
- UpdateScrollInfo();
- // This won't make sense anymore in the new sequence.
- OnUnlockPlayback();
- cs.Leave();
- if(editSequence)
- m_modDoc.SetModified();
- m_modDoc.UpdateAllViews(nullptr, SequenceHint().Data(), nullptr);
- }
- void COrderList::QueuePattern(CPoint pt)
- {
- CRect rect;
- GetClientRect(&rect);
- if(!rect.PtInRect(pt))
- return;
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- const PATTERNINDEX ignoreIndex = sndFile.Order.GetIgnoreIndex();
- const PATTERNINDEX stopIndex = sndFile.Order.GetInvalidPatIndex();
- const ORDERINDEX length = Order().GetLength();
- ORDERINDEX order = GetOrderFromPoint(pt);
- // If this is not a playable order item, find the next valid item.
- while(order < length && (Order()[order] == ignoreIndex || Order()[order] == stopIndex))
- {
- order++;
- }
- if(order < length)
- {
- if(sndFile.m_PlayState.m_nSeqOverride == order)
- {
- // This item is already queued: Dequeue it.
- sndFile.m_PlayState.m_nSeqOverride = ORDERINDEX_INVALID;
- } else
- {
- if(Order().IsPositionLocked(order))
- {
- // Users wants to go somewhere else, so let them do that.
- OnUnlockPlayback();
- }
- sndFile.m_PlayState.m_nSeqOverride = order;
- }
- Invalidate(FALSE);
- }
- }
- void COrderList::OnLockPlayback()
- {
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- OrdSelection selection = GetCurSel();
- if(selection.firstOrd == sndFile.m_lockOrderStart && selection.lastOrd == sndFile.m_lockOrderEnd)
- {
- OnUnlockPlayback();
- } else
- {
- sndFile.m_lockOrderStart = selection.firstOrd;
- sndFile.m_lockOrderEnd = selection.lastOrd;
- Invalidate(FALSE);
- }
- }
- void COrderList::OnUnlockPlayback()
- {
- CSoundFile &sndFile = m_modDoc.GetSoundFile();
- sndFile.m_lockOrderStart = sndFile.m_lockOrderEnd = ORDERINDEX_INVALID;
- Invalidate(FALSE);
- }
- void COrderList::InsertUpdatePlaystate(ORDERINDEX first, ORDERINDEX last)
- {
- auto &sndFile = m_modDoc.GetSoundFile();
- Util::InsertItem(first, last, sndFile.m_PlayState.m_nNextOrder);
- if(sndFile.m_PlayState.m_nSeqOverride != ORDERINDEX_INVALID)
- Util::InsertItem(first, last, sndFile.m_PlayState.m_nSeqOverride);
- // Adjust order lock position
- if(sndFile.m_lockOrderStart != ORDERINDEX_INVALID)
- Util::InsertRange(first, last, sndFile.m_lockOrderStart, sndFile.m_lockOrderEnd);
- }
- void COrderList::DeleteUpdatePlaystate(ORDERINDEX first, ORDERINDEX last)
- {
- auto &sndFile = m_modDoc.GetSoundFile();
- Util::DeleteItem(first, last, sndFile.m_PlayState.m_nNextOrder);
- if(sndFile.m_PlayState.m_nSeqOverride != ORDERINDEX_INVALID)
- Util::DeleteItem(first, last, sndFile.m_PlayState.m_nSeqOverride);
- // Adjust order lock position
- if(sndFile.m_lockOrderStart != ORDERINDEX_INVALID)
- Util::DeleteRange(first, last, sndFile.m_lockOrderStart, sndFile.m_lockOrderEnd);
- }
- INT_PTR COrderList::OnToolHitTest(CPoint point, TOOLINFO *pTI) const
- {
- CRect rect;
- GetClientRect(&rect);
- pTI->hwnd = m_hWnd;
- pTI->uId = GetOrderFromPoint(point);
- pTI->rect = rect;
- pTI->lpszText = LPSTR_TEXTCALLBACK;
- return pTI->uId;
- }
- BOOL COrderList::OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *)
- {
- TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
- if(!(pTTT->uFlags & TTF_IDISHWND))
- {
- CString text;
- const CSoundFile &sndFile = m_modDoc.GetSoundFile();
- const ModSequence &order = Order();
- const ORDERINDEX ord = mpt::saturate_cast<ORDERINDEX>(pNMHDR->idFrom), ordLen = order.GetLengthTailTrimmed();
- text.Format(_T("Position %u of %u [%02Xh of %02Xh]"), ord, ordLen, ord, ordLen);
- if(order.IsValidPat(ord))
- {
- PATTERNINDEX pat = order[ord];
- const std::string name = sndFile.Patterns[pat].GetName();
- if(!name.empty())
- {
- ::SendMessage(pNMHDR->hwndFrom, TTM_SETMAXTIPWIDTH, 0, int32_max); // Allow multiline tooltip
- text += _T("\r\n") + mpt::ToCString(sndFile.GetCharsetInternal(), name);
- }
- }
- mpt::String::WriteCStringBuf(pTTT->szText) = text;
- return TRUE;
- }
- return FALSE;
- }
- OPENMPT_NAMESPACE_END
|