From: <sag...@us...> - 2013-03-11 22:47:57
|
Revision: 1560 http://sourceforge.net/p/modplug/code/1560 Author: saga-games Date: 2013-03-11 22:47:40 +0000 (Mon, 11 Mar 2013) Log Message: ----------- [New] Can now copy multiple patterns at once. [New] Pattern Clipboard Manager: Can now have more than one internal clipboard. Accessible through shortcuts only for now. [Reg] Removed "Copy Orders" in favour of multi-pattern copying [Mod] OpenMPT: Version is now 1.21.01.19 Modified Paths: -------------- trunk/OpenMPT/common/version.h trunk/OpenMPT/mptrack/CommandSet.cpp trunk/OpenMPT/mptrack/CommandSet.h trunk/OpenMPT/mptrack/Ctrl_pat.h trunk/OpenMPT/mptrack/Ctrl_seq.cpp trunk/OpenMPT/mptrack/KeyConfigDlg.cpp trunk/OpenMPT/mptrack/Moddoc.h trunk/OpenMPT/mptrack/PatternClipboard.cpp trunk/OpenMPT/mptrack/PatternClipboard.h trunk/OpenMPT/mptrack/TrackerSettings.cpp trunk/OpenMPT/mptrack/View_pat.cpp trunk/OpenMPT/mptrack/View_pat.h trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/mptrack/resource.h Modified: trunk/OpenMPT/common/version.h =================================================================== --- trunk/OpenMPT/common/version.h 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/common/version.h 2013-03-11 22:47:40 UTC (rev 1560) @@ -19,7 +19,7 @@ #define VER_MAJORMAJOR 1 #define VER_MAJOR 21 #define VER_MINOR 01 -#define VER_MINORMINOR 18 +#define VER_MINORMINOR 19 //Creates version number from version parts that appears in version string. //For example MAKE_VERSION_NUMERIC(1,17,02,28) gives version number of Modified: trunk/OpenMPT/mptrack/CommandSet.cpp =================================================================== --- trunk/OpenMPT/mptrack/CommandSet.cpp 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/CommandSet.cpp 2013-03-11 22:47:40 UTC (rev 1560) @@ -652,6 +652,9 @@ DefineKeyCommand(kcFindInstrument, 1897, _T("Pick up nearest instrument number")); DefineKeyCommand(kcPlaySongFromPattern, 1898, _T("Play Song from Pattern Start")); DefineKeyCommand(kcVSTGUIToggleRecordMIDIOut, 1899, _T("Record MIDI Out to Pattern Editor")); + DefineKeyCommand(kcToggleClipboardManager, 1900, _T("Toggle Clipboard Manager")); + DefineKeyCommand(kcClipboardPrev, 1901, _T("Cycle to Previous Clipboard")); + DefineKeyCommand(kcClipboardNext, 1902, _T("Cycle to Next Clipboard")); // Add new key commands here. Modified: trunk/OpenMPT/mptrack/CommandSet.h =================================================================== --- trunk/OpenMPT/mptrack/CommandSet.h 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/CommandSet.h 2013-03-11 22:47:40 UTC (rev 1560) @@ -257,6 +257,12 @@ kcToggleNoteOffRecordMIDI, kcEndPatternEditMisc=kcToggleNoteOffRecordMIDI, + kcStartPatternClipboard, + kcToggleClipboardManager = kcStartPatternClipboard, + kcClipboardPrev, + kcClipboardNext, + kcEndPatternClipboard = kcClipboardNext, + kcStartChannelKeys, kcChannelMute = kcStartChannelKeys, kcChannelSolo, Modified: trunk/OpenMPT/mptrack/Ctrl_pat.h =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_pat.h 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/Ctrl_pat.h 2013-03-11 22:47:40 UTC (rev 1560) @@ -110,7 +110,6 @@ // Clipboard. void OnEditCopy(); void OnEditCut(); - void OnEditPaste(); // Helper function for entering pattern number void EnterPatternNum(int enterNum); Modified: trunk/OpenMPT/mptrack/Ctrl_seq.cpp =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_seq.cpp 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/Ctrl_seq.cpp 2013-03-11 22:47:40 UTC (rev 1560) @@ -60,8 +60,7 @@ ON_COMMAND(ID_ORDERLIST_RENDER, OnRenderOrder) ON_COMMAND(ID_ORDERLIST_EDIT_COPY, OnEditCopy) ON_COMMAND(ID_ORDERLIST_EDIT_CUT, OnEditCut) - ON_COMMAND(ID_ORDERLIST_EDIT_PASTE, OnEditPaste) - + ON_COMMAND(ID_PATTERN_PROPERTIES, OnPatternProperties) ON_COMMAND(ID_PLAYER_PLAY, OnPlayerPlay) ON_COMMAND(ID_PLAYER_PAUSE, OnPlayerPause) @@ -421,7 +420,7 @@ case kcEditCut: OnEditCut(); return wParam; case kcEditPaste: - OnEditPaste(); return wParam; + OnPatternPaste(); return wParam; // Orderlist navigation case kcOrderlistNavigateLeftSelect: @@ -573,11 +572,6 @@ } -static const char szClipboardOrdersHdr[] = "OpenMPT %3s\r\n"; -static const char szClipboardOrdCountFieldHdr[] = "OrdNum: %u\r\n"; -static const char szClipboardOrdersFieldHdr[] = "OrdLst: "; - - void COrderList::OnEditCut() //-------------------------- { @@ -586,138 +580,13 @@ } -void COrderList::OnEditPaste() -//---------------------------- -{ - CMainFrame *pMainFrm = CMainFrame::GetMainFrame(); - CSoundFile* pSf = m_pModDoc->GetSoundFile(); - if (!pMainFrm) - return; - BeginWaitCursor(); - if (pMainFrm->OpenClipboard()) - { - HGLOBAL hCpy = ::GetClipboardData(CF_TEXT); - LPCSTR p; - - if ((hCpy) && ((p = (LPCSTR)GlobalLock(hCpy)) != NULL)) - { - const DWORD dwMemSize = GlobalSize(hCpy); - - if (dwMemSize > sizeof(szClipboardOrdersHdr) && - memcmp(p, "OpenMPT ", 8) == 0 && - memcmp(p + 11, "\r\n", 2) == 0) - { - char buf[8]; - p += sizeof(szClipboardOrdersHdr) - 1; - std::istrstream iStrm(p, dwMemSize - sizeof(szClipboardOrdersHdr) + 1); - ORDERINDEX nCount = 0; - std::vector<PATTERNINDEX> vecPat; - while (iStrm.get(buf, sizeof(buf), '\n')) - { - if (memcmp(buf, "OrdNum:", 8) == 0) // Read expected order count. - iStrm >> nCount; - else if (memcmp(buf, "OrdLst:", 8) != 0) - { // Unrecognized data -> skip line. - iStrm.ignore((std::numeric_limits<std::streamsize>::max)(), '\n'); - continue; - } - else // Read orders. - { - LimitMax(nCount, pSf->GetModSpecifications().ordersMax); - vecPat.reserve(nCount); - char bufItem[16]; - while (iStrm.peek() >= 32 && iStrm.getline(bufItem, sizeof(bufItem), ' ')) - { - if (vecPat.size() >= pSf->GetModSpecifications().ordersMax) - break; - if (!(isdigit(bufItem[0]) || bufItem[0] == '+' || bufItem[0] == '-')) - continue; - PATTERNINDEX nPat = pSf->Order.GetInvalidPatIndex(); - if (bufItem[0] == '+') - { - nPat = pSf->Order.GetIgnoreIndex(); - if(!pSf->GetModSpecifications().hasIgnoreIndex) continue; - } - else if (isdigit(bufItem[0])) - { - nPat = ConvertStrTo<PATTERNINDEX>(bufItem); - if (nPat >= pSf->GetModSpecifications().patternsMax) - nPat = pSf->Order.GetInvalidPatIndex(); - } - vecPat.push_back(nPat); - } - nCount = pSf->Order.Insert(m_nScrollPos, (ORDERINDEX)vecPat.size()); - for (ORDERINDEX nOrd = 0; nOrd < nCount; nOrd++) - pSf->Order[m_nScrollPos + nOrd] = vecPat[nOrd]; - } - m_pModDoc->SetModified(); - m_pModDoc->UpdateAllViews(NULL, HINT_MODSEQUENCE, NULL); - } - } - GlobalUnlock(hCpy); - } - CloseClipboard(); - } - EndWaitCursor(); -} - - void COrderList::OnEditCopy() //--------------------------- { - CMainFrame *pMainFrm = CMainFrame::GetMainFrame(); - if ((!pMainFrm)) return; - const OrdSelection ordsel = GetCurSel(false); - - DWORD dwMemSize; - HGLOBAL hCpy; - BeginWaitCursor(); - dwMemSize = sizeof(szClipboardOrdersHdr) + sizeof(szClipboardOrdersFieldHdr) + sizeof(szClipboardOrdCountFieldHdr); - dwMemSize += ordsel.GetSelCount() * 6 + 8; - if ((pMainFrm->OpenClipboard()) && ((hCpy = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, dwMemSize))!=NULL)) - { - LPCSTR pszFormatName; - EmptyClipboard(); - switch(m_pModDoc->GetSoundFile()->GetType()) - { - case MOD_TYPE_S3M: pszFormatName = "S3M"; break; - case MOD_TYPE_XM: pszFormatName = "XM"; break; - case MOD_TYPE_IT: pszFormatName = "IT"; break; - case MOD_TYPE_MPT: pszFormatName = "MPT"; break; - default: pszFormatName = "MOD"; break; - } - LPSTR p = (LPSTR)GlobalLock(hCpy); - if (p) - { - const ModSequence& seq = m_pModDoc->GetSoundFile()->Order; - wsprintf(p, szClipboardOrdersHdr, pszFormatName); - p += strlen(p); - wsprintf(p, szClipboardOrdCountFieldHdr, ordsel.GetSelCount()); - strcat(p, szClipboardOrdersFieldHdr); - p += strlen(p); - for(ORDERINDEX i = ordsel.firstOrd; i <= ordsel.lastOrd; i++) - { - std::string str; - if (seq[i] == seq.GetInvalidPatIndex()) - str = "-"; - else if (seq[i] == seq.GetIgnoreIndex()) - str = "+"; - else - str = Stringify(seq[i]); - memcpy(p, str.c_str(), str.size()); - p += str.size(); - *p++ = ' '; - } - *p++ = '\r'; - *p++ = '\n'; - *p = 0; - } - GlobalUnlock(hCpy); - SetClipboardData(CF_TEXT, (HANDLE) hCpy); - CloseClipboard(); - } + CViewPattern::GetPatternClipboard().Copy(*m_pModDoc->GetSoundFile(), ordsel.firstOrd, ordsel.lastOrd); + CViewPattern::GetPatternClipboardDialog().UpdateList(); EndWaitCursor(); } @@ -725,7 +594,7 @@ void COrderList::UpdateView(DWORD dwHintMask, CObject *pObj) //---------------------------------------------------------- { - if ((pObj != this) && (dwHintMask & HINT_MODSEQUENCE)) + if(pObj != this && (dwHintMask & HINT_MODSEQUENCE)) { InvalidateRect(NULL, FALSE); UpdateInfoText(); @@ -1093,35 +962,32 @@ HMENU hMenu = ::CreatePopupMenu(); if(!hMenu) return; - // check if at least one pattern in the current selection exists - bool bPatternExists = false; + // Check if at least one pattern in the current selection exists + bool patExists = false; OrdSelection selection = GetCurSel(false); - for(ORDERINDEX nOrd = selection.firstOrd; nOrd <= selection.lastOrd; nOrd++) + for(ORDERINDEX ord = selection.firstOrd; ord <= selection.lastOrd && !patExists; ord++) { - bPatternExists = ((pSndFile->Order[nOrd] < pSndFile->Patterns.Size()) - && (pSndFile->Patterns[pSndFile->Order[nOrd]] != nullptr)); - if(bPatternExists) break; + patExists = pSndFile->Patterns.IsValidPat(pSndFile->Order[ord]); } - const DWORD greyed = bPatternExists ? 0 : MF_GRAYED; + const DWORD greyed = patExists ? 0 : MF_GRAYED; CInputHandler* ih = (CMainFrame::GetMainFrame())->GetInputHandler(); if(multiSelection) { - // several patterns are selected. + // Several patterns are selected. AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_INSERT, "&Insert Patterns\t" + ih->GetKeyTextFromCommand(kcOrderlistEditInsert)); AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_DELETE, "&Remove Patterns\t" + ih->GetKeyTextFromCommand(kcOrderlistEditDelete)); AppendMenu(hMenu, MF_SEPARATOR, NULL, ""); - AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_COPY, "&Copy Orders\t" + ih->GetKeyTextFromCommand(kcEditCopy)); - AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_CUT, "&C&ut Orders\t" + ih->GetKeyTextFromCommand(kcEditCut)); - AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_PASTE, "&Paste Orders\t" + ih->GetKeyTextFromCommand(kcEditPaste)); + AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_COPY, "&Copy Patterns\t" + ih->GetKeyTextFromCommand(kcEditCopy)); + AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_CUT, "&C&ut Patterns\t" + ih->GetKeyTextFromCommand(kcEditCut)); + AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERNPASTE, "P&aste Patterns\t" + ih->GetKeyTextFromCommand(kcEditPaste)); AppendMenu(hMenu, MF_SEPARATOR, NULL, ""); AppendMenu(hMenu, MF_STRING | greyed, ID_ORDERLIST_COPY, "&Duplicate Patterns\t" + ih->GetKeyTextFromCommand(kcDuplicatePattern)); - } - else + } else { - // only one pattern is selected + // Only one pattern is selected AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_INSERT, "&Insert Pattern\t" + ih->GetKeyTextFromCommand(kcOrderlistEditInsert)); if(pSndFile->GetModSpecifications().hasIgnoreIndex) { @@ -1132,12 +998,11 @@ AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_NEW, "Create &New Pattern\t" + ih->GetKeyTextFromCommand(kcNewPattern)); AppendMenu(hMenu, MF_STRING | greyed, ID_ORDERLIST_COPY, "&Duplicate Pattern\t" + ih->GetKeyTextFromCommand(kcDuplicatePattern)); AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERNCOPY, "&Copy Pattern"); - AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERNPASTE, "P&aste Pattern"); - AppendMenu(hMenu, MF_STRING, ID_ORDERLIST_EDIT_PASTE, "&Paste Orders\t" + ih->GetKeyTextFromCommand(kcEditPaste)); + AppendMenu(hMenu, MF_STRING, ID_PATTERNPASTE, "P&aste Pattern\t" + ih->GetKeyTextFromCommand(kcEditPaste)); if (pSndFile->TypeIsIT_MPT_XM()) { AppendMenu(hMenu, MF_SEPARATOR, NULL, ""); - AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERN_PROPERTIES, "&Pattern properties..."); + AppendMenu(hMenu, MF_STRING | greyed, ID_PATTERN_PROPERTIES, "&Pattern Properties..."); } if (pSndFile->GetType() == MOD_TYPE_MPT) { Modified: trunk/OpenMPT/mptrack/KeyConfigDlg.cpp =================================================================== --- trunk/OpenMPT/mptrack/KeyConfigDlg.cpp 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/KeyConfigDlg.cpp 2013-03-11 22:47:40 UTC (rev 1560) @@ -285,6 +285,9 @@ for(int c = kcStartPatternEditMisc; c <= kcEndPatternEditMisc; c++) newCat.commands.Add(c); newCat.separators.Add(kcEndPatternEditMisc); //-------------------------------------- + for(int c = kcStartPatternClipboard; c <= kcEndPatternClipboard; c++) + newCat.commands.Add(c); + newCat.separators.Add(kcEndPatternClipboard); //-------------------------------------- commandCategories.Add(newCat); } Modified: trunk/OpenMPT/mptrack/Moddoc.h =================================================================== --- trunk/OpenMPT/mptrack/Moddoc.h 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/Moddoc.h 2013-03-11 22:47:40 UTC (rev 1560) @@ -182,6 +182,8 @@ public: CSoundFile *GetSoundFile() { return &m_SndFile; } const CSoundFile *GetSoundFile() const { return &m_SndFile; } + CSoundFile &GetrSoundFile() { return m_SndFile; } + const CSoundFile &GetrSoundFile() const { return m_SndFile; } void InitPlayer(); void SetPause(BOOL bPause) { m_bPaused = bPause; } Modified: trunk/OpenMPT/mptrack/PatternClipboard.cpp =================================================================== --- trunk/OpenMPT/mptrack/PatternClipboard.cpp 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/PatternClipboard.cpp 2013-03-11 22:47:40 UTC (rev 1560) @@ -12,18 +12,25 @@ #include "PatternClipboard.h" #include "Mainfrm.h" #include "Moddoc.h" +#include "View_pat.h" -// Currently just one slot for fall-back pasting. -PatternClipboard::clipindex_t PatternClipboard::clipboardSize = 1; +/* Clipboard format: + * Hdr: "ModPlug Tracker S3M\r\n" + * Full: '|C#401v64A06' + * Reset: '|...........' + * Empty: '| ' + * End of row: '\r\n' + * + * When pasting multiple patterns, the header line is followed by the order list: + * Orders: 0,1,2,+,-,1\r\n + * After that, the individual pattern headers and pattern data follows: + * 'Rows: 64\r\n' (must be first) + * 'Name: Pattern Name\r\n' (optional) + * 'Signature: 4/16\r\n' (optional) + * Pattern data... + */ -// Clipboard format: -// Hdr: "ModPlug Tracker S3M\r\n" -// Full: '|C#401v64A06' -// Reset: '|...........' -// Empty: '| ' -// End of row: '\n' - CString PatternClipboard::GetFileExtension(const char *ext) const //--------------------------------------------------------------- { @@ -41,15 +48,107 @@ } +// Copy a range of patterns to both the system clipboard and the internal clipboard. +bool PatternClipboard::Copy(CSoundFile &sndFile, ORDERINDEX first, ORDERINDEX last) +//--------------------------------------------------------------------------------- +{ + LimitMax(first, sndFile.Order.GetLength()); + LimitMax(last, sndFile.Order.GetLength()); + + // Set up clipboard header. + CString data = "ModPlug Tracker " + GetFileExtension(sndFile.GetModSpecifications().fileExtension) + "\r\nOrders: "; + CString patternData; + + // Pattern => Order list assignment + std::vector<PATTERNINDEX> patList(sndFile.Patterns.Size(), PATTERNINDEX_INVALID); + PATTERNINDEX insertedPats = 0; + + // Add order list and pattern information to header. + for(ORDERINDEX ord = first; ord <= last; ord++) + { + PATTERNINDEX pattern = sndFile.Order[ord]; + + if(ord != first) + { + data.AppendChar(','); + } + if(pattern == sndFile.Order.GetInvalidPatIndex()) + { + data.AppendChar('-'); + } else if(pattern == sndFile.Order.GetIgnoreIndex()) + { + data.AppendChar('+'); + } else if(sndFile.Patterns.IsValidPat(pattern)) + { + if(patList[pattern] == PATTERNINDEX_INVALID) + { + // New pattern + patList[pattern] = insertedPats++; + + patternData.AppendFormat("Rows: %u\r\n", sndFile.Patterns[pattern].GetNumRows()); + CString name = sndFile.Patterns[pattern].GetName(); + if(!name.IsEmpty()) + { + patternData.Append("Name: " + name + "\r\n"); + } + if(sndFile.Patterns[pattern].GetOverrideSignature()) + { + patternData.AppendFormat("Signature: %u/%u\r\n", sndFile.Patterns[pattern].GetRowsPerBeat(), sndFile.Patterns[pattern].GetRowsPerMeasure()); + } + patternData.Append(CreateClipboardString(sndFile, pattern, PatternRect(PatternCursor(), PatternCursor(sndFile.Patterns[pattern].GetNumRows() - 1, sndFile.GetNumChannels() - 1, PatternCursor::lastColumn)))); + } + + data.AppendFormat("%u", patList[pattern]); + } + } + data.Append("\r\n" + patternData); + + if(activeClipboard < clipboards.size()) + { + // Copy to internal clipboard + CString desc; + desc.Format("%u Patterns (%u to %u)", last - first + 1, first, last); + clipboards[activeClipboard] = PatternClipboardElement(data, desc); + } + + return ToSystemClipboard(data); +} + + // Copy a pattern selection to both the system clipboard and the internal clipboard. bool PatternClipboard::Copy(CSoundFile &sndFile, PATTERNINDEX pattern, PatternRect selection) //------------------------------------------------------------------------------------------- { - if(!sndFile.Patterns.IsValidPat(pattern)) + CString data = CreateClipboardString(sndFile, pattern, selection); + if(data.IsEmpty()) { return false; } + // Set up clipboard header. + data = "ModPlug Tracker " + GetFileExtension(sndFile.GetModSpecifications().fileExtension) + "\r\n" + data; + + if(activeClipboard < clipboards.size()) + { + // Copy to internal clipboard + CString desc; + desc.Format("%u rows, %u channels (pattern %u)", selection.GetNumRows(), selection.GetNumChannels(), pattern); + clipboards[activeClipboard] = PatternClipboardElement(data, desc); + } + + return ToSystemClipboard(data); +} + + +// Create the clipboard text for a pattern selection +CString PatternClipboard::CreateClipboardString(CSoundFile &sndFile, PATTERNINDEX pattern, PatternRect selection) +//--------------------------------------------------------------------------------------------------------------- +{ + if(!sndFile.Patterns.IsValidPat(pattern)) + { + return ""; + } + if(selection.GetStartColumn() == PatternCursor::paramColumn) { // Special case: If selection starts with a parameter column, extend it to include the effect letter as well. @@ -61,16 +160,9 @@ const ROWINDEX startRow = selection.GetStartRow(), numRows = selection.GetNumRows(); const CHANNELINDEX startChan = selection.GetStartChannel(), numChans = selection.GetNumChannels(); - // XXX - size_t memSize = 21 + numRows * (numChans * 12 + 2) + 1; - CString data; - data.Preallocate(memSize); + data.Preallocate(numRows * (numChans * 12 + 2)); - // Set up clipboard header. - // XXX - data = "ModPlug Tracker " + GetFileExtension(sndFile.GetModSpecifications().fileExtension) + "\r\n"; - for(ROWINDEX row = 0; row < numRows; row++) { if(row + startRow >= sndFile.Patterns[pattern].GetNumRows()) @@ -80,9 +172,9 @@ const ModCommand *m = sndFile.Patterns[pattern].GetpModCommand(row + startRow, startChan); - for(CHANNELINDEX chan = 0; chan < numChans; chan++, m++) + for(CHANNELINDEX chn = 0; chn < numChans; chn++, m++) { - PatternCursor cursor(0, startChan + chan); + PatternCursor cursor(0, startChan + chn); data.AppendChar('|'); // Note @@ -182,55 +274,42 @@ data.Append("\r\n"); } - activeClipboard = 0; - - if(clipboardSize > 0) - { - // Copy to internal clipboard - if(clipboards.size() == clipboardSize) - { - clipboards.pop_back(); - } - - clipboards.push_front(PatternClipboardElement(data)); - } - - return ToSystemClipboard(data); - + return data; } // Try pasting a pattern selection from the system clipboard. -bool PatternClipboard::Paste(CSoundFile &sndFile, PATTERNINDEX pattern, const PatternCursor &pastePos, PasteModes mode) -//--------------------------------------------------------------------------------------------------------------------- +bool PatternClipboard::Paste(CSoundFile &sndFile, ModCommandPos &pastePos, PasteModes mode, ORDERINDEX curOrder) +//-------------------------------------------------------------------------------------------------------------- { CString data; - if(!FromSystemClipboard(data) || !HandlePaste(sndFile, pattern, pastePos, mode, data)) + if(!FromSystemClipboard(data) || !HandlePaste(sndFile, pastePos, mode, data, curOrder)) { // Fall back to internal clipboard if there's no valid pattern data in the system clipboard. - return Paste(sndFile, pattern, pastePos, mode, 0); + return Paste(sndFile, pastePos, mode, curOrder, activeClipboard); } return true; } // Try pasting a pattern selection from an internal clipboard. -bool PatternClipboard::Paste(CSoundFile &sndFile, PATTERNINDEX pattern, const PatternCursor &pastePos, PasteModes mode, clipindex_t internalClipboard) -//---------------------------------------------------------------------------------------------------------------------------------------------------- +bool PatternClipboard::Paste(CSoundFile &sndFile, ModCommandPos &pastePos, PasteModes mode, ORDERINDEX curOrder, clipindex_t internalClipboard) +//--------------------------------------------------------------------------------------------------------------------------------------------- { if(internalClipboard >= clipboards.size()) { return false; } - return HandlePaste(sndFile, pattern, pastePos, mode, clipboards[internalClipboard].content); + return HandlePaste(sndFile, pastePos, mode, clipboards[internalClipboard].content, curOrder); } -// Perform the pasting operation. -bool PatternClipboard::HandlePaste(CSoundFile &sndFile, PATTERNINDEX pattern, const PatternCursor &pastePos, PasteModes mode, const CString &data) -//------------------------------------------------------------------------------------------------------------------------------------------------ +// Parse clipboard string and perform the pasting operation. +bool PatternClipboard::HandlePaste(CSoundFile &sndFile, ModCommandPos &pastePos, PasteModes mode, const CString &data, ORDERINDEX curOrder) +//----------------------------------------------------------------------------------------------------------------------------------------- { - if(!sndFile.Patterns.IsValidPat(pattern) || sndFile.GetpModDoc() == nullptr) + PATTERNINDEX pattern = pastePos.pattern; + if(sndFile.GetpModDoc() == nullptr) { return false; } @@ -239,32 +318,10 @@ const TEMPO tempoMin = sndFile.GetModSpecifications().tempoMin; - CHANNELINDEX startChan = pastePos.GetChannel(), col; - ROWINDEX startRow = pastePos.GetRow(); - ROWINDEX curRow = startRow; bool success = false; bool prepareUndo = true; // prepare pattern for undo next time - bool firstUndo = true; // for chaining undos (see overflow paste) + bool firstUndo = true; // for chaining undos (see overflow / multi-pattern paste) - const bool overflowPaste = (CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_OVERFLOWPASTE) && (mode != pmPasteFlood) && (mode != pmPushForward); - const bool doITStyleMix = (mode == pmMixPasteIT); - const bool doMixPaste = (mode == pmMixPaste) || doITStyleMix; - - // We need to know the current order for overflow paste - ORDERINDEX currentOrder = 0; - if(overflowPaste) - { - ROWINDEX rTemp; - PATTERNINDEX pTemp; - modDoc.GetEditPosition(rTemp, pTemp, currentOrder); - } - - // Can we actually paste at this position? - if(startRow >= sndFile.Patterns[pattern].GetNumRows() || startChan >= sndFile.GetNumChannels()) - { - return false; - } - // Search for signature int pos, startPos = 0; MODTYPE pasteFormat = MOD_TYPE_NONE; @@ -292,16 +349,114 @@ return false; } + // Skip whitespaces + while(data[startPos] == '\r' || data[startPos] == '\n' || data[startPos] == ' ') + { + startPos++; + } + + std::vector<PATTERNINDEX> patList; + bool multiPaste = false; + if(data.Mid(startPos, 8) == "Orders: ") + { + // Pasting several patterns at once. + multiPaste = true; + mode = pmOverwrite; + + if(sndFile.Patterns.IsValidPat(sndFile.Order[curOrder])) + { + // Put new patterns after current pattern, if it exists + curOrder++; + } + + pos = startPos + 8; + startPos = data.Find("\n", pos) + 1; + ORDERINDEX writeOrder = curOrder; + + while(pos < startPos && pos != 0) + { + PATTERNINDEX insertPat; + if(data[pos] == '+') + { + insertPat = sndFile.Order.GetIgnoreIndex(); + } else if(data[pos] == '-') + { + insertPat = sndFile.Order.GetInvalidPatIndex(); + } else + { + insertPat = ConvertStrTo<PATTERNINDEX>(data.Mid(pos, 16)); + + if(insertPat < patList.size()) + { + // Duplicate pattern + insertPat = patList[insertPat]; + } else + { + // New pattern + if(insertPat >= patList.size()) + { + patList.resize(insertPat + 1, PATTERNINDEX_INVALID); + } + + patList[insertPat] = sndFile.Patterns.Insert(64); + insertPat = patList[insertPat]; + } + } + + // Next order item, please + pos = data.Find(",", pos + 1) + 1; + + if((insertPat == sndFile.Order.GetIgnoreIndex() && !sndFile.GetModSpecifications().hasIgnoreIndex) || insertPat == PATTERNINDEX_INVALID) + { + continue; + } + + if(sndFile.Order.Insert(writeOrder, 1) == 0) + { + break; + } + sndFile.Order[writeOrder++] = insertPat; + } + + if(!patList.empty()) + { + // First pattern we're going to paste in. + pattern = patList[0]; + } + + // We already modified the order list... + success = true; + + pastePos.pattern = pattern; + pastePos.row = 0; + pastePos.channel = 0; + } + + size_t curPattern = 0; // Currently pasted pattern for multi-paste + ROWINDEX startRow = pastePos.row; + ROWINDEX curRow = startRow; + CHANNELINDEX startChan = pastePos.channel, col; + + // Can we actually paste at this position? + if(!sndFile.Patterns.IsValidPat(pattern) || startRow >= sndFile.Patterns[pattern].GetNumRows() || startChan >= sndFile.GetNumChannels()) + { + return success; + } + const CModSpecifications &sourceSpecs = CSoundFile::GetModSpecifications(pasteFormat); + const bool overflowPaste = ((CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_OVERFLOWPASTE) && mode != pmPasteFlood && mode != pmPushForward) && !multiPaste; + const bool doITStyleMix = (mode == pmMixPasteIT); + const bool doMixPaste = (mode == pmMixPaste) || doITStyleMix; const bool clipboardHasS3MCommands = (pasteFormat & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_S3M)) != 0; - ModCommand *m = sndFile.Patterns[pattern].GetpModCommand(startRow, 0); + ModCommand *patData = sndFile.Patterns[pattern].GetpModCommand(startRow, 0); pos = startPos; - while(curRow < sndFile.Patterns[pattern].GetNumRows()) + while(curRow < sndFile.Patterns[pattern].GetNumRows() || multiPaste) { + const int nextPatStart = data.Find('|', pos); // Search for column separator or end of paste data. - if((pos = data.Find('|', pos)) == -1) + if(nextPatStart == -1) { // End of paste if(mode == pmPasteFlood && curRow != startRow) @@ -309,20 +464,78 @@ // Restarting pasting from beginning. pos = startPos; continue; - } - else + } else { // Prevent infinite loop with malformed clipboard data. break; } } + // Handle multi-paste: Read pattern information + if(multiPaste) + { + // Skip whitespaces + while(data[pos] == '\r' || data[pos] == '\n' || data[pos] == ' ') + { + pos++; + } + + // Parse pattern header lines + while(pos < nextPatStart) + { + int eol = data.Find("\n", pos + 1) + 1; + if(eol == -1) + { + break; + } + if(data.Mid(pos, 6) == "Rows: ") + { + pos += 6; + // Advance to next pattern + do + { + if(curPattern >= patList.size()) + { + return success; + } + pattern = patList[curPattern++]; + } while (pattern == PATTERNINDEX_INVALID); + ROWINDEX numRows = ConvertStrTo<ROWINDEX>(data.Mid(pos, eol - pos)); + sndFile.Patterns[pattern].Resize(numRows); + patData = sndFile.Patterns[pattern].GetpModCommand(0, 0); + curRow = 0; + prepareUndo = true; + } else if(data.Mid(pos, 6) == "Name: ") + { + pos += 6; + sndFile.Patterns[pattern].SetName(data.Mid(pos, eol - pos - 2).TrimRight()); + } else if(data.Mid(pos, 11) == "Signature: ") + { + pos += 11; + int pos2 = data.Find("/", pos + 1) + 1; + ROWINDEX rpb = ConvertStrTo<ROWINDEX>(data.Mid(pos, pos2 - pos)); + ROWINDEX rpm = ConvertStrTo<ROWINDEX>(data.Mid(pos2, eol - pos2)); + sndFile.Patterns[pattern].SetSignature(rpb, rpm); + } else + { + break; + } + pos = eol; + } + } + + // Search for column separator or end of paste data. + pos = nextPatStart; + success = true; col = startChan; // Paste columns - while((data[pos] == '|') && (pos + 11 < data.GetLength())) + while((pos + 11 < data.GetLength()) && (data[pos] == '|')) { pos++; + // Handle pasting large pattern into smaller pattern (e.g. 128-row pattern into MOD, which only allows 64 rows) + static ModCommand dummy; + ModCommand &m = curRow < sndFile.Patterns[pattern].GetNumRows() ? patData[col] : dummy; // Check valid paste condition. Paste will be skipped if // - col is not a valid channelindex or @@ -330,50 +543,50 @@ // - doing mix paste and trying to paste PCnote on non-empty modcommand. const bool skipPaste = col >= sndFile.GetNumChannels() || - (doMixPaste && m[col].IsPcNote()) || - (doMixPaste && data[pos] == 'P' && !m[col].IsEmpty()); + (doMixPaste && m.IsPcNote()) || + (doMixPaste && data[pos] == 'P' && !m.IsEmpty()); if(skipPaste == false) { // Before changing anything in this pattern, we have to create an undo point. if(prepareUndo) { - modDoc.GetPatternUndo().PrepareUndo(pattern, startChan, startRow, sndFile.GetNumChannels(), sndFile.Patterns[pattern].GetNumRows(), !firstUndo); + modDoc.GetPatternUndo().PrepareUndo(pattern, startChan, startRow, sndFile.GetNumChannels(), sndFile.Patterns[pattern].GetNumRows(), "Paste", !firstUndo); prepareUndo = false; firstUndo = false; } // ITSyle mixpaste requires that we keep a copy of the thing we are about to paste on // so that we can refer back to check if there was anything in e.g. the note column before we pasted. - const ModCommand origModCmd = m[col]; + const ModCommand origModCmd = m; // push channel data below paste point first. if(mode == pmPushForward) { - for(ROWINDEX nPushRow = sndFile.Patterns[pattern].GetNumRows() - 1 - curRow; nPushRow > 0; nPushRow--) + for(ROWINDEX pushRow = sndFile.Patterns[pattern].GetNumRows() - 1 - curRow; pushRow > 0; pushRow--) { - m[col + nPushRow * sndFile.GetNumChannels()] = m[col + (nPushRow - 1) * sndFile.GetNumChannels()]; + patData[col + pushRow * sndFile.GetNumChannels()] = patData[col + (pushRow - 1) * sndFile.GetNumChannels()]; } - m[col].Clear(); + m.Clear(); } // Note if(data[pos] != ' ' && (!doMixPaste || ((!doITStyleMix && origModCmd.note == NOTE_NONE) || (doITStyleMix && origModCmd.note == NOTE_NONE && origModCmd.instr == 0 && origModCmd.volcmd == VOLCMD_NONE)))) { - m[col].note = NOTE_NONE; + m.note = NOTE_NONE; if(data[pos] == '=') - m[col].note = NOTE_KEYOFF; + m.note = NOTE_KEYOFF; else if(data[pos] == '^') - m[col].note = NOTE_NOTECUT; + m.note = NOTE_NOTECUT; else if(data[pos] == '~') - m[col].note = NOTE_FADE; + m.note = NOTE_FADE; else if(data[pos] == 'P') { if(data[pos + 2] == 'S') - m[col].note = NOTE_PCS; + m.note = NOTE_PCS; else - m[col].note = NOTE_PC; + m.note = NOTE_PC; } else if (data[pos] != '.') { // Check note names @@ -381,18 +594,18 @@ { if(data[pos] == szNoteNames[i][0] && data[pos + 1] == szNoteNames[i][1]) { - m[col].note = ModCommand::NOTE(i + NOTE_MIN); + m.note = ModCommand::NOTE(i + NOTE_MIN); break; } } - if(m[col].note != NOTE_NONE) + if(m.note != NOTE_NONE) { // Check octave - m[col].note += (data[pos + 2] - '0') * 12; - if(!m[col].IsNote()) + m.note += (data[pos + 2] - '0') * 12; + if(!m.IsNote()) { // Invalid octave - m[col].note = NOTE_NONE; + m.note = NOTE_NONE; } } } @@ -404,8 +617,8 @@ { if(data[pos + 3] >= '0' && data[pos + 3] <= ('0' + (MAX_SAMPLES / 10))) { - m[col].instr = (data[pos + 3] - '0') * 10 + (data[pos + 4] - '0'); - } else m[col].instr = 0; + m.instr = (data[pos + 3] - '0') * 10 + (data[pos + 4] - '0'); + } else m.instr = 0; } // Volume @@ -414,45 +627,44 @@ { if(data[pos + 5] != '.') { - if(m[col].IsPcNote()) + if(m.IsPcNote()) { - m[col].SetValueVolCol(ConvertStrTo<uint16>(data.Mid(pos + 5, 3))); + m.SetValueVolCol(ConvertStrTo<uint16>(data.Mid(pos + 5, 3))); } else { - m[col].volcmd = VOLCMD_NONE; + m.volcmd = VOLCMD_NONE; for(ModCommand::VOLCMD i = 1; i < MAX_VOLCMDS; i++) { const char cmd = sourceSpecs.GetVolEffectLetter(i); if(data[pos + 5] == cmd && cmd != '?') { - m[col].volcmd = i; + m.volcmd = i; break; } } - m[col].vol = (data[pos + 6] - '0') * 10 + (data[pos + 7] - '0'); + m.vol = (data[pos + 6] - '0') * 10 + (data[pos + 7] - '0'); } } else { - m[col].volcmd = VOLCMD_NONE; - m[col].vol = 0; + m.volcmd = VOLCMD_NONE; + m.vol = 0; } } // Effect - if(m[col].IsPcNote()) + if(m.IsPcNote()) { if(data[pos + 8] != '.' && data[pos + 8] > ' ') { - m[col].SetValueEffectCol(ConvertStrTo<uint16>(data.Mid(pos + 8, 3))); + m.SetValueEffectCol(ConvertStrTo<uint16>(data.Mid(pos + 8, 3))); } - } - else + } else { if(data[pos + 8] > ' ' && (!doMixPaste || ((!doITStyleMix && origModCmd.command == CMD_NONE) || (doITStyleMix && origModCmd.command == CMD_NONE && origModCmd.param == 0)))) { - m[col].command = CMD_NONE; + m.command = CMD_NONE; if(data[pos + 8] != '.') { for(ModCommand::COMMAND i = 1; i < MAX_EFFECTS; i++) @@ -460,7 +672,7 @@ const char cmd = sourceSpecs.GetEffectLetter(i); if(data[pos + 8] == cmd && cmd != '?') { - m[col].command = i; + m.command = i; break; } } @@ -471,13 +683,13 @@ if(data[pos + 9] > ' ' && (!doMixPaste || ((!doITStyleMix && (origModCmd.command == CMD_NONE || origModCmd.param == 0)) || (doITStyleMix && origModCmd.command == CMD_NONE && origModCmd.param == 0)))) { - m[col].param = 0; + m.param = 0; if(data[pos + 9] != '.') { for(size_t i = 0; i < 16; i++) { - if(data[pos + 9] == szHexChar[i]) m[col].param |= (i << 4); - if(data[pos + 10] == szHexChar[i]) m[col].param |= i; + if(data[pos + 9] == szHexChar[i]) m.param |= (i << 4); + if(data[pos + 10] == szHexChar[i]) m.param |= i; } } } @@ -485,45 +697,45 @@ // Speed / tempo command conversion if (sndFile.GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM)) { - switch(m[col].command) + switch(m.command) { case CMD_SPEED: case CMD_TEMPO: if(!clipboardHasS3MCommands) { - if(m[col].param < tempoMin) + if(m.param < tempoMin) { - m[col].command = CMD_SPEED; + m.command = CMD_SPEED; } else { - m[col].command = CMD_TEMPO; + m.command = CMD_TEMPO; } } else { - if(m[col].command == CMD_SPEED && m[col].param >= tempoMin) + if(m.command == CMD_SPEED && m.param >= tempoMin) { - m[col].param = CMD_TEMPO; - } else if(m[col].command == CMD_TEMPO && m[col].param < tempoMin) + m.param = CMD_TEMPO; + } else if(m.command == CMD_TEMPO && m.param < tempoMin) { - m[col].param = CMD_SPEED; + m.param = CMD_SPEED; } } break; } } else { - switch(m[col].command) + switch(m.command) { case CMD_SPEED: case CMD_TEMPO: if(!clipboardHasS3MCommands) { - if(m[col].param < tempoMin) + if(m.param < tempoMin) { - m[col].command = CMD_SPEED; + m.command = CMD_SPEED; } else { - m[col].command = CMD_TEMPO; + m.command = CMD_TEMPO; } } break; @@ -536,14 +748,14 @@ // of the old modcommand would falsely be interpreted being of type // origFormat and ConvertCommand could change them. if (pasteFormat != sndFile.GetType() && (!doMixPaste || origModCmd.IsEmpty(false))) - m[col].Convert(pasteFormat, sndFile.GetType()); + m.Convert(pasteFormat, sndFile.GetType()); } pos += 11; col++; } // Next row - m += sndFile.GetNumChannels(); + patData += sndFile.GetNumChannels(); curRow++; if(overflowPaste) @@ -553,12 +765,12 @@ while(curRow >= sndFile.Patterns[pattern].GetNumRows()) { curRow = 0; - ORDERINDEX nextOrder = sndFile.Order.GetNextOrderIgnoringSkips(currentOrder); + ORDERINDEX nextOrder = sndFile.Order.GetNextOrderIgnoringSkips(curOrder); if(nextOrder == 0 || nextOrder >= sndFile.Order.size()) return success; pattern = sndFile.Order[nextOrder]; if(sndFile.Patterns.IsValidPat(pattern) == false) return success; - m = sndFile.Patterns[pattern]; - currentOrder = nextOrder; + patData = sndFile.Patterns[pattern]; + curOrder = nextOrder; prepareUndo = true; startRow = 0; } @@ -578,6 +790,7 @@ return ToSystemClipboard(clipboards[activeClipboard]); } + // Switch to the next internal clipboard. bool PatternClipboard::CycleForward() //----------------------------------- @@ -612,32 +825,11 @@ void PatternClipboard::SetClipboardSize(clipindex_t maxEntries) //------------------------------------------------------------- { - clipboardSize = maxEntries; - RestrictClipboardSize(); + clipboards.resize(maxEntries, PatternClipboardElement("", "unused")); + LimitMax(activeClipboard, maxEntries - 1); } -// Remove all clipboard contents. -void PatternClipboard::Clear() -//---------------------------- -{ - clipboards.clear(); - activeClipboard = 0; -} - - -// Keep the number of clipboards consistent with the maximum number of allowed clipboards. -void PatternClipboard::RestrictClipboardSize() -//-------------------------------------------- -{ - if(clipboards.size() > clipboardSize) - { - clipboards.resize(clipboardSize); - activeClipboard = 0; - } -} - - // System-specific clipboard functions bool PatternClipboard::ToSystemClipboard(const CString &data) //----------------------------------------------------------- @@ -692,4 +884,102 @@ ::CloseClipboard(); return(p != nullptr); -} \ No newline at end of file +} + + +BEGIN_MESSAGE_MAP(PatternClipboardDialog, CDialog) + ON_EN_UPDATE(IDC_EDIT1, OnNumClipboardsChanged) + ON_LBN_SELCHANGE(IDC_LIST1, OnSelectClipboard) +END_MESSAGE_MAP() + + +void PatternClipboardDialog::DoDataExchange(CDataExchange* pDX) +//------------------------------------------------------------- +{ + DDX_Control(pDX, IDC_SPIN1, numClipboardsSpin); + DDX_Control(pDX, IDC_LIST1, clipList); +} + + +PatternClipboardDialog::PatternClipboardDialog(PatternClipboard &c) : clipboards(c), isLocked(true), isCreated(false), posX(-1) +//----------------------------------------------------------------------------------------------------------------------------- +{ +} + + +void PatternClipboardDialog::Show() +//--------------------------------- +{ + isLocked = true; + if(!isCreated) + { + Create(IDD_CLIPBOARD, CMainFrame::GetMainFrame()); + numClipboardsSpin.SetRange(0, int16_max); + } + SetDlgItemInt(IDC_EDIT1, clipboards.GetClipboardSize(), FALSE); + isLocked = false; + isCreated = true; + UpdateList(); + + SetWindowPos(nullptr, posX, posY, 0, 0, SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER | (posX == -1 ? SWP_NOMOVE : 0)); +} + + +void PatternClipboardDialog::OnNumClipboardsChanged() +//--------------------------------------------------- +{ + if(isLocked) + { + return; + } + clipboards.SetClipboardSize(GetDlgItemInt(IDC_EDIT1, nullptr, FALSE)); + UpdateList(); +} + + +void PatternClipboardDialog::UpdateList() +//--------------------------------------- +{ + if(isLocked) + { + return; + } + clipList.ResetContent(); + PatternClipboard::clipindex_t i = 0; + for(std::deque<PatternClipboardElement>::const_iterator clip = clipboards.clipboards.cbegin(); clip != clipboards.clipboards.cend(); clip++, i++) + { + const int item = clipList.AddString(clip->description); + clipList.SetItemDataPtr(item, reinterpret_cast<void *>(i)); + if(clipboards.activeClipboard == i) + { + clipList.SetCurSel(item); + } + } +} + + +void PatternClipboardDialog::OnSelectClipboard() +//---------------------------------------------- +{ + if(isLocked) + { + return; + } + PatternClipboard::clipindex_t item = reinterpret_cast<PatternClipboard::clipindex_t>(clipList.GetItemDataPtr(clipList.GetCurSel())); + clipboards.SelectClipboard(item); +} + + +void PatternClipboardDialog::OnCancel() +//------------------------------------- +{ + isCreated = false; + isLocked = true; + + RECT rect; + GetWindowRect(&rect); + posX = rect.left; + posY = rect.top; + + DestroyWindow(); +} Modified: trunk/OpenMPT/mptrack/PatternClipboard.h =================================================================== --- trunk/OpenMPT/mptrack/PatternClipboard.h 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/PatternClipboard.h 2013-03-11 22:47:40 UTC (rev 1560) @@ -14,6 +14,8 @@ #include "Sndfile.h" #include "PatternCursor.h" +struct ModCommandPos; + //=========================== class PatternClipboardElement //=========================== @@ -21,11 +23,12 @@ public: CString content; + CString description; public: PatternClipboardElement() { }; - PatternClipboardElement(CString &data) : content(data) { }; + PatternClipboardElement(const CString &data, const CString &desc) : content(data), description(desc) { }; }; @@ -33,6 +36,8 @@ class PatternClipboard //==================== { + friend class PatternClipboardDialog; + public: enum PasteModes @@ -49,8 +54,6 @@ protected: - // Maximum number of internal clipboard entries - static clipindex_t clipboardSize; // Active internal clipboard index clipindex_t activeClipboard; // Internal clipboard collection @@ -58,14 +61,16 @@ public: - PatternClipboard() : activeClipboard(0) { }; + PatternClipboard() : activeClipboard(0) { SetClipboardSize(1); }; + // Copy a range of patterns to both the system clipboard and the internal clipboard. + bool Copy(CSoundFile &sndFile, ORDERINDEX first, ORDERINDEX last); // Copy a pattern selection to both the system clipboard and the internal clipboard. bool Copy(CSoundFile &sndFile, PATTERNINDEX pattern, PatternRect selection); // Try pasting a pattern selection from the system clipboard. - bool Paste(CSoundFile &sndFile, PATTERNINDEX pattern, const PatternCursor &pastePos, PasteModes mode); + bool Paste(CSoundFile &sndFile, ModCommandPos &pastePos, PasteModes mode, ORDERINDEX curOrder); // Try pasting a pattern selection from an internal clipboard. - bool Paste(CSoundFile &sndFile, PATTERNINDEX pattern, const PatternCursor &pastePos, PasteModes mode, clipindex_t internalClipboard); + bool Paste(CSoundFile &sndFile, ModCommandPos &pastePos, PasteModes mode, ORDERINDEX curOrder, clipindex_t internalClipboard); // Copy one of the internal clipboards to the system clipboard. bool SelectClipboard(clipindex_t which); // Switch to the next internal clipboard. @@ -76,18 +81,16 @@ void SetClipboardSize(clipindex_t maxEntries); // Return the current number of clipboards. clipindex_t GetClipboardSize() const { return clipboards.size(); }; - // Remove all clipboard contents. - void Clear(); protected: + // Create the clipboard text for a pattern selection + CString CreateClipboardString(CSoundFile &sndFile, PATTERNINDEX pattern, PatternRect selection); + CString GetFileExtension(const char *ext) const; - // Perform the pasting operation. - bool HandlePaste(CSoundFile &sndFile, PATTERNINDEX pattern, const PatternCursor &pastePos, PasteModes mode, const CString &data); + // Parse clipboard string and perform the pasting operation. + bool HandlePaste(CSoundFile &sndFile, ModCommandPos &pastePos, PasteModes mode, const CString &data, ORDERINDEX curOrder); - // Keep the number of clipboards consistent with the maximum number of allowed clipboards. - void RestrictClipboardSize(); - // System-specific clipboard functions bool ToSystemClipboard(const PatternClipboardElement &clipboard) { return ToSystemClipboard(clipboard.content); }; bool ToSystemClipboard(const CString &data); @@ -95,15 +98,28 @@ }; -//========================== -class PatternClipboardDialog -//========================== +//=========================================== +class PatternClipboardDialog : public CDialog +//=========================================== { protected: - PatternClipboard &clipboards; + CSpinButtonCtrl numClipboardsSpin; + CListBox clipList; + int posX, posY; + bool isLocked, isCreated; public: + PatternClipboardDialog(PatternClipboard &c); + void UpdateList(); + void Show(); + void Toggle() { if(isCreated) OnCancel(); else Show(); } - PatternClipboardDialog(PatternClipboard &c) : clipboards(c) { }; +protected: + virtual void DoDataExchange(CDataExchange* pDX); + DECLARE_MESSAGE_MAP(); + + afx_msg void OnCancel(); + afx_msg void OnNumClipboardsChanged(); + afx_msg void OnSelectClipboard(); }; Modified: trunk/OpenMPT/mptrack/TrackerSettings.cpp =================================================================== --- trunk/OpenMPT/mptrack/TrackerSettings.cpp 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/TrackerSettings.cpp 2013-03-11 22:47:40 UTC (rev 1560) @@ -22,6 +22,7 @@ #include "../common/StringFixer.h" #include "TrackerSettings.h" #include "../common/misc_util.h" +#include "View_pat.h" const TCHAR *TrackerSettings::m_szDirectoryToSettingsName[NUM_DIRS] = { _T("Songs_Directory"), _T("Samples_Directory"), _T("Instruments_Directory"), _T("Plugins_Directory"), _T("Plugin_Presets_Directory"), _T("Export_Directory"), _T(""), _T("") }; @@ -396,6 +397,7 @@ m_nSampleUndoMaxBuffer = CMainFrame::GetPrivateProfileLong("Sample Editor" , "UndoBufferSize", m_nSampleUndoMaxBuffer >> 20, iniFile); m_nSampleUndoMaxBuffer = max(1, m_nSampleUndoMaxBuffer) << 20; + CViewPattern::GetPatternClipboard().SetClipboardSize(GetPrivateProfileInt("Pattern Editor", "NumClipboards", CViewPattern::GetPatternClipboard().GetClipboardSize(), iniFile)); // Default Paths TCHAR szPath[_MAX_PATH] = ""; @@ -754,6 +756,8 @@ CMainFrame::WritePrivateProfileDWord("Pattern Editor", "AutoChordWaitTime", gnAutoChordWaitTime, iniFile); CMainFrame::WritePrivateProfileDWord("Pattern Editor", "RecordQuantize", recordQuantizeRows, iniFile); + CMainFrame::WritePrivateProfileDWord("Pattern Editor", "NumClipboards", CViewPattern::GetPatternClipboard().GetClipboardSize(), iniFile); + // Write default paths const bool bConvertPaths = theApp.IsPortableMode(); TCHAR szPath[_MAX_PATH] = ""; Modified: trunk/OpenMPT/mptrack/View_pat.cpp =================================================================== --- trunk/OpenMPT/mptrack/View_pat.cpp 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/View_pat.cpp 2013-03-11 22:47:40 UTC (rev 1560) @@ -45,6 +45,7 @@ // Static initializers ModCommand CViewPattern::m_cmdOld = ModCommand::Empty(); PatternClipboard CViewPattern::patternClipboard; +PatternClipboardDialog CViewPattern::patternClipboardDialog(CViewPattern::patternClipboard); IMPLEMENT_SERIAL(CViewPattern, CModScrollView, 0) @@ -272,7 +273,7 @@ } else if (CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_CONTSCROLL) { - UINT nCurOrder = SendCtrlMessage(CTRLMSG_GETCURRENTORDER); + ORDERINDEX nCurOrder = SendCtrlMessage(CTRLMSG_GETCURRENTORDER); if ((nCurOrder > 0) && (nCurOrder < pSndFile->Order.size()) && (m_nPattern == pSndFile->Order[nCurOrder])) { const ORDERINDEX prevOrd = pSndFile->Order.GetPreviousOrderIgnoringSkips(nCurOrder); @@ -343,10 +344,10 @@ SetCurSel(selStart, m_Cursor); UpdateIndicator(); - Log("Row: %d; Chan: %d; ColType: %d;\n", - selStart.GetRow(), - selStart.GetChannel(), - selStart.GetColumnType()); +// Log("Row: %d; Chan: %d; ColType: %d;\n", +// selStart.GetRow(), +// selStart.GetChannel(), +// selStart.GetColumnType()); return true; } @@ -4077,7 +4078,7 @@ case VIEWMSG_PASTEPATTERN: { - PastePattern(m_nPattern, 0, PatternClipboard::pmOverwrite); + PastePattern(m_nPattern, PatternCursor(0), PatternClipboard::pmOverwrite); InvalidatePattern(); } break; @@ -4366,6 +4367,19 @@ if(m_nSpacing < MAX_SPACING) SetSpacing(m_nSpacing + 1); break; + // Clipboard Manager + case kcToggleClipboardManager: + patternClipboardDialog.Toggle(); + break; + case kcClipboardPrev: + patternClipboard.CycleBackward(); + patternClipboardDialog.UpdateList(); + break; + case kcClipboardNext: + patternClipboard.CycleForward(); + patternClipboardDialog.UpdateList(); + break; + } //Ranges: if(wParam >= kcVPStartNotes && wParam <= kcVPEndNotes) @@ -4784,7 +4798,7 @@ } // Create undo-point. - pModDoc->GetPatternUndo().PrepareUndo(nPat, nChn, nRow, 1, 1, "Note Stop Entry"); + pModDoc->GetPatternUndo().PrepareUndo(nPat, nChn, nRow, 1, 1); // -- write sdx if playing live if(usePlaybackPosition && nTick && pTarget->command == CMD_NONE && !doQuantize) @@ -4937,41 +4951,41 @@ { CMainFrame *pMainFrm = CMainFrame::GetMainFrame(); CModDoc *pModDoc = GetDocument(); - CSoundFile *pSndFile; - if(pMainFrm == nullptr || pModDoc == nullptr || (pSndFile = pModDoc->GetSoundFile()) == nullptr) + if(pMainFrm == nullptr || pModDoc == nullptr) { return; } + CSoundFile &sndFile = pModDoc->GetrSoundFile(); ROWINDEX nRow = GetCurrentRow(); - const ROWINDEX nRowPlayback = pSndFile->m_nRow; - const UINT nTick = pSndFile->m_nTickCount; - const PATTERNINDEX nPatPlayback = pSndFile->m_nPattern; + const ROWINDEX nRowPlayback = sndFile.m_nRow; + const UINT nTick = sndFile.m_nTickCount; + const PATTERNINDEX nPatPlayback = sndFile.m_nPattern; const bool recordEnabled = IsEditingEnabled(); CHANNELINDEX nChn = GetCurrentChannel(); if(note < NOTE_MIN_SPECIAL) { - Limit(note, pSndFile->GetModSpecifications().noteMin, pSndFile->GetModSpecifications().noteMax); + Limit(note, sndFile.GetModSpecifications().noteMin, sndFile.GetModSpecifications().noteMax); } // Special case: Convert note off commands to C00 for MOD files - if((pSndFile->GetType() == MOD_TYPE_MOD) && (note == NOTE_NOTECUT || note == NOTE_FADE || note == NOTE_KEYOFF)) + if((sndFile.GetType() == MOD_TYPE_MOD) && (note == NOTE_NOTECUT || note == NOTE_FADE || note == NOTE_KEYOFF)) { TempEnterFX(CMD_VOLUME, 0); return; } // Check whether the module format supports the note. - if(pSndFile->GetModSpecifications().HasNote(note) == false) + if(sndFile.GetModSpecifications().HasNote(note) == false) { return; } BYTE recordGroup = pModDoc->IsChannelRecord(nChn); const bool liveRecord = IsLiveRecord(); - const bool usePlaybackPosition = (liveRecord && (CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_AUTODELAY) && !pSndFile->m_SongFlags[SONG_STEP]); + const bool usePlaybackPosition = (liveRecord && (CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_AUTODELAY) && !sndFile.m_SongFlags[SONG_STEP]); const bool isSplit = IsNoteSplit(note); if(pModDoc->GetSplitKeyboardSettings().IsSplitActive() @@ -5004,7 +5018,7 @@ PATTERNINDEX nPat = m_nPattern; if(usePlaybackPosition) - SetEditPos(*pSndFile, nRow, nPat, nRowPlayback, nPatPlayback); + SetEditPos(sndFile, nRow, nPat, nRowPlayback, nPatPlayback); // Quantize const bool doQuantize = (liveRecord || (fromMidi && (CMainFrame::GetSettings().m_dwMidiSetup & MIDISETUP_PLAYPATTERNONMIDIIN))) && CMainFrame::GetSettings().recordQuantizeRows != 0; @@ -5012,14 +5026,14 @@ { QuantizeRow(nPat, nRow); // "Grace notes" are stuffed into the next row, if possible - if(pSndFile->Patterns[nPat].GetpModCommand(nRow, nChn)->IsNote() && nRow < pSndFile->Patterns[nPat].GetNumRows() - 1) + if(sndFile.Patterns[nPat].GetpModCommand(nRow, nChn)->IsNote() && nRow < sndFile.Patterns[nPat].GetNumRows() - 1) { nRow++; } } // -- Work out where to put the new note - ModCommand *pTarget = pSndFile->Patterns[nPat].GetpModCommand(nRow, nChn); + ModCommand *pTarget = sndFile.Patterns[nPat].GetpModCommand(nRow, nChn); ModCommand newcmd = *pTarget; // Param control 'note' @@ -5070,7 +5084,7 @@ if(volWrite != -1) { - if(pSndFile->GetModSpecifications().HasVolCommand(VOLCMD_VOLUME)) + if(sndFile.GetModSpecifications().HasVolCommand(VOLCMD_VOLUME)) { newcmd.volcmd = VOLCMD_VOLUME; newcmd.vol = (ModCommand::VOL)volWrite; @@ -5086,9 +5100,9 @@ { if(newcmd.command == CMD_NONE) //make sure we don't overwrite any existing commands. { - newcmd.command = (pSndFile->TypeIsS3M_IT_MPT()) ? CMD_S3MCMDEX : CMD_MODCMDEX; + newcmd.command = (sndFile.TypeIsS3M_IT_MPT()) ? CMD_S3MCMDEX : CMD_MODCMDEX; UINT maxSpeed = 0x0F; - if(pSndFile->m_nMusicSpeed > 0) maxSpeed = min(0x0F, pSndFile->m_nMusicSpeed - 1); + if(sndFile.m_nMusicSpeed > 0) maxSpeed = min(0x0F, sndFile.m_nMusicSpeed - 1); newcmd.param = 0xD0 + min(maxSpeed, nTick); } } @@ -5131,7 +5145,7 @@ ROWINDEX srow = nRow; while(srow-- > 0) { - search -= pSndFile->GetNumChannels(); + search -= sndFile.GetNumChannels(); if (search->instr && !search->IsPcNote()) { nPlayIns = search->instr; @@ -5174,7 +5188,7 @@ { if((m_nSpacing > 0) && (m_nSpacing <= MAX_SPACING)) { - if(nRow + m_nSpacing < pSndFile->Patterns[nPat].GetNumRows() || (CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_CONTSCROLL)) + if(nRow + m_nSpacing < sndFile.Patterns[nPat].GetNumRows() || (CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_CONTSCROLL)) { SetCurrentRow(nRow + m_nSpacing, (CMainFrame::GetSettings().m_dwPatternSetup & PATTERN_CONTSCROLL) ? true: false); m_bLastNoteEntryBlocked = false; @@ -5217,11 +5231,11 @@ { CMainFrame *pMainFrm = CMainFrame::GetMainFrame(); CModDoc *pModDoc = GetDocument(); - CSoundFile *pSndFile; - if(pMainFrm == nullptr || pModDoc == nullptr || (pSndFile = pModDoc->GetSoundFile()) == nullptr) + if(pMainFrm == nullptr || pModDoc == nullptr) { return; } + CSoundFile &sndFile = pModDoc->GetrSoundFile(); UINT nPlayChord = 0; BYTE chordplaylist[3]; @@ -5229,13 +5243,13 @@ const CHANNELINDEX nChn = GetCurrentChannel(); UINT nPlayIns = 0; // Simply backup the whole row. - pModDoc->GetPatternUndo().PrepareUndo(m_nPattern, nChn, GetCurrentRow(), pSndFile->GetNumChannels(), 1); + pModDoc->GetPatternUndo().PrepareUndo(m_nPattern, nChn, GetCurrentRow(), sndFile.GetNumChannels(), 1); - const PatternRow rowBase = pSndFile->Patterns[m_nPattern].GetRow(GetCurrentRow()); + const PatternRow rowBase = sndFile.Patterns[m_nPattern].GetRow(GetCurrentRow()); ModCommand* pTarget = &rowBase[nChn]; // Save old row contents - vector<ModCommand> newrow(pSndFile->GetNumChannels()); - for(CHANNELINDEX n = 0; n < pSndFile->GetNumChannels(); n++) + vector<ModCommand> newrow(sndFile.GetNumChannels()); + for(CHANNELINDEX n = 0; n < sndFile.GetNumChannels(); n++) { newrow[n] = rowBase[n]; } @@ -5272,10 +5286,10 @@ recordGroup = pModDoc->IsChannelRecord(nChn); - for (UINT kchrd=1; kchrd<pSndFile->m_nChannels; kchrd++) + for (UINT kchrd=1; kchrd < sndFile.m_nChannels; kchrd++) { if ((nchno > 2) || (!pChords[nchord].notes[nchno])) break; - if (++nchordch >= pSndFile->m_nChannels) nchordch = 0; + if (++nchordch >= sndFile.m_nChannels) nchordch = 0; currentRecordGroup = pModDoc->IsChannelRecord(nchordch); if (!recordGroup) @@ -5316,7 +5330,7 @@ if(modified) { - for(CHANNELINDEX n = 0; n < pSndFile->GetNumChannels(); n++) + for(CHANNELINDEX n = 0; n < sndFile.GetNumChannels(); n++) { rowBase[n] = newrow[n]; } @@ -5354,7 +5368,7 @@ ROWINDEX srow = GetCurrentRow(); while (srow-- > 0) { - search -= pSndFile->GetNumChannels(); + search -= sndFile.GetNumChannels(); if (search->instr) { nPlayIns = search->instr; @@ -6655,6 +6669,7 @@ BeginWaitCursor(); bool result = GetPatternClipboard().Copy(*GetSoundFile(), nPattern, selection); EndWaitCursor(); + patternClipboardDialog.UpdateList(); return result; } @@ -6664,13 +6679,26 @@ //---------------------------------------------------------------------------------------------------------------------- { BeginWaitCursor(); - bool result = GetPatternClipboard().Paste(*GetSoundFile(), nPattern, pastePos, mode); + ModCommandPos pos; + pos.pattern = nPattern; + pos.row = pastePos.GetRow(); + pos.channel = pastePos.GetChannel(); + bool result = GetPatternClipboard().Paste(*GetSoundFile(), pos, mode, SendCtrlMessage(CTRLMSG_GETCURRENTORDER)); EndWaitCursor(); + if(pos.pattern != nPattern) + { + // Multipaste: Switch to pasted pattern. + SetCurrentPattern(pos.pattern); + ORDERINDEX curOrder = SendCtrlMessage(CTRLMSG_GETCURRENTORDER); + curOrder = GetSoundFile()->Order.FindOrder(pos.pattern, curOrder); + SendCtrlMessage(CTRLMSG_SETCURRENTORDER, curOrder); + } + if(result) { GetDocument()->SetModified(); - GetDocument()->UpdateAllViews(NULL, HINT_PATTERNDATA | (nPattern << HINT_SHIFT_PAT), NULL); + GetDocument()->UpdateAllViews(NULL, HINT_MODSEQUENCE | HINT_PATTERNDATA | (pos.pattern << HINT_SHIFT_PAT), NULL); } return result; Modified: trunk/OpenMPT/mptrack/View_pat.h =================================================================== --- trunk/OpenMPT/mptrack/View_pat.h 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/View_pat.h 2013-03-11 22:47:40 UTC (rev 1560) @@ -190,6 +190,7 @@ // Internal pattern clipboard static PatternClipboard patternClipboard; + static PatternClipboardDialog patternClipboardDialog; QuickChannelProperties quickChannelProperties; @@ -215,6 +216,7 @@ void SetModified(bool updateAllViews = true); static PatternClipboard &GetPatternClipboard() { return patternClipboard; } + static PatternClipboardDialog &GetPatternClipboardDialog() { return patternClipboardDialog; } bool UpdateSizes(); void UpdateScrollSize(); Modified: trunk/OpenMPT/mptrack/mptrack.rc =================================================================== --- trunk/OpenMPT/mptrack/mptrack.rc 2013-03-11 15:49:33 UTC (rev 1559) +++ trunk/OpenMPT/mptrack/mptrack.rc 2013-03-11 22:47:40 UTC (rev 1560) @@ -243,6 +243,19 @@ CONTROL "&Surround",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,192,54,42,12 END +IDD_CLIPBOARD DIALOGEX 0, 0, 166, 201 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTERMOUSE | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Clipboard Manager" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LISTBOX IDC_LIST1,6,6,156,156,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "Number of clipboards:",IDC_... [truncated message content] |