1 /*
2 * Copyright (C) 2018-2020 Rerrah
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use,
8 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following
11 * conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "pattern_editor_panel.hpp"
27 #include <algorithm>
28 #include <vector>
29 #include <utility>
30 #include <stdexcept>
31 #include <thread>
32 #include <unordered_map>
33 #include <numeric>
34 #include <QPainter>
35 #include <QFontMetrics>
36 #include <QFontInfo>
37 #include <QPoint>
38 #include <QApplication>
39 #include <QClipboard>
40 #include <QMenu>
41 #include <QAction>
42 #include <QRegularExpression>
43 #include <QRegularExpressionMatch>
44 #include <QMetaMethod>
45 #include <QIcon>
46 #include "gui/event_guard.hpp"
47 #include "gui/command/pattern/pattern_commands_qt.hpp"
48 #include "midi/midi.hpp"
49 #include "jam_manager.hpp"
50 #include "gui/effect_description.hpp"
51 #include "gui/jam_layout.hpp"
52 #include "gui/gui_util.hpp"
53
PatternEditorPanel(QWidget * parent)54 PatternEditorPanel::PatternEditorPanel(QWidget *parent)
55 : QWidget(parent),
56 config_(std::make_shared<Configuration>()), // Dummy
57 stepFontWidth_(0),
58 stepFontHeight_(0),
59 stepFontAscent_(0),
60 stepFontLeading_(0),
61 headerFontAscent_(0),
62 widthSpace_(0),
63 widthSpaceDbl_(0),
64 stepNumWidthCnt_(0),
65 stepNumWidth_(0),
66 stepNumBase_(0),
67 baseTrackWidth_(0),
68 toneNameWidth_(0),
69 instWidth_(0),
70 volWidth_(0),
71 effWidth_(0),
72 effIDWidth_(0),
73 effValWidth_(0),
74 tracksWidthFromLeftToEnd_(0),
75 hdMuteToggleWidth_(0),
76 hdEffCompandButtonWidth_(0),
77 headerHeight_(0),
78 hdPlusY_(0),
79 hdMinusY_(0),
80 curRowBaselineY_(0),
81 curRowY_(0),
82 visTracks_(1), // Dummy
83 rightEffn_(16), // Dummy
84 leftTrackVisIdx_(0),
85 curSongNum_(0),
86 curPos_{ 0, 0, 0, 0, },
87 hovPos_{ -1, -1, -1, -1 },
88 mousePressPos_{ -1, -1, -1, -1 },
89 mouseReleasePos_{ -1, -1, -1, -1 },
90 selLeftAbovePos_{ -1, -1, -1, -1 },
91 selRightBelowPos_{ -1, -1, -1, -1 },
92 shiftPressedPos_{ -1, -1, -1, -1 },
93 doubleClickPos_{ -1, -1, -1, -1 },
94 markerPos_{ -1, -1, -1, -1 },
95 isIgnoreToSlider_(false),
96 isIgnoreToOrder_(false),
97 isPressedPlus_(false),
98 isPressedMinus_(false),
99 entryCnt_(0),
100 selectAllState_(-1),
101 isMuteElse_(false),
102 hl1Cnt_(4),
103 hl2Cnt_(16),
104 editableStepCnt_(1),
105 viewedRowCnt_(1),
106 viewedRowsHeight_(0),
107 viewedRowOffset_(0),
108 viewedCenterY_(0),
109 viewedCenterBaseY_(0),
110 backChanged_(false),
111 textChanged_(false),
112 foreChanged_(false),
113 headerChanged_(false),
114 focusChanged_(false),
115 followModeChanged_(false),
116 hasFocussedBefore_(false),
117 stepDownCount_(0),
118 repaintable_(true),
119 repaintingCnt_(0),
120 isInitedFirstMod_(false),
121 upSc_(Qt::Key_Up, this, nullptr, nullptr, Qt::WidgetShortcut),
122 upWSSc_(Qt::SHIFT + Qt::Key_Up, this, nullptr, nullptr, Qt::WidgetShortcut),
123 dnSc_(Qt::Key_Down, this, nullptr, nullptr, Qt::WidgetShortcut),
124 dnWSSc_(Qt::SHIFT + Qt::Key_Down, this, nullptr, nullptr, Qt::WidgetShortcut),
125 pgUpSc_(Qt::Key_PageUp, this, nullptr, nullptr, Qt::WidgetShortcut),
126 pgUpWSSc_(Qt::SHIFT + Qt::Key_PageUp, this, nullptr, nullptr, Qt::WidgetShortcut),
127 pgDnSc_(Qt::Key_PageDown, this, nullptr, nullptr, Qt::WidgetShortcut),
128 pgDnWSSc_(Qt::SHIFT + Qt::Key_PageDown, this, nullptr, nullptr, Qt::WidgetShortcut),
129 homeSc_(Qt::Key_Home, this, nullptr, nullptr, Qt::WidgetShortcut),
130 homeWSSc_(Qt::SHIFT + Qt::Key_Home, this, nullptr, nullptr, Qt::WidgetShortcut),
131 endSc_(Qt::Key_End, this, nullptr, nullptr, Qt::WidgetShortcut),
132 endWSSc_(Qt::SHIFT + Qt::Key_End, this, nullptr, nullptr, Qt::WidgetShortcut),
133 hlUpSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut),
134 hlUpWSSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut),
135 hlDnSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut),
136 hlDnWSSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut),
137 ltSc_(Qt::Key_Left, this, nullptr, nullptr, Qt::WidgetShortcut),
138 ltWSSc_(Qt::SHIFT + Qt::Key_Left, this, nullptr, nullptr, Qt::WidgetShortcut),
139 rtSc_(Qt::Key_Right, this, nullptr, nullptr, Qt::WidgetShortcut),
140 rtWSSc_(Qt::SHIFT + Qt::Key_Right, this, nullptr, nullptr, Qt::WidgetShortcut),
141 keyOffSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut),
142 echoBufSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut),
143 stepMvUpSc_(Qt::ALT + Qt::Key_Up, this, nullptr, nullptr, Qt::WidgetShortcut),
144 stepMvDnSc_(Qt::ALT + Qt::Key_Down, this, nullptr, nullptr, Qt::WidgetShortcut),
145 expandColSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut),
146 shrinkColSc_(QKeySequence(), this, nullptr, nullptr, Qt::WidgetShortcut)
147 {
148 setAttribute(Qt::WA_Hover);
149 setFocusPolicy(Qt::ClickFocus);
150 setContextMenuPolicy(Qt::CustomContextMenu);
151
152 // Initialize font
153 headerFont_ = QApplication::font();
154 headerFont_.setPointSize(10);
155 stepFont_ = QFont("Monospace", 10);
156 stepFont_.setStyleHint(QFont::TypeWriter);
157 stepFont_.setStyleStrategy(QFont::ForceIntegerMetrics);
158
159 updateSizes();
160
161 // Track visibility
162 songStyle_.type = SongType::Standard; // Dummy
163 songStyle_.trackAttribs.push_back({ 0, SoundSource::FM, 0 }); // Dummy
164 std::iota(visTracks_.begin(), visTracks_.end(), 0);
165
166 // Shortcuts
__anon5de16e990102(bool isShift, auto getFunc) 167 auto vMoveLam = [&] (bool isShift, auto getFunc) { // For lazy evaluation
168 if (bt_->isPlaySong() && bt_->isFollowPlay()) return;
169 moveCursorToDown(getFunc());
170 checkSelectionByCursorMove(isShift);
171 };
__anon5de16e990202null172 auto upLam = [&] { return (editableStepCnt_ ? -editableStepCnt_ : -1); };
__anon5de16e990302null173 QObject::connect(&upSc_, &QShortcut::activated, this, [vMoveLam, upLam] { vMoveLam(false, upLam); });
__anon5de16e990402null174 QObject::connect(&upWSSc_, &QShortcut::activated, this, [vMoveLam, upLam] { vMoveLam(true, upLam); });
__anon5de16e990502null175 auto dnLam = [&] { return (editableStepCnt_ ? editableStepCnt_ : 1); };
__anon5de16e990602null176 QObject::connect(&dnSc_, &QShortcut::activated, this, [vMoveLam, dnLam] { vMoveLam(false, dnLam); });
__anon5de16e990702null177 QObject::connect(&dnWSSc_, &QShortcut::activated, this, [vMoveLam, dnLam] { vMoveLam(true, dnLam); });
__anon5de16e990802null178 auto pgUpLam = [&] { return -static_cast<int>(config_->getPageJumpLength()); };
__anon5de16e990902null179 QObject::connect(&pgUpSc_, &QShortcut::activated, this, [vMoveLam, pgUpLam] { vMoveLam(false, pgUpLam); });
__anon5de16e990a02null180 QObject::connect(&pgUpWSSc_, &QShortcut::activated, this, [vMoveLam, pgUpLam] { vMoveLam(true, pgUpLam); });
__anon5de16e990b02null181 auto pgDnLam = [&] { return static_cast<int>(config_->getPageJumpLength()); };
__anon5de16e990c02null182 QObject::connect(&pgDnSc_, &QShortcut::activated, this, [vMoveLam, pgDnLam] { vMoveLam(false, pgDnLam); });
__anon5de16e990d02null183 QObject::connect(&pgDnWSSc_, &QShortcut::activated, this, [vMoveLam, pgDnLam] { vMoveLam(true, pgDnLam); });
__anon5de16e990e02null184 auto homeLam = [&] { return -curPos_.step; };
__anon5de16e990f02null185 QObject::connect(&homeSc_, &QShortcut::activated, this, [vMoveLam, homeLam] { vMoveLam(false, homeLam); });
__anon5de16e991002null186 QObject::connect(&homeWSSc_, &QShortcut::activated, this, [vMoveLam, homeLam] { vMoveLam(true, homeLam); });
__anon5de16e991102null187 auto endLam = [&] {
188 return (static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - curPos_.step - 1);
189 };
__anon5de16e991202null190 QObject::connect(&endSc_, &QShortcut::activated, this, [vMoveLam, endLam] { vMoveLam(false, endLam); });
__anon5de16e991302null191 QObject::connect(&endWSSc_, &QShortcut::activated, this, [vMoveLam, endLam] { vMoveLam(true, endLam); });
__anon5de16e991402null192 auto hlUpLam = [&] {
193 int base;
194 if (curPos_.step) {
195 base = curPos_.step;
196 }
197 else {
198 base = static_cast<int>(bt_->getPatternSizeFromOrderNumber(
199 curSongNum_,
200 (curPos_.order) ? (curPos_.order - 1)
201 : (static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1)));
202 }
203 return ((base - 1) / hl1Cnt_ * hl1Cnt_ - base);
204 };
__anon5de16e991502null205 QObject::connect(&hlUpSc_, &QShortcut::activated, this, [vMoveLam, hlUpLam] { vMoveLam(false, hlUpLam); });
__anon5de16e991602null206 QObject::connect(&hlUpWSSc_, &QShortcut::activated, this, [vMoveLam, hlUpLam] { vMoveLam(true, hlUpLam); });
__anon5de16e991702null207 auto hlDnLam = [&] {
208 int next = std::min((curPos_.step / hl1Cnt_ + 1) * hl1Cnt_,
209 static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)));
210 return (next - curPos_.step);
211 };
__anon5de16e991802null212 QObject::connect(&hlDnSc_, &QShortcut::activated, this, [vMoveLam, hlDnLam] { vMoveLam(false, hlDnLam); });
__anon5de16e991902null213 QObject::connect(&hlDnWSSc_, &QShortcut::activated, this, [vMoveLam, hlDnLam] { vMoveLam(true, hlDnLam); });
__anon5de16e991a02(bool isLeft, bool isShift) 214 auto ltRtLam = [&] (bool isLeft, bool isShift) {
215 moveCursorToRight(isLeft ? -1 : 1);
216 checkSelectionByCursorMove(isShift);
217 };
__anon5de16e991b02null218 QObject::connect(<Sc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(true, false); });
__anon5de16e991c02null219 QObject::connect(<WSSc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(true, true); });
__anon5de16e991d02null220 QObject::connect(&rtSc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(false, false); });
__anon5de16e991e02null221 QObject::connect(&rtWSSc_, &QShortcut::activated, this, [ltRtLam] { ltRtLam(false, true); });
__anon5de16e991f02null222 QObject::connect(&keyOffSc_, &QShortcut::activated, this, [&] {
223 if (!bt_->isJamMode() && curPos_.colInTrack == 0) {
224 bt_->setStepKeyOff(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step);
225 comStack_.lock()->push(new SetKeyOffToStepQtCommand(this));
226 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_);
227 }
228 });
__anon5de16e992002null229 QObject::connect(&echoBufSc_, &QShortcut::activated, this, [&] {
230 if (!bt_->isJamMode() && curPos_.colInTrack == 0) {
231 int n = bt_->getCurrentOctave();
232 if (n > 3) n = 3;
233 bt_->setEchoBufferAccess(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, n);
234 comStack_.lock()->push(new SetEchoBufferAccessQtCommand(this));
235 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_);
236 }
237 });
__anon5de16e992102null238 QObject::connect(&stepMvUpSc_, &QShortcut::activated, this, [&] {
239 if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !bt_->isJamMode())
240 deletePreviousStep();
241 });
__anon5de16e992202null242 QObject::connect(&stepMvDnSc_, &QShortcut::activated, this, [&] {
243 if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !bt_->isJamMode()) {
244 insertStep();
245 moveCursorToDown(1);
246 }
247 });
__anon5de16e992302null248 QObject::connect(&expandColSc_, &QShortcut::activated, this, [&] {
249 onExpandEffectColumnPressed(curPos_.trackVisIdx);
250 });
__anon5de16e992402null251 QObject::connect(&shrinkColSc_, &QShortcut::activated, this, [&] {
252 onShrinkEffectColumnPressed(curPos_.trackVisIdx);
253 });
254 onShortcutUpdated();
255
256 // MIDI
257 midiKeyEventMethod_ = metaObject()->indexOfSlot("midiKeyEvent(uchar,uchar,uchar)");
258 Q_ASSERT(midiKeyEventMethod_ != -1);
259 MidiInterface::instance().installInputHandler(&midiThreadReceivedEvent, this);
260 }
261
~PatternEditorPanel()262 PatternEditorPanel::~PatternEditorPanel()
263 {
264 MidiInterface::instance().uninstallInputHandler(&midiThreadReceivedEvent, this);
265 }
266
funcResize()267 void PatternEditorPanel::funcResize()
268 {
269 // Recalculate center row position
270 curRowY_ = (geometry().height() + headerHeight_ - stepFontHeight_) >> 1;
271 curRowBaselineY_ = curRowY_ + stepFontAscent_ - (stepFontLeading_ >> 1);
272
273 initDisplay();
274 }
275
updateSizes()276 void PatternEditorPanel::updateSizes()
277 {
278 QFontMetrics metrics(stepFont_);
279 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
280 stepFontWidth_ = metrics.horizontalAdvance('0');
281 #else
282 stepFontWidth_ = metrics.width('0');
283 #endif
284 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
285 stepFontAscent_ = metrics.capHeight();
286 #else
287 stepFontAscent_ = metrics.boundingRect('X').height();
288 #endif
289 stepFontLeading_ = metrics.ascent() - stepFontAscent_ + metrics.descent() / 2;
290 stepFontHeight_ = stepFontAscent_ + stepFontLeading_;
291
292 QFontMetrics m(headerFont_);
293 headerFontAscent_ = m.ascent() + 2;
294
295 /* Width & height */
296 widthSpace_ = stepFontWidth_ / 5 * 2;
297 widthSpaceDbl_ = widthSpace_ * 2;
298 stepNumWidthCnt_ = config_->getShowRowNumberInHex() ? 2 : 3;
299 if (config_->getShowRowNumberInHex()) {
300 stepNumWidthCnt_ = 2;
301 stepNumBase_ = 16;
302 }
303 else {
304 stepNumWidthCnt_ = 3;
305 stepNumBase_ = 10;
306 }
307 stepNumWidth_ = stepFontWidth_ * stepNumWidthCnt_ + widthSpace_;
308 toneNameWidth_ = stepFontWidth_ * 3;
309 instWidth_ = stepFontWidth_ * 2;
310 volWidth_ = stepFontWidth_ * 2;
311 effIDWidth_ = stepFontWidth_ * 2;
312 effValWidth_ = stepFontWidth_ * 2;
313 effWidth_ = effIDWidth_ + effValWidth_ + widthSpaceDbl_;
314 baseTrackWidth_ = toneNameWidth_ + instWidth_ + volWidth_
315 + effIDWidth_ + effValWidth_ + widthSpaceDbl_ * 4;
316 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
317 hdEffCompandButtonWidth_ = m.horizontalAdvance("+");
318 #else
319 hdEffCompandButtonWidth_ = m.width("+");
320 #endif
321 hdMuteToggleWidth_ = baseTrackWidth_ - hdEffCompandButtonWidth_ - stepFontWidth_ / 2 * 3;
322 headerHeight_ = m.height() * 2 + 1;
323 hdPlusY_ = headerHeight_ / 4 + m.lineSpacing() / 2 - m.leading() / 2 - m.descent();
324 hdMinusY_ = headerHeight_ / 2 + hdPlusY_;
325
326 initDisplay();
327 }
328
initDisplay()329 void PatternEditorPanel::initDisplay()
330 {
331 int width = geometry().width();
332
333 // Recalculate pixmap sizes
334 viewedRegionHeight_ = std::max((geometry().height() - headerHeight_), stepFontHeight_);
335 int cnt = viewedRegionHeight_ / stepFontHeight_;
336 viewedRowCnt_ = (cnt % 2) ? (cnt + 2) : (cnt + 1);
337 viewedRowsHeight_ = viewedRowCnt_ * stepFontHeight_;
338
339 viewedRowOffset_ = (viewedRowsHeight_ - viewedRegionHeight_) >> 1;
340 viewedCenterY_ = (viewedRowsHeight_ - stepFontHeight_) >> 1;
341 viewedCenterBaseY_ = viewedCenterY_ + stepFontAscent_ + (stepFontLeading_ >> 1);
342
343 completePixmap_ = QPixmap(geometry().size());
344 backPixmap_ = QPixmap(width, viewedRowsHeight_);
345 textPixmap_ = QPixmap(width, viewedRowsHeight_);
346 forePixmap_ = QPixmap(width, viewedRowsHeight_);
347 headerPixmap_ = QPixmap(width, headerHeight_);
348 }
349
setCore(std::shared_ptr<BambooTracker> core)350 void PatternEditorPanel::setCore(std::shared_ptr<BambooTracker> core)
351 {
352 bt_ = core;
353 }
354
isReadyCore() const355 bool PatternEditorPanel::isReadyCore() const
356 {
357 return (bt_ != nullptr);
358 }
359
setCommandStack(std::weak_ptr<QUndoStack> stack)360 void PatternEditorPanel::setCommandStack(std::weak_ptr<QUndoStack> stack)
361 {
362 comStack_ = stack;
363 }
364
setConfiguration(std::shared_ptr<Configuration> config)365 void PatternEditorPanel::setConfiguration(std::shared_ptr<Configuration> config)
366 {
367 config_ = config;
368 }
369
setColorPallete(std::shared_ptr<ColorPalette> palette)370 void PatternEditorPanel::setColorPallete(std::shared_ptr<ColorPalette> palette)
371 {
372 palette_ = palette;
373 }
374
waitPaintFinish()375 void PatternEditorPanel::waitPaintFinish()
376 {
377 while (true) {
378 if (repaintingCnt_.load())
379 std::this_thread::sleep_for(std::chrono::milliseconds(10));
380 else {
381 curPos_ = { 0, 0, 0, 0 }; // Init
382 return;
383 }
384 }
385 }
386
getHeaderFont() const387 QString PatternEditorPanel::getHeaderFont() const
388 {
389 return QFontInfo(headerFont_).family();
390 }
391
getHeaderFontSize() const392 int PatternEditorPanel::getHeaderFontSize() const
393 {
394 return QFontInfo(headerFont_).pointSize();
395 }
396
getRowsFont() const397 QString PatternEditorPanel::getRowsFont() const
398 {
399 return QFontInfo(stepFont_).family();
400 }
401
getRowsFontSize() const402 int PatternEditorPanel::getRowsFontSize() const
403 {
404 return QFontInfo(stepFont_).pointSize();
405 }
406
setFonts(QString headerFont,int headerSize,QString rowsFont,int rowsSize)407 void PatternEditorPanel::setFonts(QString headerFont, int headerSize, QString rowsFont, int rowsSize)
408 {
409 headerFont_ = QFont(headerFont, headerSize);
410 stepFont_ = QFont(rowsFont, rowsSize);
411
412 updateSizes();
413 updateTracksWidthFromLeftToEnd();
414
415 redrawAll();
416 }
417
setVisibleTracks(std::vector<int> tracks)418 void PatternEditorPanel::setVisibleTracks(std::vector<int> tracks)
419 {
420 visTracks_ = tracks;
421 rightEffn_.resize(visTracks_.size());
422 std::transform(visTracks_.begin(), visTracks_.end(), rightEffn_.begin(), [&](int t) {
423 return static_cast<int>(bt_->getEffectDisplayWidth(curSongNum_, t));
424 });
425 int max = static_cast<int>(tracks.size());
426 bool cond = (max <= curPos_.trackVisIdx);
427 if (cond) curPos_.trackVisIdx = max;
428 leftTrackVisIdx_ = std::min(leftTrackVisIdx_, curPos_.trackVisIdx);
429 updateTracksWidthFromLeftToEnd();
430
431 bt_->setCurrentTrack(visTracks_.at(curPos_.trackVisIdx));
432 emit currentTrackChanged(curPos_.trackVisIdx);
433
434 if (cond) {
435 if (config_->getMoveCursorByHorizontalScroll())
436 emit hScrollBarChangeRequested(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack));
437 else
438 emit hScrollBarChangeRequested(leftTrackVisIdx_);
439 }
440
441 redrawAll();
442 }
443
getVisibleTracks() const444 std::vector<int> PatternEditorPanel::getVisibleTracks() const
445 {
446 return visTracks_;
447 }
448
redrawByPatternChanged(bool patternSizeChanged)449 void PatternEditorPanel::redrawByPatternChanged(bool patternSizeChanged)
450 {
451 textChanged_ = true;
452
453 // When pattern size is changed, redraw all area
454 if (patternSizeChanged) {
455 backChanged_ = true;
456 foreChanged_ = true;
457 }
458
459 repaint();
460 }
461
redrawByFocusChanged()462 void PatternEditorPanel::redrawByFocusChanged()
463 {
464 if (hasFocussedBefore_) {
465 focusChanged_ = true;
466 repaint();
467 }
468 else {
469 redrawAll();
470 hasFocussedBefore_ = true;
471 }
472 }
473
redrawByHoverChanged()474 void PatternEditorPanel::redrawByHoverChanged()
475 {
476 headerChanged_ = true;
477 backChanged_ = true;
478 repaint();
479 }
480
redrawByMaskChanged()481 void PatternEditorPanel::redrawByMaskChanged()
482 {
483 foreChanged_ = true;
484 headerChanged_ = true;
485 repaint();
486 }
487
redrawPatterns()488 void PatternEditorPanel::redrawPatterns()
489 {
490 backChanged_ = true;
491 textChanged_ = true;
492 foreChanged_ = true;
493 repaint();
494 }
495
redrawAll()496 void PatternEditorPanel::redrawAll()
497 {
498 headerChanged_ = true;
499 redrawPatterns();
500 }
501
resetEntryCount()502 void PatternEditorPanel::resetEntryCount()
503 {
504 entryCnt_ = 0;
505 }
506
drawPattern(const QRect & rect)507 void PatternEditorPanel::drawPattern(const QRect &rect)
508 {
509 if (repaintable_.load()) {
510 repaintable_.store(false);
511 ++repaintingCnt_; // Use module data after this line
512
513 if (rect.size() != completePixmap_.size()) { // Prevent resize event was failed
514 funcResize();
515 headerChanged_ = true;
516 backChanged_ = true;
517 textChanged_ = true;
518 foreChanged_ = true;
519 }
520
521 if (backChanged_ || textChanged_ || foreChanged_ || headerChanged_ || focusChanged_ || stepDownCount_ || followModeChanged_) {
522
523 int maxWidth = std::min(rect.width(), tracksWidthFromLeftToEnd_);
524 completePixmap_.fill(palette_->ptnBackColor);
525
526 if (!focusChanged_) {
527 if (stepDownCount_ && !followModeChanged_) {
528 quickDrawRows(maxWidth);
529 }
530 else {
531 backPixmap_.fill(Qt::transparent);
532 if (textChanged_) textPixmap_.fill(Qt::transparent);
533 if (foreChanged_) forePixmap_.fill(Qt::transparent);
534 drawRows(maxWidth);
535 }
536 drawBorders(maxWidth);
537
538 if (headerChanged_) {
539 // headerPixmap_->fill(Qt::transparent);
540 drawHeaders(maxWidth);
541 }
542 }
543
544 {
545 QPainter mergePainter(&completePixmap_);
546 QRect rowsRect(0, viewedRowOffset_, maxWidth, viewedRegionHeight_);
547 QRect inViewRect(0, headerHeight_, maxWidth, viewedRegionHeight_);
548 mergePainter.drawPixmap(inViewRect, backPixmap_, rowsRect);
549 mergePainter.drawPixmap(inViewRect, textPixmap_, rowsRect);
550 mergePainter.drawPixmap(inViewRect, forePixmap_, rowsRect);
551 mergePainter.drawPixmap(headerPixmap_.rect(), headerPixmap_);
552 }
553
554 if (!hasFocus()) drawShadow();
555
556 backChanged_ = false;
557 textChanged_ = false;
558 foreChanged_ = false;
559 headerChanged_ = false;
560 focusChanged_ = false;
561 followModeChanged_ = false;
562 stepDownCount_ = 0;
563 }
564
565 --repaintingCnt_; // Used module data until this line
566 repaintable_.store(true);
567 }
568
569 QPainter completePainter(this);
570 completePainter.drawPixmap(rect, completePixmap_);
571 }
572
drawRows(int maxWidth)573 void PatternEditorPanel::drawRows(int maxWidth)
574 {
575 QPainter forePainter(&forePixmap_);
576 QPainter textPainter(&textPixmap_);
577 QPainter backPainter(&backPixmap_);
578 textPainter.setFont(stepFont_);
579
580 /* Current row */
581 // Fill row
582 backPainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_,
583 bt_->isJamMode() ? palette_->ptnCurStepColor : palette_->ptnCurEditStepColor);
584 // Step number
585 if (markerPos_.isEqualRows(curPos_))
586 backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker
587 if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(curPos_))
588 backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover
589 if (textChanged_) {
590 if (curPos_.step % hl2Cnt_) {
591 textPainter.setPen(!(curPos_.step % hl2Cnt_) ? palette_->ptnHl1StepNumColor
592 : !(curPos_.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor
593 : palette_->ptnDefStepNumColor);
594 }
595 else {
596 textPainter.setPen(palette_->ptnHl2StepNumColor);
597 }
598 textPainter.drawText(1, viewedCenterBaseY_, QString("%1").arg(curPos_.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper());
599 }
600 // Step data
601 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
602 x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, curPos_.order, curPos_.step, x, viewedCenterBaseY_, viewedCenterY_);
603 }
604 viewedCenterPos_ = curPos_;
605
606 int stepNum, odrNum;
607 int rowY, baseY;
608 int playOdrNum = bt_->getPlayingOrderNumber();
609 int playStepNum = bt_->getPlayingStepNumber();
610
611 /* Previous rows */
612 viewedFirstPos_ = curPos_;
613 for (rowY = viewedCenterY_ - stepFontHeight_, baseY = viewedCenterBaseY_ - stepFontHeight_,
614 stepNum = curPos_.step - 1, odrNum = curPos_.order;
615 rowY >= 0;
616 rowY -= stepFontHeight_, baseY -= stepFontHeight_, --stepNum) {
617 if (stepNum == -1) {
618 if (odrNum == 0) {
619 break;
620 }
621 else if (config_->getShowPreviousNextOrders()) {
622 --odrNum;
623 stepNum = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, odrNum)) - 1;
624 }
625 else {
626 break;
627 }
628 }
629
630 QColor rowColor;
631 if (!config_->getFollowMode() && odrNum == playOdrNum && stepNum == playStepNum) {
632 rowColor = palette_->ptnPlayStepColor;
633 }
634 else {
635 rowColor = !(stepNum % hl2Cnt_) ? palette_->ptnHl2StepColor
636 : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepColor
637 : palette_->ptnDefStepColor;
638 }
639
640 // Fill row
641 backPainter.fillRect(0, rowY, maxWidth, stepFontHeight_, rowColor);
642 // Step number
643 if (markerPos_.isEqualRows(odrNum, stepNum))
644 backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker
645 if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(odrNum, stepNum))
646 backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover
647 if (textChanged_) {
648 textPainter.setPen(!(stepNum % hl2Cnt_) ? palette_->ptnHl2StepNumColor
649 : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepNumColor
650 : palette_->ptnDefStepNumColor);
651 textPainter.drawText(1, baseY, QString("%1").arg(stepNum, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper());
652 }
653 // Step data
654 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
655 x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, odrNum, stepNum, x, baseY, rowY);
656 }
657 if (foreChanged_) {
658 if (odrNum != curPos_.order) // Mask
659 forePainter.fillRect(0, rowY, maxWidth, stepFontHeight_, palette_->ptnMaskColor);
660 }
661 viewedFirstPos_.setRows(odrNum, stepNum);
662 }
663
664 int stepEnd = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order));
665
666 /* Next rows */
667 viewedLastPos_ = curPos_;
668 for (rowY = viewedCenterY_ + stepFontHeight_, baseY = viewedCenterBaseY_ + stepFontHeight_,
669 stepNum = curPos_.step + 1, odrNum = curPos_.order;
670 rowY < viewedRowsHeight_;
671 rowY += stepFontHeight_, baseY += stepFontHeight_, ++stepNum) {
672 if (stepNum == stepEnd) {
673 if (odrNum == static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1) {
674 break;
675 }
676 else if (config_->getShowPreviousNextOrders()) {
677 ++odrNum;
678 stepNum = 0;
679 stepEnd = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, odrNum));
680 }
681 else {
682 break;
683 }
684 }
685
686 QColor rowColor;
687 if (!config_->getFollowMode() && odrNum == playOdrNum && stepNum == playStepNum) {
688 rowColor = palette_->ptnPlayStepColor;
689 }
690 else {
691 rowColor = !(stepNum % hl2Cnt_) ? palette_->ptnHl2StepColor
692 : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepColor
693 : palette_->ptnDefStepColor;
694 }
695
696 // Fill row
697 backPainter.fillRect(0, rowY, maxWidth, stepFontHeight_, rowColor);
698 // Step number
699 if (markerPos_.isEqualRows(odrNum, stepNum))
700 backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker
701 if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(odrNum, stepNum))
702 backPainter.fillRect(0, rowY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover
703 if (textChanged_) {
704 textPainter.setPen(!(stepNum % hl2Cnt_) ? palette_->ptnHl2StepNumColor
705 : !(stepNum % hl1Cnt_) ? palette_->ptnHl1StepNumColor
706 : palette_->ptnDefStepNumColor);
707 textPainter.drawText(1, baseY, QString("%1").arg(stepNum, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper());
708 }
709 // Step data
710 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
711 x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, odrNum, stepNum, x, baseY, rowY);
712 }
713 if (foreChanged_) {
714 if (odrNum != curPos_.order) // Mask
715 forePainter.fillRect(0, rowY, maxWidth, stepFontHeight_, palette_->ptnMaskColor);
716 }
717 viewedLastPos_.setRows(odrNum, stepNum);
718 }
719 }
720
quickDrawRows(int maxWidth)721 void PatternEditorPanel::quickDrawRows(int maxWidth)
722 {
723 int halfRowsCnt = viewedRowCnt_ >> 1;
724 bool repaintForeAll = (curPos_.step - stepDownCount_ < 0);
725 int shift = stepFontHeight_ * stepDownCount_;
726
727 /* Move up */
728 QRect srcRect(0, 0, maxWidth, viewedRowsHeight_);
729 if (!repaintForeAll) forePixmap_.scroll(0, -shift, srcRect);
730 textPixmap_.scroll(0, -shift, srcRect);
731 backPixmap_.scroll(0, -shift, srcRect);
732 {
733 PatternPosition fpos = calculatePositionFrom(viewedCenterPos_.order, viewedCenterPos_.step, stepDownCount_ - halfRowsCnt);
734 if (fpos.order != -1) viewedFirstPos_ = std::move(fpos);
735 }
736
737 QPainter forePainter(&forePixmap_);
738 QPainter textPainter(&textPixmap_);
739 QPainter backPainter(&backPixmap_);
740 textPainter.setFont(stepFont_);
741
742 /* Clear previous cursor row, current cursor row and last rows text and foreground */
743 int prevY = viewedCenterY_ - shift;
744 int lastY = viewedRowsHeight_ - shift;
745 textPainter.setCompositionMode(QPainter::CompositionMode_Source);
746 textPainter.fillRect(0, prevY, maxWidth, stepFontHeight_, Qt::transparent);
747 textPainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_, Qt::transparent);
748 textPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent);
749 textPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
750 if (!repaintForeAll) {
751 forePainter.setCompositionMode(QPainter::CompositionMode_Source);
752 forePainter.fillRect(0, prevY, maxWidth, stepFontHeight_, Qt::transparent);
753 forePainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_, Qt::transparent);
754 forePainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent);
755 forePainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
756 }
757
758 /* Redraw previous cursor step */
759 {
760 int baseY = viewedCenterBaseY_ - shift;
761 QColor rowColor = !(viewedCenterPos_.step % hl2Cnt_) ? palette_->ptnHl2StepColor
762 : !(viewedCenterPos_.step % hl1Cnt_) ? palette_->ptnHl1StepColor
763 : palette_->ptnDefStepColor;
764 // Fill row
765 backPainter.fillRect(0, prevY, maxWidth, stepFontHeight_, rowColor);
766 // Step number
767 if (markerPos_.isEqualRows(viewedCenterPos_))
768 backPainter.fillRect(0, prevY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker
769 if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(viewedCenterPos_))
770 backPainter.fillRect(0, prevY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover
771 textPainter.setPen(!(viewedCenterPos_.step % hl2Cnt_) ? palette_->ptnHl2StepNumColor
772 : !(viewedCenterPos_.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor
773 : palette_->ptnDefStepNumColor);
774 textPainter.drawText(1, baseY, QString("%1").arg(viewedCenterPos_.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper());
775 // Step data
776 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
777 x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, viewedCenterPos_.order, viewedCenterPos_.step, x, baseY, prevY);
778 }
779 }
780
781 /* Redraw current cursor step */
782 // Fill row
783 backPainter.fillRect(0, viewedCenterY_, maxWidth, stepFontHeight_,
784 bt_->isJamMode() ? palette_->ptnCurStepColor : palette_->ptnCurEditStepColor);
785 // Step number
786 if (markerPos_.isEqualRows(curPos_))
787 backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker
788 if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(curPos_))
789 backPainter.fillRect(0, viewedCenterY_, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover
790 if (curPos_.step % hl2Cnt_) {
791 textPainter.setPen(!(curPos_.step % hl2Cnt_) ? palette_->ptnHl1StepNumColor
792 : !(curPos_.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor
793 : palette_->ptnDefStepNumColor);
794 }
795 else {
796 textPainter.setPen(palette_->ptnHl2StepNumColor);
797 }
798 textPainter.drawText(1, viewedCenterBaseY_, QString("%1").arg(curPos_.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper());
799 // Step data
800 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
801 x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, curPos_.order, curPos_.step, x, viewedCenterBaseY_, viewedCenterY_);
802 }
803 viewedCenterPos_ = curPos_;
804
805 /* Draw new step at last if necessary */
806 {
807 PatternPosition bpos = calculatePositionFrom(viewedCenterPos_.order, viewedCenterPos_.step, halfRowsCnt);
808 if (!config_->getShowPreviousNextOrders() && viewedCenterPos_.order != bpos.order) {
809 // Clear row
810 backPainter.setCompositionMode(QPainter::CompositionMode_Source);
811 backPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent);
812 }
813 else {
814 int baseY = lastY + (viewedCenterBaseY_ - viewedCenterY_);
815 bpos = std::exchange(viewedLastPos_, bpos);
816 while (true) {
817 if (bpos.compareRows(viewedLastPos_) == 0) break;
818 PatternPosition tmpBpos = calculatePositionFrom(bpos.order, bpos.step, 1);
819 if (tmpBpos.order == -1) { // when viewedLastPos_.row == -1 (viewedlastPos_.row < viewedCenterPos_.row + halRowsCnt)
820 viewedLastPos_ = bpos;
821 // Clear row
822 backPainter.setCompositionMode(QPainter::CompositionMode_Source);
823 backPainter.fillRect(0, lastY, maxWidth, shift, Qt::transparent);
824 break;
825 }
826 else {
827 bpos = tmpBpos;
828 }
829
830 QColor rowColor = !(bpos.step % hl2Cnt_) ? palette_->ptnHl2StepColor
831 : !(bpos.step % hl1Cnt_) ? palette_->ptnHl1StepColor
832 : palette_->ptnDefStepColor;
833 // Fill row
834 backPainter.fillRect(0, lastY, maxWidth, stepFontHeight_, rowColor);
835 // Step number
836 if (markerPos_.isEqualRows(bpos))
837 backPainter.fillRect(0, lastY, stepNumWidth_, stepFontHeight_, palette_->ptnMarkerColor); // Paint marker
838 if (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(bpos))
839 backPainter.fillRect(0, lastY, stepNumWidth_, stepFontHeight_, palette_->ptnHovCellColor); // Paint hover
840 textPainter.setPen(!(bpos.step % hl2Cnt_) ? palette_->ptnHl2StepNumColor
841 : !(bpos.step % hl1Cnt_) ? palette_->ptnHl1StepNumColor
842 : palette_->ptnDefStepNumColor);
843 textPainter.drawText(1, baseY, QString("%1").arg(bpos.step, stepNumWidthCnt_, stepNumBase_, QChar('0')).toUpper());
844 // Step data
845 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
846 x += drawStep(forePainter, textPainter, backPainter, trackVisIdx, bpos.order, bpos.step, x, baseY, lastY);
847 }
848 if (bpos.order != curPos_.order) // Mask
849 forePainter.fillRect(0, lastY, maxWidth, stepFontHeight_, palette_->ptnMaskColor);
850
851 baseY += stepFontHeight_;
852 lastY += stepFontHeight_;
853 }
854 }
855 }
856
857 /* Redraw foreground all area if new order */
858 if (repaintForeAll) {
859 forePixmap_.fill(Qt::transparent);
860 int y = viewedCenterY_ - viewedCenterPos_.step * stepFontHeight_;
861 if (y > 0) forePainter.fillRect(0, 0, maxWidth, y, palette_->ptnMaskColor);
862 if (viewedLastPos_.order != viewedCenterPos_.order) {
863 y += static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, viewedCenterPos_.order)) * stepFontHeight_;
864 forePainter.fillRect(0, y, maxWidth, viewedRowsHeight_ - y, palette_->ptnMaskColor);
865 }
866 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
867 int w = baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast<size_t>(trackVisIdx));
868 if (foreChanged_ && bt_->isMute(visTracks_.at(trackVisIdx))) // Paint mute mask
869 forePainter.fillRect(x, 0, w, viewedRowsHeight_, palette_->ptnMaskColor);
870 x += w;
871 }
872 }
873 }
874
drawStep(QPainter & forePainter,QPainter & textPainter,QPainter & backPainter,int trackVisIdx,int orderNum,int stepNum,int x,int baseY,int rowY)875 int PatternEditorPanel::drawStep(QPainter &forePainter, QPainter &textPainter, QPainter& backPainter, int trackVisIdx, int orderNum, int stepNum, int x, int baseY, int rowY)
876 {
877 int trackNum = visTracks_.at(trackVisIdx);
878 int offset = x + widthSpace_;
879 PatternPosition pos{ trackVisIdx, 0, orderNum, stepNum };
880 QColor textColor = curPos_.isEqualRows(orderNum, stepNum) ? palette_->ptnCurTextColor : palette_->ptnDefTextColor;
881 bool isHovTrack = (hovPos_.order == -2 && hovPos_.trackVisIdx == trackVisIdx);
882 bool isHovStep = (hovPos_.trackVisIdx == -2 && hovPos_.isEqualRows(orderNum, stepNum));
883 SoundSource src = songStyle_.trackAttribs[static_cast<size_t>(trackNum)].source;
884
885
886 /* Tone name */
887 if (pos == curPos_) // Paint current cell
888 backPainter.fillRect(offset - widthSpace_, rowY, toneNameWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnCurCellColor);
889 if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover
890 backPainter.fillRect(offset - widthSpace_, rowY, toneNameWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnHovCellColor);
891 if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0)
892 && isSelectedCell(trackVisIdx, 0, orderNum, stepNum)) // Paint selected
893 backPainter.fillRect(offset - widthSpace_, rowY, toneNameWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnSelCellColor);
894 if (textChanged_) {
895 int noteNum = bt_->getStepNoteNumber(curSongNum_, trackNum, orderNum, stepNum);
896 switch (noteNum) {
897 case -1: // None
898 textPainter.setPen(textColor);
899 textPainter.drawText(offset, baseY, "---");
900 break;
901 case -2: // Key off
902 textPainter.fillRect(offset, rowY + stepFontHeight_ * 2 / 5,
903 toneNameWidth_, stepFontHeight_ / 5, palette_->ptnNoteColor);
904 break;
905 case -3: // Echo 0
906 textPainter.setPen(palette_->ptnNoteColor);
907 textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^0");
908 break;
909 case -4: // Echo 1
910 textPainter.setPen(palette_->ptnNoteColor);
911 textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^1");
912 break;
913 case -5: // Echo 2
914 textPainter.setPen(palette_->ptnNoteColor);
915 textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^2");
916 break;
917 case -6: // Echo 3
918 textPainter.setPen(palette_->ptnNoteColor);
919 textPainter.drawText(offset + stepFontWidth_ / 2, baseY, "^3");
920 break;
921 default: // Convert tone name
922 {
923 QString toneStr;
924 switch (noteNum % 12) {
925 case 0: toneStr = "C-"; break;
926 case 1: toneStr = "C#"; break;
927 case 2: toneStr = "D-"; break;
928 case 3: toneStr = "D#"; break;
929 case 4: toneStr = "E-"; break;
930 case 5: toneStr = "F-"; break;
931 case 6: toneStr = "F#"; break;
932 case 7: toneStr = "G-"; break;
933 case 8: toneStr = "G#"; break;
934 case 9: toneStr = "A-"; break;
935 case 10: toneStr = "A#"; break;
936 case 11: toneStr = "B-"; break;
937 }
938 textPainter.setPen(palette_->ptnNoteColor);
939 textPainter.drawText(offset, baseY, toneStr + QString::number(noteNum / 12));
940 break;
941 }
942 }
943 }
944 offset += toneNameWidth_ + widthSpaceDbl_;
945 pos.colInTrack = 1;
946
947 /* Instrument */
948 if (pos == curPos_) // Paint current cell
949 backPainter.fillRect(offset - widthSpace_, rowY, instWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnCurCellColor);
950 if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover
951 backPainter.fillRect(offset - widthSpace_, rowY, instWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnHovCellColor);
952 if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0)
953 && isSelectedCell(trackVisIdx, 1, orderNum, stepNum)) // Paint selected
954 backPainter.fillRect(offset - widthSpace_, rowY, instWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnSelCellColor);
955 if (textChanged_) {
956 int instNum = bt_->getStepInstrument(curSongNum_, trackNum, orderNum, stepNum);
957 if (instNum == -1) {
958 textPainter.setPen(textColor);
959 textPainter.drawText(offset, baseY, "--");
960 }
961 else {
962 std::unique_ptr<AbstractInstrument> inst = bt_->getInstrument(instNum);
963 textPainter.setPen((inst != nullptr && src == inst->getSoundSource())
964 ? palette_->ptnInstColor
965 : palette_->ptnErrorColor);
966 textPainter.drawText(offset, baseY, QString("%1").arg(instNum, 2, 16, QChar('0')).toUpper());
967 }
968 }
969 offset += instWidth_ + widthSpaceDbl_;
970 pos.colInTrack = 2;
971
972 /* Volume */
973 if (pos == curPos_) // Paint current cell
974 backPainter.fillRect(offset - widthSpace_, rowY, volWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnCurCellColor);
975 if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover
976 backPainter.fillRect(offset - widthSpace_, rowY, volWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnHovCellColor);
977 if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0)
978 && isSelectedCell(trackVisIdx, 2, orderNum, stepNum)) // Paint selected
979 backPainter.fillRect(offset - widthSpace_, rowY, volWidth_ + widthSpaceDbl_, stepFontHeight_, palette_->ptnSelCellColor);
980 if (textChanged_) {
981 int vol = bt_->getStepVolume(curSongNum_, trackNum, orderNum, stepNum);
982 if (vol == -1) {
983 textPainter.setPen(textColor);
984 textPainter.drawText(offset, baseY, "--");
985 }
986 else {
987 int volLim = 0; // Dummy set
988 switch (src) {
989 case SoundSource::FM: volLim = 0x80; break;
990 case SoundSource::SSG: volLim = 0x10; break;
991 case SoundSource::RHYTHM: volLim = 0x20; break;
992 case SoundSource::ADPCM: volLim = 0x100; break;
993 }
994 textPainter.setPen((vol < volLim) ? palette_->ptnVolColor : palette_->ptnErrorColor);
995 if (src == SoundSource::FM && vol < volLim && config_->getReverseFMVolumeOrder()) {
996 vol = volLim - vol - 1;
997 }
998 textPainter.drawText(offset, baseY, QString("%1").arg(vol, 2, 16, QChar('0')).toUpper());
999 }
1000 }
1001 offset += volWidth_ + widthSpaceDbl_;
1002 pos.colInTrack = 3;
1003
1004 /* Effect */
1005 for (int i = 0; i <= rightEffn_.at(static_cast<size_t>(trackVisIdx)); ++i) {
1006 /* Effect ID */
1007 if (pos == curPos_) // Paint current cell
1008 backPainter.fillRect(offset - widthSpace_, rowY, effIDWidth_ + widthSpace_, stepFontHeight_, palette_->ptnCurCellColor);
1009 if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover
1010 backPainter.fillRect(offset - widthSpace_, rowY, effIDWidth_ + widthSpace_, stepFontHeight_, palette_->ptnHovCellColor);
1011 if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0)
1012 && isSelectedCell(trackVisIdx, pos.colInTrack, orderNum, stepNum)) // Paint selected
1013 backPainter.fillRect(offset - widthSpace_, rowY, effIDWidth_ + widthSpace_, stepFontHeight_, palette_->ptnSelCellColor);
1014 std::string effId;
1015 QString effStr;
1016 if (textChanged_) {
1017 effId = bt_->getStepEffectID(curSongNum_, trackNum, orderNum, stepNum, i);
1018 effStr = QString::fromStdString(effId);
1019 if (effStr == "--") {
1020 textPainter.setPen(textColor);
1021 textPainter.drawText(offset, baseY, effStr);
1022 }
1023 else {
1024 textPainter.setPen(palette_->ptnEffColor);
1025 textPainter.drawText(offset, baseY, effStr);
1026 }
1027 }
1028 offset += effIDWidth_;
1029 ++pos.colInTrack;
1030
1031 /* Effect Value */
1032 if (pos == curPos_) // Paint current cell
1033 backPainter.fillRect(offset, rowY, effValWidth_ + widthSpace_, stepFontHeight_, palette_->ptnCurCellColor);
1034 if (pos == hovPos_ || isHovTrack || isHovStep) // Paint hover
1035 backPainter.fillRect(offset, rowY, effValWidth_ + widthSpace_, stepFontHeight_, palette_->ptnHovCellColor);
1036 if ((selLeftAbovePos_.trackVisIdx >= 0 && selLeftAbovePos_.order >= 0)
1037 && isSelectedCell(trackVisIdx, pos.colInTrack, orderNum, stepNum)) // Paint selected
1038 backPainter.fillRect(offset, rowY, effValWidth_ + widthSpace_, stepFontHeight_, palette_->ptnSelCellColor);
1039 if (textChanged_) {
1040 int effVal = bt_->getStepEffectValue(curSongNum_, trackNum, orderNum, stepNum, i);
1041 if (effVal == -1) {
1042 textPainter.setPen(textColor);
1043 textPainter.drawText(offset, baseY, "--");
1044 }
1045 else {
1046 textPainter.setPen(palette_->ptnEffColor);
1047 switch (Effect::toEffectType(src, effId)) {
1048 case EffectType::VolumeDelay:
1049 if (src == SoundSource::FM && config_->getReverseFMVolumeOrder() && effVal < 0x80)
1050 effVal = 0x7f - effVal;
1051 break;
1052 case EffectType::Brightness:
1053 if (config_->getReverseFMVolumeOrder() && effVal > 0)
1054 effVal = 0xff - effVal + 1;
1055 break;
1056 default:
1057 break;
1058 }
1059 textPainter.drawText(offset, baseY, QString("%1").arg(effVal, 2, 16, QChar('0')).toUpper());
1060 }
1061 }
1062 offset += effValWidth_ + widthSpaceDbl_;
1063 ++pos.colInTrack;
1064 }
1065
1066 if (foreChanged_ && bt_->isMute(trackNum)) // Paint mute mask
1067 forePainter.fillRect(x, rowY, offset - x - 1, stepFontHeight_, palette_->ptnMaskColor);
1068
1069 return baseTrackWidth_ + effWidth_ * rightEffn_[static_cast<size_t>(trackVisIdx)];
1070 }
1071
drawHeaders(int maxWidth)1072 void PatternEditorPanel::drawHeaders(int maxWidth)
1073 {
1074 QPainter painter(&headerPixmap_);
1075 painter.setFont(headerFont_);
1076
1077 painter.fillRect(0, 0, geometry().width(), headerHeight_, palette_->ptnHeaderRowColor);
1078 painter.setPen(palette_->ptnHeaderBorderColor);
1079 int bottomLineY = headerHeight_ - 1;
1080 painter.drawLine(0, bottomLineY, geometry().width(), bottomLineY);
1081 int lspace = stepFontWidth_ / 2;
1082 for (int x = stepNumWidth_, trackVisIdx = leftTrackVisIdx_; x < maxWidth; ++trackVisIdx) {
1083 painter.setPen(palette_->ptnHeaderBorderColor);
1084 painter.drawLine(x, 0, x, headerHeight_);
1085 int trackNum = visTracks_.at(trackVisIdx);
1086 int tw = baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast<size_t>(trackVisIdx));
1087 if (hovPos_.order == -2 && hovPos_.trackVisIdx == trackVisIdx)
1088 painter.fillRect(x, 0, tw, headerHeight_, palette_->ptnHovCellColor);
1089 int left = x + lspace;
1090 painter.setPen(palette_->ptnHeaderTextColor);
1091 const TrackAttribute& attrib = songStyle_.trackAttribs[static_cast<size_t>(trackNum)];
1092 painter.drawText(left, headerFontAscent_,
1093 getTrackName(songStyle_.type, attrib.source, attrib.channelInSource));
1094
1095 painter.fillRect(left, headerHeight_ - 4, hdMuteToggleWidth_, 2,
1096 bt_->isMute(trackNum) ? palette_->ptnMuteColor : palette_->ptnUnmuteColor);
1097
1098 painter.drawText(left + hdMuteToggleWidth_ + lspace, hdPlusY_, "+");
1099 painter.drawText(left + hdMuteToggleWidth_ + lspace, hdMinusY_, "-");
1100
1101 x += tw;
1102 }
1103 }
1104
drawBorders(int maxWidth)1105 void PatternEditorPanel::drawBorders(int maxWidth)
1106 {
1107 QPainter painter(&backPixmap_);
1108 painter.setPen(palette_->ptnBorderColor);
1109 painter.drawLine(stepNumWidth_, 0, stepNumWidth_, backPixmap_.height());
1110 size_t trackVisIdx = static_cast<size_t>(leftTrackVisIdx_);
1111 for (int x = stepNumWidth_; trackVisIdx < rightEffn_.size(); ) {
1112 x += (baseTrackWidth_ + effWidth_ * rightEffn_.at(trackVisIdx));
1113 if (x > maxWidth) break;
1114 painter.drawLine(x, 0, x, backPixmap_.height());
1115 ++trackVisIdx;
1116 }
1117 }
1118
drawShadow()1119 void PatternEditorPanel::drawShadow()
1120 {
1121 QPainter painter(&completePixmap_);
1122 painter.fillRect(0, 0, geometry().width(), geometry().height(), palette_->ptnUnfocusedShadowColor);
1123 }
1124
1125 // NOTE: end >= -1
calculateTracksWidthWithRowNum(int beginIdx,int endIdx) const1126 int PatternEditorPanel::calculateTracksWidthWithRowNum(int beginIdx, int endIdx) const
1127 {
1128 int width = stepNumWidth_;
1129 for (int i = beginIdx; i <= endIdx; ++i) {
1130 width += (baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast<size_t>(i)));
1131 }
1132 return width;
1133 }
1134
calculateColNumInRow(int trackVisIdx,int colNumInTrack,bool isExpanded) const1135 int PatternEditorPanel::calculateColNumInRow(int trackVisIdx, int colNumInTrack, bool isExpanded) const
1136 {
1137 if (isExpanded) {
1138 return trackVisIdx * 11 + colNumInTrack;
1139 }
1140 else {
1141 trackVisIdx = std::min(trackVisIdx, static_cast<int>(rightEffn_.size()));
1142 return std::accumulate(rightEffn_.begin(), rightEffn_.begin() + trackVisIdx, colNumInTrack,
1143 [](int acc, int v) { return acc + 5 + 2 * v; });
1144 }
1145 }
1146
moveCursorToRight(int n)1147 void PatternEditorPanel::moveCursorToRight(int n)
1148 {
1149 int oldTrackIdx = curPos_.trackVisIdx;
1150 bool oldLeftTrackIdx = leftTrackVisIdx_;
1151
1152 curPos_.colInTrack += n;
1153 if (n > 0) {
1154 while (true) {
1155 int lim = 5 + 2 * rightEffn_.at(static_cast<size_t>(curPos_.trackVisIdx));
1156 if (curPos_.colInTrack < lim) {
1157 break;
1158 }
1159 else {
1160 if (curPos_.trackVisIdx == static_cast<int>(visTracks_.size()) - 1) {
1161 if (config_->getWarpCursor()) {
1162 curPos_.trackVisIdx = 0;
1163 }
1164 else {
1165 curPos_.colInTrack = lim - 1;
1166 break;
1167 }
1168 }
1169 else {
1170 ++curPos_.trackVisIdx;
1171 }
1172 curPos_.colInTrack -= lim;
1173 }
1174 }
1175 }
1176 else {
1177 while (true) {
1178 if (curPos_.colInTrack >= 0) {
1179 break;
1180 }
1181 else {
1182 if (!curPos_.trackVisIdx) {
1183 if (config_->getWarpCursor()) {
1184 curPos_.trackVisIdx = static_cast<int>(visTracks_.size()) - 1;
1185 }
1186 else {
1187 curPos_.colInTrack = 0;
1188 break;
1189 }
1190 }
1191 else {
1192 --curPos_.trackVisIdx;
1193 }
1194 curPos_.colInTrack += (5 + 2 * rightEffn_.at(static_cast<size_t>(curPos_.trackVisIdx)));
1195 }
1196 }
1197 }
1198 if (oldTrackIdx < curPos_.trackVisIdx) {
1199 while (calculateTracksWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx) > geometry().width())
1200 ++leftTrackVisIdx_;
1201 }
1202 else {
1203 if (curPos_.trackVisIdx < leftTrackVisIdx_) leftTrackVisIdx_ = curPos_.trackVisIdx;
1204 }
1205
1206 updateTracksWidthFromLeftToEnd();
1207 entryCnt_ = 0;
1208
1209 if (curPos_.trackVisIdx != oldTrackIdx)
1210 bt_->setCurrentTrack(visTracks_.at(curPos_.trackVisIdx));
1211
1212 if (!isIgnoreToSlider_) {
1213 if (config_->getMoveCursorByHorizontalScroll()) {
1214 emit hScrollBarChangeRequested(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack));
1215 }
1216 else if (curPos_.trackVisIdx != oldTrackIdx) {
1217 emit hScrollBarChangeRequested(leftTrackVisIdx_);
1218 }
1219 }
1220
1221 if (!isIgnoreToOrder_ && curPos_.trackVisIdx != oldTrackIdx) // Send to order list
1222 emit currentTrackChanged(curPos_.trackVisIdx);
1223
1224 // Request fore-background repaint if leftmost track is changed else request only background repaint
1225 if (leftTrackVisIdx_ != oldLeftTrackIdx) {
1226 headerChanged_ = true;
1227 foreChanged_ = true;
1228 textChanged_ = true;
1229 }
1230 backChanged_ = true;
1231 repaint();
1232 }
1233
moveViewToRight(int n)1234 void PatternEditorPanel::moveViewToRight(int n)
1235 {
1236 leftTrackVisIdx_ += n;
1237 updateTracksWidthFromLeftToEnd();
1238
1239 // Calculate cursor position
1240 int trackIdx = curPos_.trackVisIdx + n;
1241 int col = std::min(curPos_.colInTrack,
1242 4 + 2 * rightEffn_.at(static_cast<size_t>(trackIdx)));
1243
1244 // Check visible
1245 int width = stepNumWidth_;
1246 for (int i = leftTrackVisIdx_; i <= trackIdx; ++i) {
1247 width += (baseTrackWidth_ + effWidth_ * rightEffn_.at(static_cast<size_t>(i)));
1248 if (geometry().width() < width) {
1249 trackIdx = i - 1;
1250 col = 4 + 2 * rightEffn_.at(static_cast<size_t>(trackIdx));
1251 break;
1252 }
1253 }
1254
1255 // Move cursor and repaint all
1256 headerChanged_ = true;
1257 foreChanged_ = true;
1258 textChanged_ = true;
1259 moveCursorToRight(calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack, trackIdx, col));
1260 }
1261
moveCursorToDown(int n)1262 void PatternEditorPanel::moveCursorToDown(int n)
1263 {
1264 int oldOdr = curPos_.order;
1265 int tmp = curPos_.step + n;
1266
1267 if (n > 0) {
1268 while (true) {
1269 int dif = tmp - static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order));
1270 if (dif < 0) {
1271 curPos_.step = tmp;
1272 break;
1273 }
1274 else {
1275 if (config_->getWarpAcrossOrders()) {
1276 if (curPos_.order == static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1) {
1277 curPos_.order = 0;
1278 }
1279 else {
1280 ++curPos_.order;
1281 }
1282 }
1283 tmp = dif;
1284 }
1285 }
1286 }
1287 else {
1288 while (true) {
1289 if (tmp < 0) {
1290 if (config_->getWarpAcrossOrders()) {
1291 if (curPos_.order == 0) {
1292 curPos_.order = static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1;
1293 }
1294 else {
1295 --curPos_.order;
1296 }
1297 }
1298 tmp += bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order);
1299 }
1300 else {
1301 curPos_.step = tmp;
1302 break;
1303 }
1304 }
1305 }
1306
1307 if (curPos_.order != oldOdr)
1308 bt_->setCurrentOrderNumber(curPos_.order);
1309 bt_->setCurrentStepNumber(curPos_.step);
1310
1311 entryCnt_ = 0;
1312
1313 if (!isIgnoreToSlider_)
1314 emit vScrollBarChangeRequested(
1315 curPos_.step, static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1);
1316
1317 if (!isIgnoreToOrder_ && curPos_.order != oldOdr) // Send to order list
1318 emit currentOrderChanged(
1319 curPos_.order, static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1);
1320
1321 backChanged_ = true;
1322 textChanged_ = true;
1323 foreChanged_ = true;
1324 repaint();
1325 }
1326
calculateColumnDistance(int beginTrackIdx,int beginColumn,int endTrackIdx,int endColumn,bool isExpanded) const1327 int PatternEditorPanel::calculateColumnDistance(int beginTrackIdx, int beginColumn, int endTrackIdx, int endColumn, bool isExpanded) const
1328 {
1329 return (calculateColNumInRow(endTrackIdx, endColumn, isExpanded)
1330 - calculateColNumInRow(beginTrackIdx, beginColumn, isExpanded));
1331 }
1332
calculateStepDistance(int beginOrder,int beginStep,int endOrder,int endStep) const1333 int PatternEditorPanel::calculateStepDistance(int beginOrder, int beginStep, int endOrder, int endStep) const
1334 {
1335 int d = 0;
1336 int startOrder, startStep, stopOrder, stopStep;
1337 bool flag;
1338
1339 if (endOrder >= beginOrder) {
1340 startOrder = endOrder;
1341 startStep = endStep;
1342 stopOrder = beginOrder;
1343 stopStep = beginStep;
1344 flag = true;
1345 }
1346 else {
1347 startOrder = beginOrder;
1348 startStep = beginStep;
1349 stopOrder = endOrder;
1350 stopStep = endStep;
1351 flag = false;
1352 }
1353
1354 while (true) {
1355 if (startOrder == stopOrder) {
1356 d += (startStep - stopStep);
1357 break;
1358 }
1359 else {
1360 d += startStep;
1361 startStep = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, --startOrder));
1362 }
1363 }
1364
1365 return flag ? d : -d;
1366 }
1367
calculatePositionFrom(int order,int step,int by) const1368 PatternPosition PatternEditorPanel::calculatePositionFrom(int order, int step, int by) const
1369 {
1370 PatternPosition pos{ -1, -1, order, step + by };
1371
1372 if (by > 0) {
1373 while (true) {
1374 int dif = pos.step - static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, pos.order));
1375 if (dif < 0) {
1376 break;
1377 }
1378 else {
1379 if (pos.order == static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1) {
1380 return { -1, -1, -1, -1 };
1381 }
1382 else {
1383 ++pos.order;
1384 }
1385 pos.step = dif;
1386 }
1387 }
1388 }
1389 else {
1390 while (true) {
1391 if (pos.step < 0) {
1392 if (pos.order == 0) {
1393 return { -1, -1, -1, -1 };
1394 }
1395 else {
1396 --pos.order;
1397 }
1398 pos.step += bt_->getPatternSizeFromOrderNumber(curSongNum_, pos.order);
1399 }
1400 else {
1401 break;
1402 }
1403 }
1404 }
1405 return pos;
1406 }
1407
calculateCurrentCursorPosition() const1408 QPoint PatternEditorPanel::calculateCurrentCursorPosition() const
1409 {
1410 int w = calculateTracksWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx - 1);
1411 if (curPos_.colInTrack > 0) {
1412 w = w + toneNameWidth_ + widthSpaceDbl_;
1413 if (curPos_.colInTrack > 1) {
1414 w = w + instWidth_ + widthSpaceDbl_;
1415 if (curPos_.colInTrack > 2) {
1416 w = w + volWidth_ + widthSpaceDbl_;
1417 for (int i = 3; i < 11; ++i) {
1418 if (curPos_.colInTrack == i) break;
1419 w = w + widthSpace_ + ((i % 2) ? effIDWidth_ : effValWidth_);
1420 }
1421 }
1422 }
1423 }
1424 return QPoint(w, curRowY_);
1425 }
1426
getScrollableCountByTrack() const1427 int PatternEditorPanel::getScrollableCountByTrack() const
1428 {
1429 int width = stepNumWidth_;
1430 size_t i = visTracks_.size();
1431 do {
1432 --i;
1433 width += (baseTrackWidth_ + effWidth_ * rightEffn_.at(i));
1434 if (geometry().width() < width) {
1435 return static_cast<int>(i + 1);
1436 }
1437 } while (i);
1438 return 0;
1439 }
1440
changeEditable()1441 void PatternEditorPanel::changeEditable()
1442 {
1443 backChanged_ = true;
1444 repaint();
1445 }
1446
getFullColmunSize() const1447 int PatternEditorPanel::getFullColmunSize() const
1448 {
1449 return calculateColNumInRow(static_cast<int>(visTracks_.size()) - 1, 4 + 2 * rightEffn_.back());
1450 }
1451
updatePositionByStepUpdate(bool isFirstUpdate,bool forceJump,bool trackChanged)1452 void PatternEditorPanel::updatePositionByStepUpdate(bool isFirstUpdate, bool forceJump, bool trackChanged)
1453 {
1454 if (!forceJump && !config_->getFollowMode()) { // Repaint only background
1455 backChanged_ = true;
1456 repaint();
1457 return;
1458 }
1459
1460 if (trackChanged) {
1461 int trackVisIdx = std::distance(visTracks_.begin(), std::find(visTracks_.begin(), visTracks_.end(), bt_->getCurrentTrackAttribute().number));
1462 int oldTrackVisIdx = std::exchange(curPos_.trackVisIdx, trackVisIdx);
1463 curPos_.colInTrack = 0;
1464 if (oldTrackVisIdx < curPos_.trackVisIdx) {
1465 while (calculateTracksWidthWithRowNum(leftTrackVisIdx_, curPos_.trackVisIdx) > geometry().width()) {
1466 ++leftTrackVisIdx_;
1467 headerChanged_ = true;
1468 }
1469 }
1470 else {
1471 if (curPos_.trackVisIdx < leftTrackVisIdx_) {
1472 leftTrackVisIdx_ = curPos_.trackVisIdx;
1473 headerChanged_ = true;
1474 }
1475 }
1476
1477 updateTracksWidthFromLeftToEnd();
1478
1479 if (!isIgnoreToSlider_) {
1480 if (config_->getMoveCursorByHorizontalScroll())
1481 emit hScrollBarChangeRequested(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack));
1482 else
1483 emit hScrollBarChangeRequested(leftTrackVisIdx_);
1484 }
1485 }
1486
1487 PatternPosition tmp = curPos_;
1488 curPos_.setRows(bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber());
1489 int cmp = curPos_.compareRows(tmp);
1490 if (cmp || isFirstUpdate) {
1491 emit vScrollBarChangeRequested(
1492 curPos_.step, static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1);
1493
1494 if (isFirstUpdate || (cmp < 0) || (cmp && !config_->getShowPreviousNextOrders())) {
1495 stepDownCount_ = 0; // Redraw entire area in first update
1496 }
1497 else {
1498 int d = calculateStepDistance(tmp.order, tmp.step, curPos_.order, curPos_.step);
1499 stepDownCount_ = (d < (viewedRowCnt_ >> 1)) ? d : 0;
1500 }
1501 }
1502 else if (!trackChanged) return; // Delayed call, already updated.
1503
1504 entryCnt_ = 0;
1505
1506 // If stepChanged is false, repaint all pattern
1507 foreChanged_ = true;
1508 textChanged_ = true;
1509 backChanged_ = true;
1510 repaint();
1511 }
1512
changeMarker()1513 void PatternEditorPanel::changeMarker()
1514 {
1515 markerPos_.setRows(bt_->getMarkerOrder(), bt_->getMarkerStep());
1516 backChanged_ = true;
1517 repaint();
1518 }
1519
enterToneData(QKeyEvent * event)1520 bool PatternEditorPanel::enterToneData(QKeyEvent* event)
1521 {
1522 int baseOct = bt_->getCurrentOctave();
1523
1524 if (event->modifiers().testFlag(Qt::NoModifier)) {
1525 Qt::Key qtKey = static_cast<Qt::Key>(event->key());
1526 try {
1527 JamKey possibleJamKey = getJamKeyFromLayoutMapping(qtKey, config_);
1528 int octaveOffset = 0;
1529 switch (possibleJamKey) {
1530 case JamKey::HighD2:
1531 case JamKey::HighCS2:
1532 case JamKey::HighC2:
1533 octaveOffset = 2;
1534 break;
1535 case JamKey::HighB:
1536 case JamKey::HighAS:
1537 case JamKey::HighA:
1538 case JamKey::HighGS:
1539 case JamKey::HighG:
1540 case JamKey::HighFS:
1541 case JamKey::HighF:
1542 case JamKey::HighE:
1543 case JamKey::HighDS:
1544 case JamKey::HighD:
1545 case JamKey::HighCS:
1546 case JamKey::HighC:
1547 case JamKey::LowD2:
1548 case JamKey::LowCS2:
1549 case JamKey::LowC2:
1550 octaveOffset = 1;
1551 break;
1552 default:
1553 break;
1554 }
1555 setStepKeyOn(JamManager::jamKeyToNote(possibleJamKey), baseOct + octaveOffset);
1556 } catch (std::invalid_argument &) {}
1557 }
1558
1559 return false;
1560 }
1561
setStepKeyOn(Note note,int octave)1562 void PatternEditorPanel::setStepKeyOn(Note note, int octave)
1563 {
1564 if (octave < 8) {
1565 bt_->setStepNote(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, octave, note,
1566 config_->getInstrumentMask(), config_->getVolumeMask());
1567 comStack_.lock()->push(new SetKeyOnToStepQtCommand(this));
1568 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_);
1569 }
1570 }
1571
enterInstrumentData(int key)1572 bool PatternEditorPanel::enterInstrumentData(int key)
1573 {
1574 switch (key) {
1575 case Qt::Key_0: setStepInstrument(0x0); return true;
1576 case Qt::Key_1: setStepInstrument(0x1); return true;
1577 case Qt::Key_2: setStepInstrument(0x2); return true;
1578 case Qt::Key_3: setStepInstrument(0x3); return true;
1579 case Qt::Key_4: setStepInstrument(0x4); return true;
1580 case Qt::Key_5: setStepInstrument(0x5); return true;
1581 case Qt::Key_6: setStepInstrument(0x6); return true;
1582 case Qt::Key_7: setStepInstrument(0x7); return true;
1583 case Qt::Key_8: setStepInstrument(0x8); return true;
1584 case Qt::Key_9: setStepInstrument(0x9); return true;
1585 case Qt::Key_A: setStepInstrument(0xa); return true;
1586 case Qt::Key_B: setStepInstrument(0xb); return true;
1587 case Qt::Key_C: setStepInstrument(0xc); return true;
1588 case Qt::Key_D: setStepInstrument(0xd); return true;
1589 case Qt::Key_E: setStepInstrument(0xe); return true;
1590 case Qt::Key_F: setStepInstrument(0xf); return true;
1591 default: return false;
1592 }
1593 }
1594
setStepInstrument(int num)1595 void PatternEditorPanel::setStepInstrument(int num)
1596 {
1597 int trackNum = visTracks_.at(curPos_.trackVisIdx);
1598 bt_->setStepInstrumentDigit(curSongNum_, trackNum, curPos_.order, curPos_.step, num, (entryCnt_ == 1));
1599 comStack_.lock()->push(new SetInstrumentToStepQtCommand(this, curPos_, (entryCnt_ == 1)));
1600
1601 emit instrumentEntered(
1602 bt_->getStepInstrument(curSongNum_, trackNum, curPos_.order, curPos_.step));
1603
1604 if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount())
1605 moveCursorToDown(editableStepCnt_);
1606 }
1607
enterVolumeData(int key)1608 bool PatternEditorPanel::enterVolumeData(int key)
1609 {
1610 switch (key) {
1611 case Qt::Key_0: setStepVolume(0x0); return true;
1612 case Qt::Key_1: setStepVolume(0x1); return true;
1613 case Qt::Key_2: setStepVolume(0x2); return true;
1614 case Qt::Key_3: setStepVolume(0x3); return true;
1615 case Qt::Key_4: setStepVolume(0x4); return true;
1616 case Qt::Key_5: setStepVolume(0x5); return true;
1617 case Qt::Key_6: setStepVolume(0x6); return true;
1618 case Qt::Key_7: setStepVolume(0x7); return true;
1619 case Qt::Key_8: setStepVolume(0x8); return true;
1620 case Qt::Key_9: setStepVolume(0x9); return true;
1621 case Qt::Key_A: setStepVolume(0xa); return true;
1622 case Qt::Key_B: setStepVolume(0xb); return true;
1623 case Qt::Key_C: setStepVolume(0xc); return true;
1624 case Qt::Key_D: setStepVolume(0xd); return true;
1625 case Qt::Key_E: setStepVolume(0xe); return true;
1626 case Qt::Key_F: setStepVolume(0xf); return true;
1627 default: return false;
1628 }
1629 }
1630
setStepVolume(int volume)1631 void PatternEditorPanel::setStepVolume(int volume)
1632 {
1633 int vol = bt_->setStepVolumeDigit(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step, volume, (entryCnt_ == 1));
1634 comStack_.lock()->push(new SetVolumeToStepQtCommand(this, curPos_, (entryCnt_ == 1)));
1635
1636 if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount())
1637 moveCursorToDown(editableStepCnt_);
1638
1639 emit volumeEntered(vol);
1640 }
1641
enterEffectID(int key)1642 bool PatternEditorPanel::enterEffectID(int key)
1643 {
1644 switch (key) {
1645 case Qt::Key_0: setStepEffectID("0"); return true;
1646 case Qt::Key_1: setStepEffectID("1"); return true;
1647 case Qt::Key_2: setStepEffectID("2"); return true;
1648 case Qt::Key_3: setStepEffectID("3"); return true;
1649 case Qt::Key_4: setStepEffectID("4"); return true;
1650 case Qt::Key_5: setStepEffectID("5"); return true;
1651 case Qt::Key_6: setStepEffectID("6"); return true;
1652 case Qt::Key_7: setStepEffectID("7"); return true;
1653 case Qt::Key_8: setStepEffectID("8"); return true;
1654 case Qt::Key_9: setStepEffectID("9"); return true;
1655 case Qt::Key_A: setStepEffectID("A"); return true;
1656 case Qt::Key_B: setStepEffectID("B"); return true;
1657 case Qt::Key_C: setStepEffectID("C"); return true;
1658 case Qt::Key_D: setStepEffectID("D"); return true;
1659 case Qt::Key_E: setStepEffectID("E"); return true;
1660 case Qt::Key_F: setStepEffectID("F"); return true;
1661 case Qt::Key_G: setStepEffectID("G"); return true;
1662 case Qt::Key_H: setStepEffectID("H"); return true;
1663 case Qt::Key_I: setStepEffectID("I"); return true;
1664 case Qt::Key_J: setStepEffectID("J"); return true;
1665 case Qt::Key_K: setStepEffectID("K"); return true;
1666 case Qt::Key_L: setStepEffectID("L"); return true;
1667 case Qt::Key_M: setStepEffectID("M"); return true;
1668 case Qt::Key_N: setStepEffectID("N"); return true;
1669 case Qt::Key_O: setStepEffectID("O"); return true;
1670 case Qt::Key_P: setStepEffectID("P"); return true;
1671 case Qt::Key_Q: setStepEffectID("Q"); return true;
1672 case Qt::Key_R: setStepEffectID("R"); return true;
1673 case Qt::Key_S: setStepEffectID("S"); return true;
1674 case Qt::Key_T: setStepEffectID("T"); return true;
1675 case Qt::Key_U: setStepEffectID("U"); return true;
1676 case Qt::Key_V: setStepEffectID("V"); return true;
1677 case Qt::Key_W: setStepEffectID("W"); return true;
1678 case Qt::Key_X: setStepEffectID("X"); return true;
1679 case Qt::Key_Y: setStepEffectID("Y"); return true;
1680 case Qt::Key_Z: setStepEffectID("Z"); return true;
1681 default: return false;
1682 }
1683 }
1684
setStepEffectID(QString str)1685 void PatternEditorPanel::setStepEffectID(QString str)
1686 {
1687 int curTrackNum = visTracks_.at(curPos_.trackVisIdx);
1688 bt_->setStepEffectIDCharacter(curSongNum_, curTrackNum, curPos_.order, curPos_.step,
1689 (curPos_.colInTrack - 3) / 2, str.toStdString(),
1690 config_->getFill00ToEffectValue(), (entryCnt_ == 1));
1691 comStack_.lock()->push(new SetEffectIDToStepQtCommand(this, curPos_, (entryCnt_ == 1)));
1692
1693 PatternPosition editPos = curPos_;
1694
1695 if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount()) {
1696 if (config_->getMoveCursorToRight()) moveCursorToRight(1);
1697 else moveCursorToDown(editableStepCnt_);
1698 }
1699
1700 // Send effect description
1701 std::string id = bt_->getStepEffectID(curSongNum_, visTracks_.at(editPos.trackVisIdx), editPos.order,
1702 editPos.step, (editPos.colInTrack - 3) / 2);
1703 SoundSource src = songStyle_.trackAttribs.at(static_cast<size_t>(curTrackNum)).source;
1704 emit effectEntered(EffectDescription::getEffectFormatAndDetailString(Effect::toEffectType(src, id)));
1705 }
1706
enterEffectValue(int key)1707 bool PatternEditorPanel::enterEffectValue(int key)
1708 {
1709 switch (key) {
1710 case Qt::Key_0: setStepEffectValue(0x0); return true;
1711 case Qt::Key_1: setStepEffectValue(0x1); return true;
1712 case Qt::Key_2: setStepEffectValue(0x2); return true;
1713 case Qt::Key_3: setStepEffectValue(0x3); return true;
1714 case Qt::Key_4: setStepEffectValue(0x4); return true;
1715 case Qt::Key_5: setStepEffectValue(0x5); return true;
1716 case Qt::Key_6: setStepEffectValue(0x6); return true;
1717 case Qt::Key_7: setStepEffectValue(0x7); return true;
1718 case Qt::Key_8: setStepEffectValue(0x8); return true;
1719 case Qt::Key_9: setStepEffectValue(0x9); return true;
1720 case Qt::Key_A: setStepEffectValue(0xa); return true;
1721 case Qt::Key_B: setStepEffectValue(0xb); return true;
1722 case Qt::Key_C: setStepEffectValue(0xc); return true;
1723 case Qt::Key_D: setStepEffectValue(0xd); return true;
1724 case Qt::Key_E: setStepEffectValue(0xe); return true;
1725 case Qt::Key_F: setStepEffectValue(0xf); return true;
1726 default: return false;
1727 }
1728 }
1729
setStepEffectValue(int value)1730 void PatternEditorPanel::setStepEffectValue(int value)
1731 {
1732 int trackNum = visTracks_.at(curPos_.trackVisIdx);
1733 int n = (curPos_.colInTrack - 4) / 2;
1734 EffectDisplayControl ctrl = EffectDisplayControl::Unset;
1735 SoundSource src = songStyle_.trackAttribs[static_cast<size_t>(trackNum)].source;
1736 switch (Effect::toEffectType(
1737 src,
1738 bt_->getStepEffectID(curSongNum_, trackNum, curPos_.order, curPos_.step, n))) {
1739 case EffectType::VolumeDelay:
1740 if (src == SoundSource::FM && config_->getReverseFMVolumeOrder())
1741 ctrl = EffectDisplayControl::ReverseFMVolumeDelay;
1742 break;
1743 case EffectType::Brightness:
1744 if (config_->getReverseFMVolumeOrder()) ctrl = EffectDisplayControl::ReverseFMBrightness;
1745 break;
1746 default:
1747 break;
1748 }
1749 bt_->setStepEffectValueDigit(curSongNum_, trackNum, curPos_.order, curPos_.step, n, value, ctrl, (entryCnt_ == 1));
1750 comStack_.lock()->push(new SetEffectValueToStepQtCommand(this, curPos_, (entryCnt_ == 1)));
1751
1752 if ((!bt_->isPlaySong() || !bt_->isFollowPlay()) && !updateEntryCount()) {
1753 if (config_->getMoveCursorToRight()) moveCursorToRight(1);
1754 else moveCursorToDown(editableStepCnt_);
1755 }
1756 }
1757
insertStep()1758 void PatternEditorPanel::insertStep()
1759 {
1760 bt_->insertStep(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step);
1761 comStack_.lock()->push(new InsertStepQtCommand(this));
1762 }
1763
deletePreviousStep()1764 void PatternEditorPanel::deletePreviousStep()
1765 {
1766 if (curPos_.step) {
1767 bt_->deletePreviousStep(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step);
1768 comStack_.lock()->push(new DeletePreviousStepQtCommand(this));
1769 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(-1);
1770 }
1771 }
1772
copySelectedCells()1773 void PatternEditorPanel::copySelectedCells()
1774 {
1775 if (selLeftAbovePos_.order == -1) return;
1776
1777 int r = visTracks_.at(selRightBelowPos_.trackVisIdx) * 11 + selRightBelowPos_.colInTrack;
1778 int l = visTracks_.at(selLeftAbovePos_.trackVisIdx) * 11 + selLeftAbovePos_.colInTrack;
1779 int w = 1 + r - l; // Real selected region width
1780 int h = 1 + calculateStepDistance(selLeftAbovePos_.order, selLeftAbovePos_.step,
1781 selRightBelowPos_.order, selRightBelowPos_.step);
1782 QString str = QString("PATTERN_COPY:%1,%2,%3,")
1783 .arg(QString::number(selLeftAbovePos_.colInTrack),
1784 QString::number(w),
1785 QString::number(h));
1786 // NOTE: After this line, PatternPosition.trackVisIdx indecates a real track number.
1787 PatternPosition pos = {
1788 selLeftAbovePos_.trackVisIdx, selLeftAbovePos_.colInTrack,
1789 selLeftAbovePos_.order, selLeftAbovePos_.step
1790 };
1791 while (true) {
1792 int trackIdx = visTracks_.at(pos.trackVisIdx);
1793 switch (pos.colInTrack) {
1794 case 0:
1795 str += QString::number(bt_->getStepNoteNumber(curSongNum_, trackIdx, pos.order, pos.step));
1796 break;
1797 case 1:
1798 str += QString::number(bt_->getStepInstrument(curSongNum_, trackIdx, pos.order, pos.step));
1799 break;
1800 case 2:
1801 str += QString::number(bt_->getStepVolume(curSongNum_, trackIdx, pos.order, pos.step));
1802 break;
1803 case 3:
1804 str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 0));
1805 break;
1806 case 4:
1807 str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 0));
1808 break;
1809 case 5:
1810 str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 1));
1811 break;
1812 case 6:
1813 str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 1));
1814 break;
1815 case 7:
1816 str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 2));
1817 break;
1818 case 8:
1819 str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 2));
1820 break;
1821 case 9:
1822 str += QString::fromStdString(bt_->getStepEffectID(curSongNum_, trackIdx, pos.order, pos.step, 3));
1823 break;
1824 case 10:
1825 str += QString::number(bt_->getStepEffectValue(curSongNum_, trackIdx, pos.order, pos.step, 3));
1826 break;
1827 }
1828
1829 if (pos.isEqualCols(selRightBelowPos_)) {
1830 if (pos.isEqualRows(selRightBelowPos_)) {
1831 break;
1832 }
1833 else {
1834 pos.setCols(selLeftAbovePos_.trackVisIdx, selLeftAbovePos_.colInTrack);
1835 ++pos.step;
1836 }
1837 }
1838 else {
1839 ++pos.colInTrack;
1840 pos.trackVisIdx += (pos.colInTrack / 11);
1841 pos.colInTrack %= 11;
1842 }
1843
1844 str += ",";
1845 }
1846
1847 QApplication::clipboard()->setText(str);
1848 }
1849
eraseSelectedCells()1850 void PatternEditorPanel::eraseSelectedCells()
1851 {
1852 bt_->erasePatternCells(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack,
1853 selLeftAbovePos_.order, selLeftAbovePos_.step,
1854 visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step);
1855 comStack_.lock()->push(new EraseCellsInPatternQtCommand(this));
1856 }
1857
pasteCopiedCells(const PatternPosition & cursorPos)1858 void PatternEditorPanel::pasteCopiedCells(const PatternPosition& cursorPos)
1859 {
1860 int sCol = 0;
1861 PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol);
1862 PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size());
1863 if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) {
1864 cells = compandPasteCells(pos, cells);
1865 }
1866
1867 bt_->pastePatternCells(
1868 curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells));
1869 comStack_.lock()->push(new PasteCopiedDataToPatternQtCommand(this));
1870 }
1871
pasteMixCopiedCells(const PatternPosition & cursorPos)1872 void PatternEditorPanel::pasteMixCopiedCells(const PatternPosition& cursorPos)
1873 {
1874 int sCol = 0;
1875 PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol);
1876 PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size());
1877 if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) {
1878 cells = compandPasteCells(pos, cells);
1879 }
1880
1881 bt_->pasteMixPatternCells(
1882 curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells));
1883 comStack_.lock()->push(new PasteMixCopiedDataToPatternQtCommand(this));
1884 }
1885
pasteOverwriteCopiedCells(const PatternPosition & cursorPos)1886 void PatternEditorPanel::pasteOverwriteCopiedCells(const PatternPosition& cursorPos)
1887 {
1888 int sCol = 0;
1889 PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol);
1890 PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size());
1891 if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) {
1892 cells = compandPasteCells(pos, cells);
1893 }
1894
1895 bt_->pasteOverwritePatternCells(
1896 curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells));
1897 comStack_.lock()->push(new PasteOverwriteCopiedDataToPatternQtCommand(this));
1898 }
1899
pasteInsertCopiedCells(const PatternPosition & cursorPos)1900 void PatternEditorPanel::pasteInsertCopiedCells(const PatternPosition& cursorPos)
1901 {
1902 int sCol = 0;
1903 PatternCells cells = decodeCells(QApplication::clipboard()->text(), sCol);
1904 PatternPosition pos = getPasteLeftAbovePosition(sCol, cursorPos, cells.front().size());
1905 if (config_->getPasteMode() == Configuration::FILL && selLeftAbovePos_.order != -1) {
1906 cells = compandPasteCells(pos, cells);
1907 }
1908
1909 bt_->pasteInsertPatternCells(
1910 curSongNum_, visTracks_.at(pos.trackVisIdx), pos.colInTrack, pos.order, pos.step, std::move(cells));
1911 comStack_.lock()->push(new PasteInsertCopiedDataToPatternQtCommand(this));
1912 }
1913
decodeCells(QString str,int & startCol)1914 PatternEditorPanel::PatternCells PatternEditorPanel::decodeCells(QString str, int& startCol)
1915 {
1916 str.remove(QRegularExpression("PATTERN_(COPY|CUT):"));
1917 QString hdRe = "^([0-9]+),([0-9]+),([0-9]+),";
1918 QRegularExpression re(hdRe);
1919 QRegularExpressionMatch match = re.match(str);
1920 startCol = match.captured(1).toInt();
1921 int w = match.captured(2).toInt();
1922 size_t h = match.captured(3).toUInt();
1923 str.remove(re);
1924
1925 std::vector<std::vector<std::string>> cells;
1926 re = QRegularExpression("^([^,]+),");
1927 for (size_t i = 0; i < h; ++i) {
1928 cells.emplace_back();
1929 for (int j = 0; j < w; ++j) {
1930 match = re.match(str);
1931 if (match.hasMatch()) {
1932 cells.at(i).push_back(match.captured(1).toStdString());
1933 str.remove(re);
1934 }
1935 else {
1936 cells.at(i).push_back(str.toStdString());
1937 break;
1938 }
1939 }
1940 }
1941
1942 return cells;
1943 }
1944
getPasteLeftAbovePosition(int pasteCol,const PatternPosition & cursorPos,size_t cellW) const1945 PatternPosition PatternEditorPanel::getPasteLeftAbovePosition(
1946 int pasteCol, const PatternPosition& cursorPos, size_t cellW) const
1947 {
1948 PatternPosition pos;
1949 Configuration::PasteMode mode = config_->getPasteMode();
1950 if ((mode == Configuration::SELECTION || mode == Configuration::FILL)
1951 && selLeftAbovePos_.colInTrack != -1) {
1952 pos = selLeftAbovePos_;
1953 pos.colInTrack = pasteCol;
1954 }
1955 else {
1956 pos = cursorPos;
1957 if (pasteCol < 3 || (pos.colInTrack - pasteCol) % 2
1958 || (11 - pos.colInTrack) < static_cast<int>(cellW)) {
1959 pos.colInTrack = pasteCol;
1960 }
1961 }
1962
1963 return pos;
1964 }
1965
cutSelectedCells()1966 void PatternEditorPanel::cutSelectedCells()
1967 {
1968 if (selLeftAbovePos_.order == -1) return;
1969
1970 copySelectedCells();
1971 eraseSelectedCells();
1972 QString str = QApplication::clipboard()->text();
1973 str.replace("COPY", "CUT");
1974 QApplication::clipboard()->setText(str);
1975 }
1976
compandPasteCells(const PatternPosition & laPos,const PatternCells & cells)1977 PatternEditorPanel::PatternCells PatternEditorPanel::compandPasteCells(const PatternPosition& laPos, const PatternCells& cells)
1978 {
1979 PatternCells newCells;
1980 int ow = cells.front().size();
1981 int oh = cells.size();
1982 int l = laPos.trackVisIdx * 11 + laPos.colInTrack;
1983 int r = selRightBelowPos_.trackVisIdx * 11 + selRightBelowPos_.colInTrack;
1984 int w = r - l + 1; // Real selected region width
1985 size_t h = static_cast<size_t>(calculateStepDistance(laPos.order, laPos.step,
1986 selRightBelowPos_.order, selRightBelowPos_.step) + 1);
1987 int bw = ((ow - 1) / 11 + 1) * 11;
1988 size_t padSize = bw - ow;
1989 std::vector<std::string> pad(padSize);
1990 int lc = (laPos.colInTrack + ow) % 11;
1991 for (size_t i = 0; i < padSize; ++i) {
1992 switch (lc) {
1993 case 3:
1994 case 5:
1995 case 7:
1996 case 9:
1997 pad[i] = "--";
1998 break;
1999 default:
2000 pad[i] = "-1";
2001 break;
2002 }
2003 lc = (lc + 1) % 11;
2004 }
2005 for (size_t i = 0; i < h; ++i) {
2006 newCells.emplace_back(w);
2007 auto&& rowBeginIt = newCells.back().begin();
2008 for (int dw = w, p = 0; dw > 0; dw -= bw, p += bw) {
2009 int ws = std::min(ow, dw);
2010 std::copy_n(cells.at(i % oh).begin(), ws, rowBeginIt + p);
2011 std::copy_n(pad.begin(), std::min(dw - ws, static_cast<int>(padSize)), rowBeginIt + p + ws);
2012 }
2013 }
2014
2015 return newCells;
2016 }
2017
transposeNote(const PatternPosition & startPos,const PatternPosition & endPos,int seminote)2018 void PatternEditorPanel::transposeNote(const PatternPosition& startPos, const PatternPosition& endPos, int seminote)
2019 {
2020 int beginTrackIdx = (startPos.colInTrack == 0) ? startPos.trackVisIdx : startPos.trackVisIdx + 1;
2021 if (beginTrackIdx <= endPos.trackVisIdx) {
2022 bt_->transposeNoteInPattern(curSongNum_, visTracks_.at(beginTrackIdx), startPos.order, startPos.step,
2023 visTracks_.at(endPos.trackVisIdx), endPos.step, seminote);
2024 comStack_.lock()->push(new TransposeNoteInPatternQtCommand(this));
2025 }
2026 }
2027
changeValuesInPattern(const PatternPosition & startPos,const PatternPosition & endPos,int value)2028 void PatternEditorPanel::changeValuesInPattern(const PatternPosition& startPos, const PatternPosition& endPos, int value)
2029 {
2030 if (startPos.compareCols(endPos) <= 0) {
2031 bt_->changeValuesInPattern(curSongNum_, visTracks_.at(startPos.trackVisIdx), startPos.colInTrack, startPos.order, startPos.step,
2032 visTracks_.at(endPos.trackVisIdx), endPos.colInTrack, endPos.step, value);
2033 comStack_.lock()->push(new ChangeValuesInPatternQtCommand(this));
2034 }
2035 }
2036
toggleTrack(int trackIdx)2037 void PatternEditorPanel::toggleTrack(int trackIdx)
2038 {
2039 int trackNum = visTracks_.at(trackIdx);
2040 bt_->setTrackMuteState(trackNum, !bt_->isMute(trackNum));
2041 isMuteElse_ = false;
2042 redrawByMaskChanged();
2043 }
2044
soloTrack(int trackIdx)2045 void PatternEditorPanel::soloTrack(int trackIdx)
2046 {
2047 int soloTrackNum = visTracks_.at(trackIdx);
2048 int trackCnt = static_cast<int>(songStyle_.trackAttribs.size());
2049 if (isMuteElse_) {
2050 if (bt_->isMute(soloTrackNum)) {
2051 for (int t = 0; t < trackCnt; ++t)
2052 bt_->setTrackMuteState(t, t != soloTrackNum);
2053 }
2054 else {
2055 isMuteElse_ = false;
2056 for (int t = 0; t < trackCnt; ++t)
2057 bt_->setTrackMuteState(t, false);
2058 }
2059 }
2060 else {
2061 isMuteElse_ = true;
2062 for (int t = 0; t < trackCnt; ++t)
2063 bt_->setTrackMuteState(t, (t == soloTrackNum) ? false : isMuteElse_);
2064 }
2065 redrawByMaskChanged();
2066 }
2067
setSelectedRectangle(const PatternPosition & start,const PatternPosition & end)2068 void PatternEditorPanel::setSelectedRectangle(const PatternPosition& start, const PatternPosition& end)
2069 {
2070 int patMax = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, end.order)) - 1;
2071 if (start.compareCols(end) > 0) {
2072 if (start.step > end.step) {
2073 selLeftAbovePos_ = end;
2074 selRightBelowPos_ = {
2075 start.trackVisIdx, start.colInTrack,
2076 end.order, (start.step > patMax) ? patMax : start.step
2077 };
2078 }
2079 else {
2080 selLeftAbovePos_ = { end.trackVisIdx, end.colInTrack, end.order, start.step };
2081 selRightBelowPos_ = { start.trackVisIdx, start.colInTrack, end.order, end.step };
2082 }
2083 }
2084 else {
2085 if (start.step > end.step) {
2086 selLeftAbovePos_ = { start.trackVisIdx, start.colInTrack, end.order, end.step };
2087 selRightBelowPos_ = {
2088 end.trackVisIdx, end.colInTrack,
2089 end.order, (start.step > patMax) ? patMax : start.step
2090 };
2091 }
2092 else {
2093 selLeftAbovePos_ = { start.trackVisIdx, start.colInTrack, end.order, start.step };
2094 selRightBelowPos_ = end;
2095 }
2096 }
2097
2098 emit selected(true);
2099
2100 backChanged_ = true;
2101 repaint();
2102 }
2103
isSelectedCell(int trackVisIdx,int colNum,int orderNum,int stepNum)2104 bool PatternEditorPanel::isSelectedCell(int trackVisIdx, int colNum, int orderNum, int stepNum)
2105 {
2106 PatternPosition pos{ trackVisIdx, colNum, orderNum, stepNum };
2107 return (selLeftAbovePos_.compareCols(pos) <= 0 && selRightBelowPos_.compareCols(pos) >= 0
2108 && selLeftAbovePos_.compareRows(pos) <= 0 && selRightBelowPos_.compareRows(pos) >= 0);
2109 }
2110
showPatternContextMenu(const PatternPosition & pos,const QPoint & point)2111 void PatternEditorPanel::showPatternContextMenu(const PatternPosition& pos, const QPoint& point)
2112 {
2113 QMenu menu;
2114 // Leave Before Qt5.7.0 style due to windows xp
2115 QAction* undo = menu.addAction(tr("&Undo"));
2116 undo->setIcon(QIcon(":/icon/undo"));
2117 QObject::connect(undo, &QAction::triggered, this, [&]() {
2118 bt_->undo();
2119 comStack_.lock()->undo();
2120 });
2121 QAction* redo = menu.addAction(tr("&Redo"));
2122 redo->setIcon(QIcon(":/icon/redo"));
2123 QObject::connect(redo, &QAction::triggered, this, [&]() {
2124 bt_->redo();
2125 comStack_.lock()->redo();
2126 });
2127 menu.addSeparator();
2128 QAction* copy = menu.addAction(tr("&Copy"));
2129 copy->setIcon(QIcon(":/icon/copy"));
2130 QObject::connect(copy, &QAction::triggered, this, &PatternEditorPanel::copySelectedCells);
2131 QAction* cut = menu.addAction(tr("Cu&t"));
2132 cut->setIcon(QIcon(":/icon/cut"));
2133 QObject::connect(cut, &QAction::triggered, this, &PatternEditorPanel::cutSelectedCells);
2134 QAction* paste = menu.addAction(tr("&Paste"));
2135 paste->setIcon(QIcon(":/icon/paste"));
2136 QObject::connect(paste, &QAction::triggered, this, [&]() { pasteCopiedCells(pos); });
2137 auto pasteSp = new QMenu(tr("Paste Specia&l"));
2138 menu.addMenu(pasteSp);
2139 QAction* pasteMix = pasteSp->addAction(tr("&Mix"));
2140 QObject::connect(pasteMix, &QAction::triggered, this, [&]() { pasteMixCopiedCells(pos); });
2141 QAction* pasteOver = pasteSp->addAction(tr("&Overwrite"));
2142 QObject::connect(pasteOver, &QAction::triggered, this, [&]() { pasteOverwriteCopiedCells(pos); });
2143 QAction* pasteIns = pasteSp->addAction(tr("&Insert"));
2144 QObject::connect(pasteIns, &QAction::triggered, this, [&]() { pasteInsertCopiedCells(pos); });
2145 QAction* erase = menu.addAction(tr("&Erase"));
2146 QObject::connect(erase, &QAction::triggered, this, &PatternEditorPanel::eraseSelectedCells);
2147 QAction* select = menu.addAction(tr("Select &All"));
2148 QObject::connect(select, &QAction::triggered, this, [&]() { onSelectPressed(1); });
2149 menu.addSeparator();
2150 auto pattern = new QMenu(tr("Patter&n"));
2151 menu.addMenu(pattern);
2152 QAction* interpolate = pattern->addAction(tr("&Interpolate"));
2153 QObject::connect(interpolate, &QAction::triggered, this, &PatternEditorPanel::onInterpolatePressed);
2154 QAction* reverse = pattern->addAction(tr("&Reverse"));
2155 QObject::connect(reverse, &QAction::triggered, this, &PatternEditorPanel::onReversePressed);
2156 QAction* replace = pattern->addAction(tr("R&eplace Instrument"));
2157 QObject::connect(replace, &QAction::triggered, this, &PatternEditorPanel::onReplaceInstrumentPressed);
2158 pattern->addSeparator();
2159 QAction* expand = pattern->addAction(tr("E&xpand"));
2160 QObject::connect(expand, &QAction::triggered, this, &PatternEditorPanel::onExpandPressed);
2161 QAction* shrink = pattern->addAction(tr("S&hrink"));
2162 QObject::connect(shrink, &QAction::triggered, this, &PatternEditorPanel::onShrinkPressed);
2163 pattern->addSeparator();
2164 auto transpose = new QMenu(tr("&Transpose"));
2165 pattern->addMenu(transpose);
2166 QAction* deNote = transpose->addAction(tr("&Decrease Note"));
2167 QObject::connect(deNote, &QAction::triggered, this, [&]() { onNoteTransposePressed(-1); });
2168 QAction* inNote = transpose->addAction(tr("&Increase Note"));
2169 QObject::connect(inNote, &QAction::triggered, this, [&]() { onNoteTransposePressed(1); });
2170 QAction* deOct = transpose->addAction(tr("D&ecrease Octave"));
2171 QObject::connect(deOct, &QAction::triggered, this, [&]() { onNoteTransposePressed(-12); });
2172 QAction* inOct = transpose->addAction(tr("I&ncrease Octave"));
2173 QObject::connect(inOct, &QAction::triggered, this, [&]() { onNoteTransposePressed(12); });
2174 auto changeVals = new QMenu(tr("&Change Values"));
2175 pattern->addMenu(changeVals);
2176 QAction* fdeVal = changeVals->addAction(tr("Fine &Decrease Values"));
2177 QObject::connect(fdeVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(-1); });
2178 QAction* finVal = changeVals->addAction(tr("Fine &Increase Values"));
2179 QObject::connect(finVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(1); });
2180 QAction* cdeVal = changeVals->addAction(tr("Coarse D&ecrease Values"));
2181 QObject::connect(cdeVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(-16); });
2182 QAction* cinVal = changeVals->addAction(tr("Coarse I&ncrease Values"));
2183 QObject::connect(cinVal, &QAction::triggered, this, [&]() { onChangeValuesPressed(16); });
2184 menu.addSeparator();
2185 QAction* toggle = menu.addAction(tr("To&ggle Track"));
2186 QObject::connect(toggle, &QAction::triggered, this, [&] { toggleTrack(pos.trackVisIdx); });
2187 QAction* solo = menu.addAction(tr("&Solo Track"));
2188 QObject::connect(solo, &QAction::triggered, this, [&] { soloTrack(pos.trackVisIdx); });
2189 menu.addSeparator();
2190 QAction* exeff = menu.addAction(tr("Expand E&ffect Column"));
2191 QObject::connect(exeff, &QAction::triggered, this, [&] { onExpandEffectColumnPressed(pos.trackVisIdx); });
2192 QAction* sheff = menu.addAction(tr("Shrin&k Effect Column"));
2193 QObject::connect(sheff, &QAction::triggered, this, [&] { onShrinkEffectColumnPressed(pos.trackVisIdx); });
2194 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
2195 undo->setShortcutVisibleInContextMenu(true);
2196 redo->setShortcutVisibleInContextMenu(true);
2197 copy->setShortcutVisibleInContextMenu(true);
2198 cut->setShortcutVisibleInContextMenu(true);
2199 paste->setShortcutVisibleInContextMenu(true);
2200 pasteMix->setShortcutVisibleInContextMenu(true);
2201 erase->setShortcutVisibleInContextMenu(true);
2202 select->setShortcutVisibleInContextMenu(true);
2203 interpolate->setShortcutVisibleInContextMenu(true);
2204 reverse->setShortcutVisibleInContextMenu(true);
2205 replace->setShortcutVisibleInContextMenu(true);
2206 deNote->setShortcutVisibleInContextMenu(true);
2207 inNote->setShortcutVisibleInContextMenu(true);
2208 deOct->setShortcutVisibleInContextMenu(true);
2209 inOct->setShortcutVisibleInContextMenu(true);
2210 fdeVal->setShortcutVisibleInContextMenu(true);
2211 finVal->setShortcutVisibleInContextMenu(true);
2212 cdeVal->setShortcutVisibleInContextMenu(true);
2213 cinVal->setShortcutVisibleInContextMenu(true);
2214 toggle->setShortcutVisibleInContextMenu(true);
2215 solo->setShortcutVisibleInContextMenu(true);
2216 exeff->setShortcutVisibleInContextMenu(true);
2217 sheff->setShortcutVisibleInContextMenu(true);
2218 #endif
2219 auto shortcuts = config_->getShortcuts();
2220 undo->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z));
2221 redo->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
2222 undo->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z));
2223 copy->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C));
2224 cut->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X));
2225 paste->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V));
2226 pasteMix->setShortcut(strToKeySeq(shortcuts.at(Configuration::PasteMix)));
2227 pasteOver->setShortcut(strToKeySeq(shortcuts.at(Configuration::PasteOverwrite)));
2228 pasteIns->setShortcut(strToKeySeq(shortcuts.at(Configuration::PasteInsert)));
2229 erase->setShortcut(QKeySequence(Qt::Key_Delete));
2230 select->setShortcut(strToKeySeq(shortcuts.at(Configuration::SelectAll)));
2231 interpolate->setShortcut(strToKeySeq(shortcuts.at(Configuration::Interpolate)));
2232 reverse->setShortcut(strToKeySeq(shortcuts.at(Configuration::Reverse)));
2233 replace->setShortcut(strToKeySeq(shortcuts.at(Configuration::ReplaceInstrument)));
2234 expand->setShortcut(strToKeySeq(shortcuts.at(Configuration::ExpandPattern)));
2235 shrink->setShortcut(strToKeySeq(shortcuts.at(Configuration::ShrinkPattern)));
2236 deNote->setShortcut(strToKeySeq(shortcuts.at(Configuration::DecreaseNote)));
2237 inNote->setShortcut(strToKeySeq(shortcuts.at(Configuration::IncreaseNote)));
2238 deOct->setShortcut(strToKeySeq(shortcuts.at(Configuration::DecreaseOctave)));
2239 inOct->setShortcut(strToKeySeq(shortcuts.at(Configuration::IncreaseOctave)));
2240 fdeVal->setShortcut(strToKeySeq(shortcuts.at(Configuration::FineDecreaseValues)));
2241 finVal->setShortcut(strToKeySeq(shortcuts.at(Configuration::FineIncreaseValues)));
2242 cdeVal->setShortcut(strToKeySeq(shortcuts.at(Configuration::CoarseDecreaseValues)));
2243 cinVal->setShortcut(strToKeySeq(shortcuts.at(Configuration::CoarseIncreaseValuse)));
2244 toggle->setShortcut(strToKeySeq(shortcuts.at(Configuration::ToggleTrack)));
2245 solo->setShortcut(strToKeySeq(shortcuts.at(Configuration::SoloTrack)));
2246 exeff->setShortcut(strToKeySeq(shortcuts.at(Configuration::ExpandEffect)));
2247 sheff->setShortcut(strToKeySeq(shortcuts.at(Configuration::ShrinkEffect)));
2248
2249 if (bt_->isJamMode() || pos.order < 0 || pos.trackVisIdx < 0) {
2250 copy->setEnabled(false);
2251 cut->setEnabled(false);
2252 paste->setEnabled(false);
2253 pasteMix->setEnabled(false);
2254 pasteOver->setEnabled(false);
2255 pasteIns->setEnabled(false);
2256 erase->setEnabled(false);
2257 interpolate->setEnabled(false);
2258 reverse->setEnabled(false);
2259 replace->setEnabled(false);
2260 expand->setEnabled(false);
2261 shrink->setEnabled(false);
2262 deNote->setEnabled(false);
2263 inNote->setEnabled(false);
2264 deOct->setEnabled(false);
2265 inOct->setEnabled(false);
2266 fdeVal->setEnabled(false);
2267 finVal->setEnabled(false);
2268 cdeVal->setEnabled(false);
2269 cinVal->setEnabled(false);
2270 }
2271 else {
2272 QString clipText = QApplication::clipboard()->text();
2273 if (!clipText.startsWith("PATTERN_COPY") && !clipText.startsWith("PATTERN_CUT")) {
2274 paste->setEnabled(false);
2275 pasteMix->setEnabled(false);
2276 pasteOver->setEnabled(false);
2277 pasteIns->setEnabled(false);
2278 }
2279 if (selRightBelowPos_.order < 0
2280 || !isSelectedCell(pos.trackVisIdx, pos.colInTrack, pos.order, pos.step)) {
2281 copy->setEnabled(false);
2282 cut->setEnabled(false);
2283 erase->setEnabled(false);
2284 interpolate->setEnabled(false);
2285 reverse->setEnabled(false);
2286 replace->setEnabled(false);
2287 expand->setEnabled(false);
2288 shrink->setEnabled(false);
2289 deNote->setEnabled(false);
2290 inNote->setEnabled(false);
2291 deOct->setEnabled(false);
2292 inOct->setEnabled(false);
2293 fdeVal->setEnabled(false);
2294 finVal->setEnabled(false);
2295 cdeVal->setEnabled(false);
2296 cinVal->setEnabled(false);
2297 }
2298 }
2299 if (!comStack_.lock()->canUndo()) {
2300 undo->setEnabled(false);
2301 }
2302 if (!comStack_.lock()->canRedo()) {
2303 redo->setEnabled(false);
2304 }
2305 if (pos.trackVisIdx < 0) {
2306 toggle->setEnabled(false);
2307 solo->setEnabled(false);
2308 exeff->setEnabled(false);
2309 sheff->setEnabled(false);
2310 }
2311
2312 menu.exec(mapToGlobal(point));
2313 }
2314
2315 /********** Slots **********/
onHScrollBarChanged(int num)2316 void PatternEditorPanel::onHScrollBarChanged(int num)
2317 {
2318 Ui::EventGuard eg(isIgnoreToSlider_);
2319
2320 // Skip if position has already changed in panel
2321 if (config_->getMoveCursorByHorizontalScroll()) {
2322 if (int dif = num - calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack))
2323 moveCursorToRight(dif);
2324 }
2325 else {
2326 if (int dif = num - leftTrackVisIdx_)
2327 moveViewToRight(dif);
2328 }
2329 }
2330
onVScrollBarChanged(int num)2331 void PatternEditorPanel::onVScrollBarChanged(int num)
2332 {
2333 Ui::EventGuard eg(isIgnoreToSlider_);
2334
2335 // Skip if position has already changed in panel
2336 if (int dif = num - curPos_.step) moveCursorToDown(dif);
2337 }
2338
onOrderListCurrentTrackChanged(int idx)2339 void PatternEditorPanel::onOrderListCurrentTrackChanged(int idx)
2340 {
2341 Ui::EventGuard eg(isIgnoreToOrder_);
2342
2343 int dif = calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack, idx, 0);
2344 moveCursorToRight(dif);
2345 }
2346
onOrderListCurrentOrderChanged(int num)2347 void PatternEditorPanel::onOrderListCurrentOrderChanged(int num)
2348 {
2349 Ui::EventGuard eg(isIgnoreToOrder_);
2350
2351 int step = std::min(curPos_.step, static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, num)) - 1);
2352 int dif = calculateStepDistance(curPos_.order, curPos_.step, num, step);
2353 moveCursorToDown(dif);
2354 }
2355
onOrderListEdited()2356 void PatternEditorPanel::onOrderListEdited()
2357 {
2358 // Reset position memory
2359 hovPos_ = { -1, -1, -1, -1 };
2360 mousePressPos_ = { -1, -1, -1, -1 };
2361 mouseReleasePos_ = { -1, -1, -1, -1 };
2362 selLeftAbovePos_ = { -1, -1, -1, -1 };
2363 selRightBelowPos_ = { -1, -1, -1, -1 };
2364 shiftPressedPos_ = { -1, -1, -1, -1 };
2365 selectAllState_ = -1;
2366 emit selected(false);
2367
2368 redrawByPatternChanged(true);
2369 }
2370
onDefaultPatternSizeChanged()2371 void PatternEditorPanel::onDefaultPatternSizeChanged()
2372 {
2373 // Check pattern size
2374 int end = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order));
2375 if (curPos_.step >= end) curPos_.step = end - 1;
2376
2377 redrawByPatternChanged(true);
2378 }
2379
onShortcutUpdated()2380 void PatternEditorPanel::onShortcutUpdated()
2381 {
2382 auto shortcuts = config_->getShortcuts();
2383
2384 hlUpSc_.setKey(strToKeySeq(shortcuts.at(Configuration::PrevHighlighted)));
2385 hlUpWSSc_.setKey(strToKeySeq("Shift+" + shortcuts.at(Configuration::PrevHighlighted)));
2386 hlDnSc_.setKey(strToKeySeq(shortcuts.at(Configuration::NextHighlighted)));
2387 hlDnWSSc_.setKey(strToKeySeq("Shift+" + shortcuts.at(Configuration::NextHighlighted)));
2388 keyOffSc_.setKey(strToKeySeq(shortcuts.at(Configuration::KeyOff)));
2389 echoBufSc_.setKey(strToKeySeq(shortcuts.at(Configuration::EchoBuffer)));
2390 expandColSc_.setKey(strToKeySeq(shortcuts.at(Configuration::ExpandEffect)));
2391 shrinkColSc_.setKey(strToKeySeq(shortcuts.at(Configuration::ShrinkEffect)));
2392 }
2393
setPatternHighlight1Count(int count)2394 void PatternEditorPanel::setPatternHighlight1Count(int count)
2395 {
2396 hl1Cnt_ = count;
2397
2398 backChanged_ = true;
2399 textChanged_ = true;
2400 repaint();
2401 }
2402
setPatternHighlight2Count(int count)2403 void PatternEditorPanel::setPatternHighlight2Count(int count)
2404 {
2405 hl2Cnt_ = count;
2406
2407 backChanged_ = true;
2408 textChanged_ = true;
2409 repaint();
2410 }
2411
setEditableStep(int n)2412 void PatternEditorPanel::setEditableStep(int n)
2413 {
2414 editableStepCnt_ = n;
2415 }
2416
onSongLoaded()2417 void PatternEditorPanel::onSongLoaded()
2418 {
2419 // NOTE: Temporary fix for https://github.com/rerrahkr/BambooTracker/issues/276
2420 isInitedFirstMod_.store(true);
2421
2422 // Initialize cursor position
2423 curSongNum_ = bt_->getCurrentSongNumber();
2424 SongType prevType = songStyle_.type;
2425 songStyle_ = bt_->getSongStyle(curSongNum_);
2426 visTracks_ = adaptVisibleTrackList(visTracks_, prevType, songStyle_.type);
2427 rightEffn_.resize(visTracks_.size());
2428 std::transform(visTracks_.begin(), visTracks_.end(), rightEffn_.begin(), [&](int t) {
2429 return static_cast<int>(bt_->getEffectDisplayWidth(curSongNum_, t));
2430 });
2431 curPos_ = {
2432 visTracks_.front(),
2433 0,
2434 bt_->getCurrentOrderNumber(),
2435 bt_->getCurrentStepNumber()
2436 };
2437 // Set cursor to the most lest-placed visible track
2438 if (visTracks_.front() != bt_->getCurrentTrackAttribute().number) {
2439 bt_->setCurrentTrack(visTracks_.front());
2440 }
2441 leftTrackVisIdx_ = 0;
2442 updateTracksWidthFromLeftToEnd();
2443
2444 hovPos_ = { -1, -1, -1, -1 };
2445 mousePressPos_ = { -1, -1, -1, -1 };
2446 mouseReleasePos_ = { -1, -1, -1, -1 };
2447 selLeftAbovePos_ = { -1, -1, -1, -1 };
2448 selRightBelowPos_ = { -1, -1, -1, -1 };
2449 shiftPressedPos_ = { -1, -1, -1, -1 };
2450 markerPos_ = { -1, -1, -1, -1 };
2451 entryCnt_ = 0;
2452 selectAllState_ = -1;
2453 emit selected(false);
2454
2455 redrawAll();
2456 }
2457
onDeletePressed()2458 void PatternEditorPanel::onDeletePressed()
2459 {
2460 if (bt_->isJamMode()) return;
2461
2462 if (selLeftAbovePos_.order != -1) { // Delete region
2463 eraseSelectedCells();
2464 }
2465 else {
2466 switch (curPos_.colInTrack) {
2467 case 0:
2468 bt_->eraseStepNote(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step);
2469 comStack_.lock()->push(new EraseStepQtCommand(this));
2470 break;
2471 case 1:
2472 bt_->eraseStepInstrument(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step);
2473 comStack_.lock()->push(new EraseInstrumentInStepQtCommand(this));
2474 break;
2475 case 2:
2476 bt_->eraseStepVolume(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step);
2477 comStack_.lock()->push(new EraseVolumeInStepQtCommand(this));
2478 break;
2479 case 3:
2480 case 5:
2481 case 7:
2482 case 9:
2483 bt_->eraseStepEffect(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step,
2484 (curPos_.colInTrack - 3) / 2);
2485 comStack_.lock()->push(new EraseEffectInStepQtCommand(this));
2486 break;
2487 case 4:
2488 case 6:
2489 case 8:
2490 case 10:
2491 bt_->eraseStepEffectValue(curSongNum_, visTracks_.at(curPos_.trackVisIdx), curPos_.order, curPos_.step,
2492 (curPos_.colInTrack - 4) / 2);
2493 comStack_.lock()->push(new EraseEffectValueInStepQtCommand(this));
2494 break;
2495 }
2496 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(editableStepCnt_);
2497 }
2498 }
2499
onPastePressed()2500 void PatternEditorPanel::onPastePressed()
2501 {
2502 if (!bt_->isJamMode()) pasteCopiedCells(curPos_);
2503 }
2504
onPasteMixPressed()2505 void PatternEditorPanel::onPasteMixPressed()
2506 {
2507 if (!bt_->isJamMode()) pasteMixCopiedCells(curPos_);
2508 }
2509
onPasteOverwritePressed()2510 void PatternEditorPanel::onPasteOverwritePressed()
2511 {
2512 if (!bt_->isJamMode()) pasteOverwriteCopiedCells(curPos_);
2513 }
2514
onPasteInsertPressed()2515 void PatternEditorPanel::onPasteInsertPressed()
2516 {
2517 if (!bt_->isJamMode()) pasteInsertCopiedCells(curPos_);
2518 }
2519
onSelectPressed(int type)2520 void PatternEditorPanel::onSelectPressed(int type)
2521 {
2522 switch (type) {
2523 case 0: // None
2524 {
2525 selLeftAbovePos_ = { -1, -1, -1, -1 };
2526 selRightBelowPos_ = { -1, -1, -1, -1 };
2527 selectAllState_ = -1;
2528 emit selected(false);
2529 backChanged_ = true;
2530 repaint();
2531 break;
2532 }
2533 case 1: // All
2534 {
2535 selectAllState_ = (selectAllState_ + 1) % 2;
2536 int max = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1;
2537 PatternPosition start, end;
2538 if (selectAllState_) {
2539 start = { 0, 0, curPos_.order, 0 };
2540 end = { static_cast<int>(visTracks_.size() - 1), 10, curPos_.order, max };
2541 }
2542 else {
2543 start = { curPos_.trackVisIdx, 0, curPos_.order, 0 };
2544 end = { curPos_.trackVisIdx, 10, curPos_.order, max };
2545 }
2546 setSelectedRectangle(start, end);
2547 break;
2548 }
2549 case 2: // Row
2550 {
2551 selectAllState_ = -1;
2552 PatternPosition start = { 0, 0, curPos_.order, curPos_.step };
2553 PatternPosition end = { static_cast<int>(visTracks_.size() - 1), 10, curPos_.order, curPos_.step };
2554 setSelectedRectangle(start, end);
2555 break;
2556 }
2557 case 3: // Column
2558 {
2559 selectAllState_ = -1;
2560 PatternPosition start = { curPos_.trackVisIdx, curPos_.colInTrack, curPos_.order, 0 };
2561 PatternPosition end = {
2562 curPos_.trackVisIdx,
2563 curPos_.colInTrack,
2564 curPos_.order,
2565 static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order) - 1)
2566 };
2567 setSelectedRectangle(start, end);
2568 break;
2569 }
2570 case 4: // Pattern
2571 {
2572 selectAllState_ = -1;
2573 PatternPosition start = { curPos_.trackVisIdx, 0, curPos_.order, 0 };
2574 PatternPosition end = {
2575 curPos_.trackVisIdx,
2576 10,
2577 curPos_.order,
2578 static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order) - 1)
2579 };
2580 setSelectedRectangle(start, end);
2581 break;
2582 }
2583 case 5: // Order
2584 {
2585 selectAllState_ = -1;
2586 PatternPosition start = { 0, 0, curPos_.order, 0 };
2587 PatternPosition end = {
2588 static_cast<int>(visTracks_.size() - 1),
2589 10,
2590 curPos_.order,
2591 static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order) - 1)
2592 };
2593 setSelectedRectangle(start, end);
2594 break;
2595 }
2596 }
2597 }
2598
onNoteTransposePressed(int seminote)2599 void PatternEditorPanel::onNoteTransposePressed(int seminote)
2600 {
2601 if (bt_->isJamMode()) return;
2602
2603 if (selLeftAbovePos_.order != -1)
2604 transposeNote(selLeftAbovePos_, selRightBelowPos_, seminote);
2605 else
2606 transposeNote(curPos_, curPos_, seminote);
2607 }
2608
onToggleTrackPressed()2609 void PatternEditorPanel::onToggleTrackPressed()
2610 {
2611 toggleTrack(curPos_.trackVisIdx);
2612 }
2613
onSoloTrackPressed()2614 void PatternEditorPanel::onSoloTrackPressed()
2615 {
2616 soloTrack(curPos_.trackVisIdx);
2617 }
2618
onUnmuteAllPressed()2619 void PatternEditorPanel::onUnmuteAllPressed()
2620 {
2621 int trackCnt = static_cast<int>(songStyle_.trackAttribs.size());
2622 for (int t = 0; t < trackCnt; ++t)
2623 bt_->setTrackMuteState(t, false);
2624 isMuteElse_ = false;
2625 redrawByMaskChanged();
2626 }
2627
onExpandPressed()2628 void PatternEditorPanel::onExpandPressed()
2629 {
2630 if (selLeftAbovePos_.order == -1) return;
2631
2632 bt_->expandPattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack,
2633 selLeftAbovePos_.order, selLeftAbovePos_.step,
2634 visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step);
2635 comStack_.lock()->push(new ExpandPatternQtCommand(this));
2636 }
2637
onShrinkPressed()2638 void PatternEditorPanel::onShrinkPressed()
2639 {
2640 if (selLeftAbovePos_.order == -1) return;
2641
2642 bt_->shrinkPattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack,
2643 selLeftAbovePos_.order, selLeftAbovePos_.step,
2644 visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step);
2645 comStack_.lock()->push(new ShrinkPatternQtCommand(this));
2646 }
2647
onInterpolatePressed()2648 void PatternEditorPanel::onInterpolatePressed()
2649 {
2650 if (selLeftAbovePos_.order == -1) return;
2651
2652 bt_->interpolatePattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack,
2653 selLeftAbovePos_.order, selLeftAbovePos_.step,
2654 visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step);
2655 comStack_.lock()->push(new InterpolatePatternQtCommand(this));
2656 }
2657
onReversePressed()2658 void PatternEditorPanel::onReversePressed()
2659 {
2660 if (selLeftAbovePos_.order == -1) return;
2661
2662 bt_->reversePattern(curSongNum_, visTracks_.at(selLeftAbovePos_.trackVisIdx), selLeftAbovePos_.colInTrack,
2663 selLeftAbovePos_.order, selLeftAbovePos_.step,
2664 visTracks_.at(selRightBelowPos_.trackVisIdx), selRightBelowPos_.colInTrack, selRightBelowPos_.step);
2665 comStack_.lock()->push(new ReversePatternQtCommand(this));
2666 }
2667
onReplaceInstrumentPressed()2668 void PatternEditorPanel::onReplaceInstrumentPressed()
2669 {
2670 if (selLeftAbovePos_.order == -1) return;
2671
2672 int curInst = bt_->getCurrentInstrumentNumber();
2673 if (curInst == -1) return;
2674
2675 int beginTrackIdx = (selLeftAbovePos_.colInTrack < 2) ? selLeftAbovePos_.trackVisIdx : (selLeftAbovePos_.trackVisIdx + 1);
2676 int endTrackIdx = (selRightBelowPos_.colInTrack == 0) ? (selRightBelowPos_.trackVisIdx - 1) : selRightBelowPos_.trackVisIdx;
2677 if (beginTrackIdx <= endTrackIdx) {
2678 bt_->replaceInstrumentInPattern(curSongNum_,
2679 visTracks_.at(beginTrackIdx), selLeftAbovePos_.order, selLeftAbovePos_.step,
2680 visTracks_.at(endTrackIdx), selRightBelowPos_.step, curInst);
2681 comStack_.lock()->push(new ReplaceInstrumentInPatternQtCommand(this));
2682 }
2683 }
2684
onExpandEffectColumnPressed(int trackVisIdx)2685 void PatternEditorPanel::onExpandEffectColumnPressed(int trackVisIdx)
2686 {
2687 size_t ti = static_cast<size_t>(trackVisIdx);
2688 if (rightEffn_.at(ti) == 3) return;
2689 bt_->setEffectDisplayWidth(curSongNum_, visTracks_.at(trackVisIdx), static_cast<size_t>(++rightEffn_[ti]));
2690 updateTracksWidthFromLeftToEnd();
2691
2692 if (config_->getMoveCursorByHorizontalScroll()) {
2693 emit effectColsCompanded(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack), getFullColmunSize());
2694 }
2695 else {
2696 emit effectColsCompanded(leftTrackVisIdx_, getScrollableCountByTrack());
2697 }
2698
2699 redrawAll();
2700 }
2701
onShrinkEffectColumnPressed(int trackVisIdx)2702 void PatternEditorPanel::onShrinkEffectColumnPressed(int trackVisIdx)
2703 {
2704 size_t ti = static_cast<size_t>(trackVisIdx);
2705 if (rightEffn_.at(ti) == 0) return;
2706 bt_->setEffectDisplayWidth(curSongNum_, visTracks_.at(trackVisIdx), static_cast<size_t>(--rightEffn_[ti]));
2707 updateTracksWidthFromLeftToEnd();
2708
2709 if (config_->getMoveCursorByHorizontalScroll()) {
2710 emit effectColsCompanded(calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack), getFullColmunSize());
2711 }
2712 else {
2713 emit effectColsCompanded(leftTrackVisIdx_, getScrollableCountByTrack());
2714 }
2715
2716 redrawAll();
2717 }
2718
onFollowModeChanged()2719 void PatternEditorPanel::onFollowModeChanged()
2720 {
2721 curPos_.setRows(bt_->getCurrentOrderNumber(), bt_->getCurrentStepNumber());
2722
2723 emit vScrollBarChangeRequested(
2724 curPos_.step, static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, curPos_.order)) - 1);
2725
2726 // Force redraw all area
2727 followModeChanged_ = true;
2728 redrawPatterns();
2729 }
2730
onChangeValuesPressed(int value)2731 void PatternEditorPanel::onChangeValuesPressed(int value)
2732 {
2733 if (bt_->isJamMode()) return;
2734
2735 if (selLeftAbovePos_.order != -1)
2736 changeValuesInPattern(selLeftAbovePos_, selRightBelowPos_, value);
2737 else
2738 changeValuesInPattern(curPos_, curPos_, value);
2739 }
2740
onPlayStepPressed()2741 void PatternEditorPanel::onPlayStepPressed()
2742 {
2743 moveCursorToDown(1);
2744 }
2745
2746 /********** Events **********/
event(QEvent * event)2747 bool PatternEditorPanel::event(QEvent *event)
2748 {
2749 switch (event->type()) {
2750 case QEvent::KeyPress:
2751 return keyPressed(dynamic_cast<QKeyEvent*>(event));
2752 case QEvent::KeyRelease:
2753 return keyReleased(dynamic_cast<QKeyEvent*>(event));
2754 case QEvent::HoverMove:
2755 return mouseHoverd(dynamic_cast<QHoverEvent*>(event));
2756 default:
2757 return QWidget::event(event);
2758 }
2759 }
2760
keyPressed(QKeyEvent * event)2761 bool PatternEditorPanel::keyPressed(QKeyEvent *event)
2762 {
2763 /* General Keys */
2764 switch (event->key()) {
2765 case Qt::Key_Shift:
2766 shiftPressedPos_ = curPos_;
2767 return true;
2768 case Qt::Key_Tab:
2769 if (curPos_.trackVisIdx == static_cast<int>(visTracks_.size()) - 1) {
2770 if (config_->getWarpCursor())
2771 moveCursorToRight(-calculateColNumInRow(curPos_.trackVisIdx, curPos_.colInTrack));
2772 }
2773 else {
2774 moveCursorToRight(5 + 2 * rightEffn_[static_cast<size_t>(curPos_.trackVisIdx)] - curPos_.colInTrack);
2775 }
2776 return true;
2777 case Qt::Key_Backtab:
2778 if (curPos_.trackVisIdx == 0) {
2779 if (config_->getWarpCursor())
2780 moveCursorToRight(getFullColmunSize() - 1);
2781 }
2782 else {
2783 moveCursorToRight(-5 - 2 * rightEffn_[static_cast<size_t>(curPos_.trackVisIdx) - 1] - curPos_.colInTrack);
2784 }
2785 return true;
2786 case Qt::Key_Insert:
2787 if (bt_->isJamMode()) {
2788 return false;
2789 }
2790 else {
2791 insertStep();
2792 return true;
2793 }
2794 case Qt::Key_Backspace:
2795 if (bt_->isJamMode()) {
2796 return false;
2797 }
2798 else {
2799 deletePreviousStep();
2800 return true;
2801 }
2802 case Qt::Key_Menu:
2803 {
2804 QPoint point = calculateCurrentCursorPosition();
2805 point.setX(point.x() + 24);
2806 point.setY(point.y() - 16);
2807 showPatternContextMenu(curPos_, point);
2808 return true;
2809 }
2810 default:
2811 if (!bt_->isJamMode()) {
2812 // Pattern edit
2813 if (!config_->getKeyRepetition() && event->isAutoRepeat()) return false;
2814 switch (curPos_.colInTrack) {
2815 case 0:
2816 return enterToneData(event);
2817 case 1:
2818 if (event->modifiers().testFlag(Qt::NoModifier)) return enterInstrumentData(event->key());
2819 break;
2820 case 2:
2821 if (event->modifiers().testFlag(Qt::NoModifier)) return enterVolumeData(event->key());
2822 break;
2823 case 3:
2824 case 5:
2825 case 7:
2826 case 9:
2827 if (event->modifiers().testFlag(Qt::NoModifier)) return enterEffectID(event->key());
2828 break;
2829 case 4:
2830 case 6:
2831 case 8:
2832 case 10:
2833 if (event->modifiers().testFlag(Qt::NoModifier)) return enterEffectValue(event->key());
2834 break;
2835 }
2836 }
2837 return false;
2838 }
2839 }
2840
keyReleased(QKeyEvent * event)2841 bool PatternEditorPanel::keyReleased(QKeyEvent* event)
2842 {
2843 switch (event->key()) {
2844 case Qt::Key_Shift:
2845 shiftPressedPos_ = { -1, -1, -1, -1 };
2846 return true;
2847 default:
2848 return false;
2849 }
2850 }
2851
paintEvent(QPaintEvent * event)2852 void PatternEditorPanel::paintEvent(QPaintEvent *event)
2853 {
2854 if (bt_ && isInitedFirstMod_.load()) {
2855 // Check order size
2856 int odrSize = static_cast<int>(bt_->getOrderSize(curSongNum_));
2857 if (curPos_.order >= odrSize) curPos_.setRows(odrSize - 1, 0);
2858
2859 const QRect& area = event->rect();
2860 if (area.x() == 0 && area.y() == 0) {
2861 drawPattern(area);
2862 }
2863 else {
2864 drawPattern(rect());
2865 }
2866 }
2867 }
2868
resizeEvent(QResizeEvent * event)2869 void PatternEditorPanel::resizeEvent(QResizeEvent *event)
2870 {
2871 QWidget::resizeEvent(event);
2872 funcResize();
2873 redrawAll();
2874 }
2875
mousePressEvent(QMouseEvent * event)2876 void PatternEditorPanel::mousePressEvent(QMouseEvent *event)
2877 {
2878 Q_UNUSED(event)
2879
2880 mousePressPos_ = hovPos_;
2881 doubleClickPos_ = mousePressPos_;
2882 mouseReleasePos_ = { -1, -1, -1, -1 };
2883 isPressedPlus_ = false;
2884 isPressedMinus_ = false;
2885
2886 if (event->button() == Qt::LeftButton) {
2887 if (mousePressPos_.order == -2 && mousePressPos_.trackVisIdx >= 0) {
2888 int w = calculateTracksWidthWithRowNum(leftTrackVisIdx_, mousePressPos_.trackVisIdx - 1)
2889 + hdMuteToggleWidth_ + stepFontWidth_ / 2;
2890 if (w < event->pos().x() && event->pos().x() < w + hdEffCompandButtonWidth_ + stepFontWidth_) {
2891 if (event->pos().y() < headerHeight_ / 2) isPressedPlus_ = true;
2892 else isPressedMinus_ = true;
2893 }
2894 }
2895 selLeftAbovePos_ = { -1, -1, -1, -1 };
2896 selRightBelowPos_ = { -1, -1, -1, -1 };
2897 selectAllState_ = -1;
2898 emit selected(false);
2899 }
2900 }
2901
mouseMoveEvent(QMouseEvent * event)2902 void PatternEditorPanel::mouseMoveEvent(QMouseEvent* event)
2903 {
2904 if (event->buttons() & Qt::LeftButton) {
2905 if (mousePressPos_.trackVisIdx < 0 || mousePressPos_.order < 0) return; // Start point is out of range
2906
2907 if (hovPos_.trackVisIdx >= 0 && hovPos_.order >= 0) {
2908 setSelectedRectangle(mousePressPos_, hovPos_);
2909 }
2910
2911 if (event->x() < stepNumWidth_ && leftTrackVisIdx_ > 0) {
2912 if (config_->getMoveCursorByHorizontalScroll()) {
2913 moveCursorToRight(-(5 + 2 * rightEffn_.at(static_cast<size_t>(leftTrackVisIdx_) - 1)));
2914 }
2915 else {
2916 moveViewToRight(-1);
2917 }
2918 }
2919 else if (event->x() > geometry().width() - stepNumWidth_ && hovPos_.trackVisIdx != -1) {
2920 if (config_->getMoveCursorByHorizontalScroll()) {
2921 moveCursorToRight(5 + 2 * rightEffn_.at(static_cast<size_t>(leftTrackVisIdx_)));
2922 }
2923 else {
2924 moveViewToRight(1);
2925 }
2926 }
2927 if (event->pos().y() < headerHeight_ + stepFontHeight_) {
2928 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(-1);
2929 }
2930 else if (event->pos().y() > geometry().height() - stepFontHeight_) {
2931 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(1);
2932 }
2933 }
2934 }
2935
mouseReleaseEvent(QMouseEvent * event)2936 void PatternEditorPanel::mouseReleaseEvent(QMouseEvent* event)
2937 {
2938 mouseReleasePos_ = hovPos_;
2939
2940 switch (event->button()) {
2941 case Qt::LeftButton:
2942 if (mousePressPos_ == mouseReleasePos_) { // Jump cell
2943 if (hovPos_.order >= 0 && hovPos_.step >= 0
2944 && hovPos_.trackVisIdx >= 0 && hovPos_.colInTrack >= 0) {
2945 int horDif = calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack,
2946 hovPos_.trackVisIdx, hovPos_.colInTrack);
2947 int verDif = calculateStepDistance(curPos_.order, curPos_.step,
2948 hovPos_.order, hovPos_.step);
2949 moveCursorToRight(horDif);
2950 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) moveCursorToDown(verDif);
2951 }
2952 else if (hovPos_.order == -2 && hovPos_.trackVisIdx >= 0) { // Header
2953 if (isPressedPlus_) {
2954 onExpandEffectColumnPressed(hovPos_.trackVisIdx);
2955 }
2956 else if (isPressedMinus_) {
2957 onShrinkEffectColumnPressed(hovPos_.trackVisIdx);
2958 }
2959 else {
2960 toggleTrack(hovPos_.trackVisIdx);
2961 int horDif = calculateColumnDistance(curPos_.trackVisIdx, curPos_.colInTrack,
2962 hovPos_.trackVisIdx, 0);
2963 moveCursorToRight(horDif);
2964 }
2965 }
2966 else if (hovPos_.trackVisIdx == -2 && hovPos_.order >= 0 && hovPos_.step >= 0) { // Step number
2967 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) {
2968 int verDif = calculateStepDistance(curPos_.order, curPos_.step,
2969 hovPos_.order, hovPos_.step);
2970 moveCursorToDown(verDif);
2971 }
2972 }
2973 }
2974 break;
2975
2976 case Qt::RightButton: // Show context menu
2977 {
2978 if (mousePressPos_.order == -2) { // Header
2979 QMenu menu;
2980 // Leave Before Qt5.7.0 style due to windows xp
2981 QAction* toggle = menu.addAction(tr("To&ggle Track"));
2982 QObject::connect(toggle, &QAction::triggered, this, [&] { toggleTrack(mousePressPos_.trackVisIdx); });
2983 QAction* solo = menu.addAction(tr("&Solo Track"));
2984 QObject::connect(solo, &QAction::triggered, this, [&] { soloTrack(mousePressPos_.trackVisIdx); });
2985 QAction* unmute = menu.addAction(tr("&Unmute All Tracks"));
2986 QObject::connect(unmute, &QAction::triggered, this, &PatternEditorPanel::onUnmuteAllPressed);
2987 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
2988 toggle->setShortcutVisibleInContextMenu(true);
2989 solo->setShortcutVisibleInContextMenu(true);
2990 #endif
2991 auto shortcuts = config_->getShortcuts();
2992 toggle->setShortcut(strToKeySeq(shortcuts.at(Configuration::ToggleTrack)));
2993 solo->setShortcut(strToKeySeq(shortcuts.at(Configuration::SoloTrack)));
2994 if (mousePressPos_.trackVisIdx < 0) {
2995 toggle->setEnabled(false);
2996 solo->setEnabled(false);
2997 unmute->setEnabled(false);
2998 }
2999 menu.exec(mapToGlobal(event->pos()));
3000 }
3001 else { // Pattern
3002 showPatternContextMenu(mousePressPos_, event->pos());
3003 }
3004
3005 break;
3006 }
3007
3008 case Qt::XButton1:
3009 {
3010 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) {
3011 int order = curPos_.order - 1;
3012 if (order < 0) order = static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1;
3013 int step = std::min(
3014 curPos_.step,
3015 static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, order)) - 1);
3016 int d = calculateStepDistance(curPos_.order, curPos_.step, order, step);
3017 moveCursorToDown(d);
3018 }
3019 break;
3020 }
3021
3022 case Qt::XButton2:
3023 {
3024 if (!bt_->isPlaySong() || !bt_->isFollowPlay()) {
3025 int order = curPos_.order + 1;
3026 if (static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1 < order) order = 0;
3027 int step = std::min(
3028 curPos_.step,
3029 static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, order)) - 1);
3030 int d = calculateStepDistance(curPos_.order, curPos_.step, order, step);
3031 moveCursorToDown(d);
3032 }
3033 break;
3034 }
3035
3036 default:
3037 break;
3038 }
3039
3040 mousePressPos_ = { -1, -1, -1, -1 };
3041 mouseReleasePos_ = { -1, -1, -1, -1 };
3042 }
3043
mouseDoubleClickEvent(QMouseEvent * event)3044 void PatternEditorPanel::mouseDoubleClickEvent(QMouseEvent* event)
3045 {
3046 if (event->button() == Qt::LeftButton) {
3047 if (doubleClickPos_.order >= 0) {
3048 if (!config_->getDontSelectOnDoubleClick()) {
3049 if (doubleClickPos_.trackVisIdx >= 0) {
3050 onSelectPressed(4);
3051 return;
3052 }
3053 else if (doubleClickPos_.trackVisIdx == -2) {
3054 onSelectPressed(5);
3055 return;
3056 }
3057 }
3058 }
3059 else if (doubleClickPos_.order == -2) {
3060 if (doubleClickPos_.trackVisIdx >= 0 && !isPressedPlus_ && !isPressedMinus_) {
3061 bool flag = true;
3062 int trackCnt = static_cast<int>(songStyle_.trackAttribs.size());
3063 int clickedNum = visTracks_.at(doubleClickPos_.trackVisIdx);
3064 for (int t = 0; t < trackCnt; ++t) {
3065 if (t != clickedNum) flag &= bt_->isMute(t);
3066 }
3067 if (flag) onUnmuteAllPressed();
3068 else soloTrack(doubleClickPos_.trackVisIdx);
3069 return;
3070 }
3071 }
3072 }
3073
3074 // Else
3075 mousePressEvent(event);
3076 }
3077
mouseHoverd(QHoverEvent * event)3078 bool PatternEditorPanel::mouseHoverd(QHoverEvent *event)
3079 {
3080 QPoint pos = event->pos();
3081 PatternPosition oldPos = hovPos_;
3082
3083 // Detect Step
3084 if (pos.y() <= headerHeight_) {
3085 // Track header
3086 hovPos_.setRows(-2, -2);
3087 }
3088 else {
3089 if (pos.y() < curRowY_) {
3090 int tmpOdr = curPos_.order;
3091 int tmpStep = curPos_.step + (pos.y() - curRowY_) / stepFontHeight_ - 1;
3092 while (true) {
3093 if (tmpStep < 0) {
3094 if (tmpOdr == 0) {
3095 hovPos_.setRows(-1, -1);
3096 break;
3097 }
3098 else {
3099 tmpStep += bt_->getPatternSizeFromOrderNumber(curSongNum_, --tmpOdr);
3100 }
3101 }
3102 else {
3103 hovPos_.setRows(tmpOdr, tmpStep);
3104 break;
3105 }
3106 }
3107 }
3108 else {
3109 int tmpOdr = curPos_.order;
3110 int tmpStep = curPos_.step + (pos.y() - curRowY_) / stepFontHeight_;
3111 while (true) {
3112 int endStep = static_cast<int>(bt_->getPatternSizeFromOrderNumber(curSongNum_, tmpOdr));
3113 if (tmpStep < endStep) {
3114 hovPos_.setRows(tmpOdr, tmpStep);
3115 break;
3116 }
3117 else {
3118 if (tmpOdr == static_cast<int>(bt_->getOrderSize(curSongNum_)) - 1) {
3119 hovPos_.setRows(-1, -1);
3120 break;
3121 }
3122 else {
3123 ++tmpOdr;
3124 tmpStep -= endStep;
3125 }
3126 }
3127 }
3128 }
3129 }
3130
3131 // Detect column
3132 if (pos.x() <= stepNumWidth_) {
3133 // Row number
3134 hovPos_.setCols(-2, -2);
3135 }
3136 else {
3137 int tmpWidth = stepNumWidth_;
3138 for (int i = leftTrackVisIdx_; ; ) {
3139 tmpWidth += (toneNameWidth_ + widthSpaceDbl_);
3140 if (pos.x() <= tmpWidth) {
3141 hovPos_.setCols(i, 0);
3142 break;
3143 }
3144 tmpWidth += (instWidth_ + widthSpaceDbl_);
3145 if (pos.x() <= tmpWidth) {
3146 hovPos_.setCols(i, 1);
3147 break;
3148 }
3149 tmpWidth += (volWidth_ + widthSpaceDbl_);
3150 if (pos.x() <= tmpWidth) {
3151 hovPos_.setCols(i, 2);
3152 break;
3153 }
3154 bool flag = false;
3155 for (int j = 0; j <= rightEffn_.at(static_cast<size_t>(i)); ++j) {
3156 tmpWidth += (effIDWidth_ + widthSpace_);
3157 if (pos.x() <= tmpWidth) {
3158 hovPos_.setCols(i, 3 + 2 * j);
3159 flag = true;
3160 break;
3161 }
3162 tmpWidth += (effValWidth_ + widthSpace_);
3163 if (pos.x() <= tmpWidth) {
3164 hovPos_.setCols(i, 4 + 2 * j);
3165 flag = true;
3166 break;
3167 }
3168 }
3169 if (flag) break;
3170 ++i;
3171
3172 if (i == static_cast<int>(visTracks_.size())) {
3173 hovPos_.setCols(-1, -1);
3174 break;
3175 }
3176 }
3177 }
3178
3179 if (hovPos_ != oldPos) redrawByHoverChanged();
3180
3181 return true;
3182 }
3183
wheelEvent(QWheelEvent * event)3184 void PatternEditorPanel::wheelEvent(QWheelEvent *event)
3185 {
3186 if (bt_->isPlaySong() && bt_->isFollowPlay()) return;
3187 int cnt = event->angleDelta().y() / 120;
3188 if (event->modifiers().testFlag(Qt::ControlModifier)) {
3189 onNoteTransposePressed(cnt);
3190 }
3191 else if (event->modifiers().testFlag(Qt::ShiftModifier)) {
3192 onChangeValuesPressed(cnt);
3193 }
3194 else {
3195 moveCursorToDown(-cnt);
3196 }
3197 }
3198
leaveEvent(QEvent * event)3199 void PatternEditorPanel::leaveEvent(QEvent* event)
3200 {
3201 Q_UNUSED(event)
3202 // Clear mouse hover selection
3203 hovPos_ = { -1, -1, -1, -1 };
3204 }
3205
midiThreadReceivedEvent(double delay,const uint8_t * msg,size_t len,void * userData)3206 void PatternEditorPanel::midiThreadReceivedEvent(double delay, const uint8_t *msg, size_t len, void *userData)
3207 {
3208 PatternEditorPanel *self = reinterpret_cast<PatternEditorPanel *>(userData);
3209
3210 Q_UNUSED(delay)
3211
3212 // Note-On/Note-Off
3213 if (len == 3 && (msg[0] & 0xe0) == 0x80) {
3214 uint8_t status = msg[0];
3215 uint8_t key = msg[1];
3216 uint8_t velocity = msg[2];
3217 QMetaMethod method = self->metaObject()->method(self->midiKeyEventMethod_);
3218 method.invoke(self, Qt::QueuedConnection,
3219 Q_ARG(uchar, status), Q_ARG(uchar, key), Q_ARG(uchar, velocity));
3220 }
3221 }
3222
midiKeyEvent(uchar status,uchar key,uchar velocity)3223 void PatternEditorPanel::midiKeyEvent(uchar status, uchar key, uchar velocity)
3224 {
3225 if (!bt_->isJamMode()) {
3226 bool release = ((status & 0xf0) == 0x80) || velocity == 0;
3227 if (!release) {
3228 std::pair<int, Note> octaveAndNote = noteNumberToOctaveAndNote(static_cast<int>(key) - 12);
3229 setStepKeyOn(octaveAndNote.second, octaveAndNote.first);
3230 }
3231 }
3232 }
3233