From f99bc388791b10898c761e84c7e9c07cea47284c Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Mon, 23 Sep 2024 15:57:21 +0100 Subject: [PATCH] fix tests --- include/justly/justly.hpp | 22 +- src/chord/ChordsModel.cpp | 132 +- src/chord/ChordsModel.hpp | 7 +- src/interval/Interval.cpp | 2 +- src/note/NotesModel.cpp | 84 +- src/note/NotesModel.hpp | 8 +- src/note/SetNotesCells.cpp | 8 +- src/other/ItemModel.cpp | 65 +- src/other/ItemModel.hpp | 24 +- src/other/justly.cpp | 41 +- src/percussion/Percussion.cpp | 25 +- src/percussion/PercussionsModel.cpp | 79 +- src/percussion/PercussionsModel.hpp | 7 +- src/percussion/SetPercussionsCells.cpp | 2 +- .../PercussionInstrument.cpp | 4 +- src/song/SongEditor.cpp | 175 +- src/song/SongEditor.hpp | 6 +- tests/Tester.cpp | 1955 +++++++---------- tests/Tester.h | 136 +- 19 files changed, 1244 insertions(+), 1538 deletions(-) diff --git a/include/justly/justly.hpp b/include/justly/justly.hpp index f4d3405e..d3d161f8 100644 --- a/include/justly/justly.hpp +++ b/include/justly/justly.hpp @@ -4,12 +4,12 @@ #include #include - #include "justly/JUSTLY_EXPORT.hpp" struct SongEditor; +class QAbstractItemModel; class QModelIndex; -class QTableView; +class QAbstractItemView; class QWidget; void JUSTLY_EXPORT register_converters(); @@ -18,12 +18,17 @@ void JUSTLY_EXPORT show_song_editor(SongEditor *song_editor_pointer); void JUSTLY_EXPORT delete_song_editor(SongEditor *song_editor_pointer); [[nodiscard]] auto JUSTLY_EXPORT -get_table_view_pointer(const SongEditor *song_editor_pointer) -> QTableView *; - -void JUSTLY_EXPORT trigger_edit_notes(const SongEditor *song_editor_pointer, qsizetype chord_number); +get_table_view_pointer(const SongEditor *song_editor_pointer) -> QAbstractItemView *; -void JUSTLY_EXPORT trigger_edit_percussions(const SongEditor *song_editor_pointer, qsizetype chord_number); +[[nodiscard]] auto JUSTLY_EXPORT +get_chords_model_pointer(const SongEditor *song_editor_pointer) -> QAbstractItemModel *; +[[nodiscard]] auto JUSTLY_EXPORT +get_notes_model_pointer(const SongEditor *song_editor_pointer) -> QAbstractItemModel *; +[[nodiscard]] auto JUSTLY_EXPORT +get_percussions_model_pointer(const SongEditor *song_editor_pointer) -> QAbstractItemModel *; +void JUSTLY_EXPORT trigger_edit_notes(SongEditor *song_editor_pointer, qsizetype chord_number); +void JUSTLY_EXPORT trigger_edit_percussions(SongEditor *song_editor_pointer, qsizetype chord_number); void JUSTLY_EXPORT trigger_back_to_chords(const SongEditor *song_editor_pointer); [[nodiscard]] auto JUSTLY_EXPORT get_gain(const SongEditor *song_editor_pointer) @@ -48,8 +53,8 @@ void JUSTLY_EXPORT set_starting_tempo(const SongEditor *song_editor_pointer, double new_value); [[nodiscard]] auto JUSTLY_EXPORT create_editor( - const SongEditor *song_editor_pointer, QModelIndex index) -> QWidget *; -void JUSTLY_EXPORT set_editor(const SongEditor *song_editor_pointer, + const QAbstractItemView *table_view_pointer,QModelIndex index) -> QWidget *; +void JUSTLY_EXPORT set_editor(const QAbstractItemView *table_view_pointer, QWidget *cell_editor_pointer, QModelIndex index, const QVariant &new_value); @@ -59,6 +64,7 @@ void JUSTLY_EXPORT redo(const SongEditor *song_editor_pointer); void JUSTLY_EXPORT trigger_insert_after(const SongEditor *song_editor_pointer); void JUSTLY_EXPORT trigger_insert_into(const SongEditor *song_editor_pointer); void JUSTLY_EXPORT trigger_delete(const SongEditor *song_editor_pointer); +void JUSTLY_EXPORT trigger_remove_rows(const SongEditor *song_editor_pointer); void JUSTLY_EXPORT trigger_cut(const SongEditor *song_editor_pointer); void JUSTLY_EXPORT trigger_copy(const SongEditor *song_editor_pointer); diff --git a/src/chord/ChordsModel.cpp b/src/chord/ChordsModel.cpp index dfd66d7d..e36ee669 100644 --- a/src/chord/ChordsModel.cpp +++ b/src/chord/ChordsModel.cpp @@ -38,18 +38,20 @@ static const auto CONCERT_A_MIDI = 69; static const auto C_0_MIDI = 12; -static const auto C_SCALE = 0; -static const auto C_SHARP_SCALE = 1; -static const auto D_SCALE = 2; -static const auto E_FLAT_SCALE = 3; -static const auto E_SCALE = 4; -static const auto F_SCALE = 5; -static const auto F_SHARP_SCALE = 6; -static const auto G_SCALE = 7; -static const auto A_FLAT_SCALE = 8; -static const auto A_SCALE = 9; -static const auto B_FLAT_SCALE = 10; -static const auto B_SCALE = 11; +enum Degree { + c_degree = 0, + c_sharp_degree = 1, + d_degree = 2, + e_flat_degree = 3, + e_degree = 4, + f_degree = 5, + f_sharp_degree = 6, + g_degree = 7, + a_flat_degree = 8, + a_degree = 9, + b_flat_degree = 10, + b_degree = 11 +}; [[nodiscard]] static auto get_chord_column(const QModelIndex &index) -> ChordColumn { @@ -87,11 +89,8 @@ auto ChordsModel::columnCount(const QModelIndex & /*parent_index*/) const return NUMBER_OF_CHORD_COLUMNS; } -auto ChordsModel::headerData(int column, Qt::Orientation orientation, - int role) const -> QVariant { - if (role == Qt::DisplayRole) { - if (orientation == Qt::Horizontal) { - switch (to_chord_column(column)) { +auto ChordsModel::get_column_name(int column_number) const -> QString { + switch (to_chord_column(column_number)) { case chord_interval_column: return ChordsModel::tr("Interval"); case chord_beats_column: @@ -107,23 +106,10 @@ auto ChordsModel::headerData(int column, Qt::Orientation orientation, case chord_percussions_column: return ChordsModel::tr("Percussions"); } - } - if (orientation == Qt::Vertical) { - return column + 1; - } - } - // no horizontal headers - // no headers for other roles - return {}; } -auto ChordsModel::flags(const QModelIndex &index) const -> Qt::ItemFlags { - auto chord_column = get_chord_column(index); - if (chord_column == chord_notes_column || - chord_notes_column == chord_percussions_column) { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; +auto ChordsModel::is_column_editable(int column_number) const -> bool { + return column_number != chord_notes_column && column_number != chord_percussions_column; } auto get_key_text(const ChordsModel &chords_model, qsizetype last_chord_number, @@ -140,84 +126,86 @@ auto get_key_text(const ChordsModel &chords_model, qsizetype last_chord_number, auto difference_from_c = closest_midi - C_0_MIDI; auto octave = static_cast(floor(difference_from_c / HALFSTEPS_PER_OCTAVE)); - auto scale = + auto degree = static_cast(difference_from_c - octave * HALFSTEPS_PER_OCTAVE); auto cents = static_cast(round((midi_float - closest_midi) * CENTS_PER_HALFSTEP)); QString scale_text; - switch (scale) { - case C_SCALE: + Q_ASSERT(degree >= 0); + Q_ASSERT(degree <= 11); + switch (static_cast(degree)) { + case c_degree: scale_text = "C"; break; - case C_SHARP_SCALE: + case c_sharp_degree: scale_text = "C♯"; break; - case D_SCALE: + case d_degree: scale_text = "D"; break; - case E_FLAT_SCALE: + case e_flat_degree: scale_text = "E♭"; break; - case E_SCALE: + case e_degree: scale_text = "E"; break; - case F_SCALE: + case f_degree: scale_text = "F"; break; - case F_SHARP_SCALE: + case f_sharp_degree: scale_text = "F♯"; break; - case G_SCALE: + case g_degree: scale_text = "G"; break; - case A_FLAT_SCALE: + case a_flat_degree: scale_text = "A♭"; break; - case A_SCALE: + case a_degree: scale_text = "A"; break; - case B_FLAT_SCALE: + case b_flat_degree: scale_text = "B♭"; break; - case B_SCALE: + case b_degree: scale_text = "B"; break; - default: - Q_ASSERT(false); } QString result; QTextStream stream(&result); - stream << key << " Hz; " << scale_text << octave << " " - << (cents >= 0 ? "+" : "−") << " " << abs(cents) << " cents"; + stream << key << " Hz; " << scale_text << octave; + if (cents != 0) { + stream << " " << (cents >= 0 ? "+" : "−") << " " << abs(cents) << " cents"; + } return result; } auto ChordsModel::data(const QModelIndex &index, int role) const -> QVariant { Q_ASSERT(index.isValid()); - auto child_number = get_child_number(index); + auto row_number = get_row_number(index); if (role == Qt::StatusTipRole) { - return get_key_text(*this, child_number); + return get_key_text(*this, row_number); + } + if (role != Qt::DisplayRole && role != Qt::EditRole) { + return {}; } - if (role == Qt::DisplayRole || role == Qt::EditRole) { - const auto &chord = chords.at(child_number); - switch (get_chord_column(index)) { - case chord_interval_column: - return QVariant::fromValue(chord.interval); - case chord_beats_column: - return QVariant::fromValue(chord.beats); - case chord_velocity_ratio_column: - return QVariant::fromValue(chord.velocity_ratio); - case chord_tempo_ratio_column: - return QVariant::fromValue(chord.tempo_ratio); - case chord_words_column: - return chord.words; - case chord_notes_column: - return chord.notes.size(); - case chord_percussions_column: - return chord.percussions.size(); - } + const auto &chord = chords.at(row_number); + switch (get_chord_column(index)) { + case chord_interval_column: + return QVariant::fromValue(chord.interval); + case chord_beats_column: + return QVariant::fromValue(chord.beats); + case chord_velocity_ratio_column: + return QVariant::fromValue(chord.velocity_ratio); + case chord_tempo_ratio_column: + return QVariant::fromValue(chord.tempo_ratio); + case chord_words_column: + return chord.words; + case chord_notes_column: + return chord.notes.size(); + case chord_percussions_column: + return chord.percussions.size(); } - return {}; } auto ChordsModel::setData(const QModelIndex &index, const QVariant &new_value, @@ -226,7 +214,7 @@ auto ChordsModel::setData(const QModelIndex &index, const QVariant &new_value, if (role != Qt::EditRole) { return false; } - auto chord_number = get_child_number(index); + auto chord_number = get_row_number(index); const auto &chord = chords.at(chord_number); switch (get_chord_column(index)) { case chord_interval_column: diff --git a/src/chord/ChordsModel.hpp b/src/chord/ChordsModel.hpp index f85d490b..40bb8dac 100644 --- a/src/chord/ChordsModel.hpp +++ b/src/chord/ChordsModel.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "chord/Chord.hpp" @@ -36,10 +35,8 @@ struct ChordsModel : public ItemModel { [[nodiscard]] auto columnCount(const QModelIndex & /*parent_index*/) const -> int override; - [[nodiscard]] auto headerData(int column, Qt::Orientation orientation, - int role) const -> QVariant override; - [[nodiscard]] auto - flags(const QModelIndex &index) const -> Qt::ItemFlags override; + [[nodiscard]] auto get_column_name(int column_number) const -> QString override; + [[nodiscard]] auto is_column_editable(int column_number) const -> bool override; [[nodiscard]] auto data(const QModelIndex &index, int role) const -> QVariant override; [[nodiscard]] auto setData(const QModelIndex &index, diff --git a/src/interval/Interval.cpp b/src/interval/Interval.cpp index c710da08..c124f31c 100644 --- a/src/interval/Interval.cpp +++ b/src/interval/Interval.cpp @@ -13,7 +13,7 @@ auto Interval::operator==(const Interval &other_interval) const -> bool { auto interval_is_default(const Interval &interval) -> bool { return interval.numerator == 1 && interval.denominator == 1 && - interval.octave != 0; + interval.octave == 0; } auto interval_to_double(const Interval &interval) -> double { diff --git a/src/note/NotesModel.cpp b/src/note/NotesModel.cpp index 7bdd0e34..4c4b572f 100644 --- a/src/note/NotesModel.cpp +++ b/src/note/NotesModel.cpp @@ -59,36 +59,21 @@ auto NotesModel::columnCount(const QModelIndex & /*parent_index*/) const return NUMBER_OF_NOTE_COLUMNS; } -auto NotesModel::headerData(int column, Qt::Orientation orientation, - int role) const -> QVariant { - if (role == Qt::DisplayRole) { - if (orientation == Qt::Horizontal) { - switch (to_note_column(column)) { - case note_instrument_column: - return NotesModel::tr("Instrument"); - case note_interval_column: - return NotesModel::tr("Interval"); - case note_beats_column: - return NotesModel::tr("Beats"); - case note_velocity_ratio_column: - return NotesModel::tr("Velocity ratio"); - case note_tempo_ratio_column: - return NotesModel::tr("Tempo ratio"); - case note_words_column: - return NotesModel::tr("Words"); - } - } - if (orientation == Qt::Vertical) { - return column + 1; - } +auto NotesModel::get_column_name(int column_number) const -> QString { + switch (to_note_column(column_number)) { + case note_instrument_column: + return NotesModel::tr("Instrument"); + case note_interval_column: + return NotesModel::tr("Interval"); + case note_beats_column: + return NotesModel::tr("Beats"); + case note_velocity_ratio_column: + return NotesModel::tr("Velocity ratio"); + case note_tempo_ratio_column: + return NotesModel::tr("Tempo ratio"); + case note_words_column: + return NotesModel::tr("Words"); } - // no horizontal headers - // no headers for other roles - return {}; -} - -auto NotesModel::flags(const QModelIndex & /*index*/) const -> Qt::ItemFlags { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; } auto NotesModel::data(const QModelIndex &index, int role) const -> QVariant { @@ -97,28 +82,27 @@ auto NotesModel::data(const QModelIndex &index, int role) const -> QVariant { return get_key_text( *parent_chords_model_pointer, parent_chord_number, interval_to_double( - notes_pointer->at(get_child_number(index)).interval)); + notes_pointer->at(get_row_number(index)).interval)); } - if (role == Qt::DisplayRole || role == Qt::EditRole) { - Q_ASSERT(notes_pointer != nullptr); - const auto ¬e = notes_pointer->at(get_child_number(index)); - switch (get_note_column(index)) { - case note_instrument_column: - return QVariant::fromValue(note.instrument_pointer); - case note_interval_column: - return QVariant::fromValue(note.interval); - case note_beats_column: - return QVariant::fromValue(note.beats); - case note_velocity_ratio_column: - return QVariant::fromValue(note.velocity_ratio); - case note_tempo_ratio_column: - return QVariant::fromValue(note.tempo_ratio); - case note_words_column: - return note.words; - } + if (role != Qt::DisplayRole && role != Qt::EditRole) { + return {}; + } + Q_ASSERT(notes_pointer != nullptr); + const auto ¬e = notes_pointer->at(get_row_number(index)); + switch (get_note_column(index)) { + case note_instrument_column: + return QVariant::fromValue(note.instrument_pointer); + case note_interval_column: + return QVariant::fromValue(note.interval); + case note_beats_column: + return QVariant::fromValue(note.beats); + case note_velocity_ratio_column: + return QVariant::fromValue(note.velocity_ratio); + case note_tempo_ratio_column: + return QVariant::fromValue(note.tempo_ratio); + case note_words_column: + return note.words; } - // no data for other roles - return {}; } auto NotesModel::setData(const QModelIndex &index, const QVariant &new_value, @@ -128,7 +112,7 @@ auto NotesModel::setData(const QModelIndex &index, const QVariant &new_value, if (role != Qt::EditRole) { return false; } - auto note_number = get_child_number(index); + auto note_number = get_row_number(index); Q_ASSERT(notes_pointer != nullptr); const auto ¬e = notes_pointer->at(note_number); switch (get_note_column(index)) { diff --git a/src/note/NotesModel.hpp b/src/note/NotesModel.hpp index 7f7eed62..1170d17f 100644 --- a/src/note/NotesModel.hpp +++ b/src/note/NotesModel.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include "justly/NoteColumn.hpp" @@ -29,10 +29,8 @@ struct NotesModel : public ItemModel { [[nodiscard]] auto columnCount(const QModelIndex & /*parent_index*/) const -> int override; - [[nodiscard]] auto headerData(int column, Qt::Orientation orientation, - int role) const -> QVariant override; - [[nodiscard]] auto - flags(const QModelIndex & /*index*/) const -> Qt::ItemFlags override; + [[nodiscard]] auto get_column_name(int column_number) const -> QString override; + [[nodiscard]] auto data(const QModelIndex &index, int role) const -> QVariant override; [[nodiscard]] auto setData(const QModelIndex &index, diff --git a/src/note/SetNotesCells.cpp b/src/note/SetNotesCells.cpp index 9db7d5da..dc6b7520 100644 --- a/src/note/SetNotesCells.cpp +++ b/src/note/SetNotesCells.cpp @@ -14,6 +14,9 @@ static void replace_note_cells(NotesModel& notes_model, qsizetype first_note_number, NoteColumn left_column, NoteColumn right_column, const QList &new_notes) { + qInfo("here"); + qInfo() << "left column" << left_column; + qInfo() << "right column" << right_column; auto *notes_pointer = notes_model.notes_pointer; Q_ASSERT(notes_pointer != nullptr); auto number_of_notes = new_notes.size(); @@ -23,11 +26,14 @@ static void replace_note_cells(NotesModel& notes_model, const auto &new_note = new_notes.at(replace_number); for (auto note_column = left_column; note_column <= right_column; note_column = static_cast(note_column + 1)) { + qInfo("here"); + qInfo() << "column" << note_column; switch (note_column) { case note_instrument_column: note.instrument_pointer = new_note.instrument_pointer; break; case note_interval_column: + qInfo() << QVariant::fromValue(new_note.interval).toString(); note.interval = new_note.interval; break; case note_beats_column: @@ -74,5 +80,5 @@ void SetNotesCells::undo() { void SetNotesCells::redo() { replace_note_cells(*notes_model_pointer, first_note_number, left_column, - right_column, old_notes); + right_column, new_notes); } diff --git a/src/other/ItemModel.cpp b/src/other/ItemModel.cpp index cdb8dea0..e33d4677 100644 --- a/src/other/ItemModel.cpp +++ b/src/other/ItemModel.cpp @@ -8,44 +8,65 @@ #include "other/other.hpp" -auto get_child_number(const QModelIndex &index) -> qsizetype { +auto get_row_number(const QModelIndex &index) -> qsizetype { return to_qsizetype(index.row()); } ItemModel::ItemModel(QObject *parent_pointer_input) - : QAbstractTableModel(parent_pointer_input) { + : QAbstractTableModel(parent_pointer_input) {} + +auto ItemModel::headerData(int section, Qt::Orientation orientation, + int role) const -> QVariant { + if (role != Qt::DisplayRole) { + return {}; + } + switch (orientation) { + case Qt::Horizontal: + return get_column_name(section); + case Qt::Vertical: + return section + 1; + } +} + +auto ItemModel::flags(const QModelIndex &index) const -> Qt::ItemFlags { + auto uneditable = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + if (is_column_editable(index.column())) { + return uneditable | Qt::ItemIsEditable; + } + return uneditable; } -void ItemModel::edited_cells(qsizetype first_child_number, - qsizetype number_of_children, - int left_column, - int right_column) { +auto ItemModel::is_column_editable(int /*column_number*/) const -> bool { + return true; +} + +void ItemModel::edited_cells(qsizetype first_row_number, + qsizetype number_of_rows, int left_column, + int right_column) { emit dataChanged( - index(first_child_number, left_column), - index(first_child_number + number_of_children - 1, right_column), + index(first_row_number, left_column), + index(first_row_number + number_of_rows - 1, right_column), {Qt::DisplayRole, Qt::EditRole}); } -void ItemModel::begin_insert_rows(qsizetype first_child_number, - qsizetype number_of_children) { - beginInsertRows(QModelIndex(), static_cast(first_child_number), - static_cast(first_child_number + number_of_children) - 1); +void ItemModel::begin_insert_rows(qsizetype first_row_number, + qsizetype number_of_rows) { + beginInsertRows(QModelIndex(), static_cast(first_row_number), + static_cast(first_row_number + number_of_rows) - + 1); } void ItemModel::end_insert_rows() { endInsertRows(); } -void ItemModel::begin_remove_rows(qsizetype first_child_number, - qsizetype number_of_children) { - beginRemoveRows(QModelIndex(), static_cast(first_child_number), - static_cast(first_child_number + number_of_children) - 1); +void ItemModel::begin_remove_rows(qsizetype first_row_number, + qsizetype number_of_rows) { + beginRemoveRows(QModelIndex(), static_cast(first_row_number), + static_cast(first_row_number + number_of_rows) - + 1); } void ItemModel::end_remove_rows() { endRemoveRows(); } -void ItemModel::begin_reset_model() { - beginResetModel(); -} +void ItemModel::begin_reset_model() { beginResetModel(); } -void ItemModel::end_reset_model() { - endResetModel(); -} +void ItemModel::end_reset_model() { endResetModel(); } diff --git a/src/other/ItemModel.hpp b/src/other/ItemModel.hpp index bd74e819..bab04653 100644 --- a/src/other/ItemModel.hpp +++ b/src/other/ItemModel.hpp @@ -1,23 +1,37 @@ #pragma once #include +#include +#include +#include #include class QObject; -[[nodiscard]] auto get_child_number(const QModelIndex &index) -> qsizetype; +[[nodiscard]] auto get_row_number(const QModelIndex &index) -> qsizetype; struct ItemModel : public QAbstractTableModel { explicit ItemModel(QObject *parent_pointer_input = nullptr); + [[nodiscard]] auto headerData(int section, Qt::Orientation orientation, + int role) const -> QVariant override; + [[nodiscard]] auto + flags(const QModelIndex &index) const -> Qt::ItemFlags override; + [[nodiscard]] virtual auto + get_column_name(int column_number) const -> QString = 0; + [[nodiscard]] virtual auto + is_column_editable(int column_number) const -> bool; + // internal functions - void edited_cells(qsizetype first_child_number, qsizetype number_of_children, - int left_column, int right_column); + void edited_cells(qsizetype first_row_number, qsizetype number_of_rows, + int left_column, int right_column); - void begin_insert_rows(qsizetype first_child_number, qsizetype number_of_children); + void begin_insert_rows(qsizetype first_row_number, + qsizetype number_of_rows); void end_insert_rows(); - void begin_remove_rows(qsizetype first_child_number, qsizetype number_of_children); + void begin_remove_rows(qsizetype first_row_number, + qsizetype number_of_rows); void end_remove_rows(); void begin_reset_model(); diff --git a/src/other/justly.cpp b/src/other/justly.cpp index 6728d287..2c028c49 100644 --- a/src/other/justly.cpp +++ b/src/other/justly.cpp @@ -20,6 +20,7 @@ #include "instrument/Instrument.hpp" #include "interval/Interval.hpp" #include "note/NotesModel.hpp" +#include "percussion/PercussionsModel.hpp" #include "percussion_instrument/PercussionInstrument.hpp" #include "percussion_set/PercussionSet.hpp" #include "rational/Rational.hpp" @@ -90,22 +91,36 @@ void delete_song_editor(SongEditor *song_editor_pointer) { } auto get_table_view_pointer(const SongEditor *song_editor_pointer) - -> QTableView * { + -> QAbstractItemView * { Q_ASSERT(song_editor_pointer != nullptr); return song_editor_pointer->table_view_pointer; } +auto get_chords_model_pointer(const SongEditor *song_editor_pointer) -> QAbstractItemModel * { + Q_ASSERT(song_editor_pointer != nullptr); + return song_editor_pointer->chords_model_pointer; +}; + +auto get_notes_model_pointer(const SongEditor *song_editor_pointer) -> QAbstractItemModel * { + Q_ASSERT(song_editor_pointer != nullptr); + return song_editor_pointer->notes_model_pointer; +}; + +auto get_percussions_model_pointer(const SongEditor *song_editor_pointer) -> QAbstractItemModel * { + return song_editor_pointer->percussions_model_pointer; +}; + void trigger_edit_notes(SongEditor *song_editor_pointer, qsizetype chord_number) { Q_ASSERT(song_editor_pointer != nullptr); - song_editor_pointer->edit_notes(chord_number); + song_editor_pointer->trigger_edit_notes(chord_number); }; void trigger_edit_percussions(SongEditor *song_editor_pointer, qsizetype chord_number) { Q_ASSERT(song_editor_pointer != nullptr); - song_editor_pointer->edit_percussions(chord_number); + song_editor_pointer->trigger_edit_percussions(chord_number); }; -void trigger_back_to_chords(SongEditor *song_editor_pointer) { +void trigger_back_to_chords(const SongEditor *song_editor_pointer) { Q_ASSERT(song_editor_pointer != nullptr); song_editor_pointer->back_to_chords_action_pointer->trigger(); }; @@ -157,11 +172,9 @@ void set_starting_tempo(const SongEditor *song_editor_pointer, song_editor_pointer->starting_tempo_editor_pointer->setValue(new_value); } -auto create_editor(const SongEditor *song_editor_pointer, +auto create_editor(const QAbstractItemView *table_view_pointer, QModelIndex index) -> QWidget * { - Q_ASSERT(song_editor_pointer != nullptr); - - auto *table_view_pointer = song_editor_pointer->table_view_pointer; + Q_ASSERT(table_view_pointer != nullptr); auto *delegate_pointer = table_view_pointer->itemDelegate(); Q_ASSERT(delegate_pointer != nullptr); @@ -178,10 +191,10 @@ auto create_editor(const SongEditor *song_editor_pointer, return cell_editor_pointer; } -void set_editor(const SongEditor *song_editor_pointer, +void set_editor(const QAbstractItemView *table_view_pointer, QWidget *cell_editor_pointer, QModelIndex index, const QVariant &new_value) { - Q_ASSERT(song_editor_pointer != nullptr); + Q_ASSERT(table_view_pointer != nullptr); Q_ASSERT(cell_editor_pointer != nullptr); const auto *cell_editor_meta_object = cell_editor_pointer->metaObject(); @@ -191,11 +204,11 @@ void set_editor(const SongEditor *song_editor_pointer, cell_editor_meta_object->userProperty().name(), new_value); auto *delegate_pointer = - song_editor_pointer->table_view_pointer->itemDelegate(); + table_view_pointer->itemDelegate(); Q_ASSERT(delegate_pointer != nullptr); delegate_pointer->setModelData( - cell_editor_pointer, song_editor_pointer->chords_model_pointer, index); + cell_editor_pointer, table_view_pointer->model(), index); } void undo(const SongEditor *song_editor_pointer) { @@ -219,6 +232,10 @@ void trigger_delete(const SongEditor *song_editor_pointer) { Q_ASSERT(song_editor_pointer != nullptr); song_editor_pointer->delete_action_pointer->trigger(); }; +void trigger_remove_rows(const SongEditor *song_editor_pointer) { + Q_ASSERT(song_editor_pointer != nullptr); + song_editor_pointer->remove_rows_action_pointer->trigger(); +}; void trigger_cut(const SongEditor *song_editor_pointer) { Q_ASSERT(song_editor_pointer != nullptr); song_editor_pointer->cut_action_pointer->trigger(); diff --git a/src/percussion/Percussion.cpp b/src/percussion/Percussion.cpp index da752a39..bd70b7db 100644 --- a/src/percussion/Percussion.cpp +++ b/src/percussion/Percussion.cpp @@ -9,6 +9,7 @@ #include "justly/PercussionColumn.hpp" #include "other/other.hpp" +#include "percussion_instrument/PercussionInstrument.hpp" #include "percussion_set/PercussionSet.hpp" #include "rational/Rational.hpp" @@ -29,16 +30,16 @@ auto get_percussions_schema() -> nlohmann::json { {"description", "a percussion"}, {"properties", nlohmann::json( - {{"percussion_set", + {{"set", nlohmann::json( {{"type", "string"}, {"description", "the percussion set"}, {"enum", get_names(get_all_percussion_sets())}})}, - {"percussion_instrument", + {"instrument", nlohmann::json( {{"type", "string"}, {"description", "the percussion instrument"}, - {"enum", get_names(get_all_percussion_sets())}})}, + {"enum", get_names(get_all_percussion_instruments())}})}, {"beats", get_rational_schema("the number of beats")}, {"velocity_percent", get_rational_schema("velocity ratio")}, {"tempo_percent", @@ -59,13 +60,13 @@ auto percussions_to_json(const QList &percussions, const auto *percussion_set_pointer = percussion.percussion_set_pointer; Q_ASSERT(percussion_set_pointer != nullptr); - json_percussion["percussion_set"] = + json_percussion["set"] = percussion_set_pointer->name.toStdString(); const auto *percussion_instrument_pointer = percussion.percussion_instrument_pointer; Q_ASSERT(percussion_instrument_pointer != nullptr); - json_percussion["percussion_instrument"] = + json_percussion["instrument"] = percussion_instrument_pointer->name.toStdString(); const auto &beats = percussion.beats; @@ -94,19 +95,19 @@ void json_to_percussions(QList &new_percussions, std::back_inserter(new_percussions), [](const nlohmann::json &json_percussion) -> Percussion { Percussion percussion; - if (json_percussion.contains("percussion_set")) { - const auto &percussion_set_value = json_percussion["percussion_set"]; + if (json_percussion.contains("set")) { + const auto &percussion_set_value = json_percussion["set"]; Q_ASSERT(percussion_set_value.is_string()); percussion.percussion_set_pointer = get_percussion_set_pointer( QString::fromStdString(percussion_set_value.get())); } - if (json_percussion.contains("percussion_instrument")) { - const auto &percussion_value = - json_percussion["percussion_instrument"]; - Q_ASSERT(percussion_value.is_string()); + if (json_percussion.contains("instrument")) { + const auto &instrument_value = + json_percussion["instrument"]; + Q_ASSERT(instrument_value.is_string()); percussion.percussion_instrument_pointer = get_percussion_instrument_pointer( - QString::fromStdString(percussion_value.get())); + QString::fromStdString(instrument_value.get())); } if (json_percussion.contains("beats")) { percussion.beats = json_to_rational(json_percussion["beats"]); diff --git a/src/percussion/PercussionsModel.cpp b/src/percussion/PercussionsModel.cpp index 09117b71..384740a5 100644 --- a/src/percussion/PercussionsModel.cpp +++ b/src/percussion/PercussionsModel.cpp @@ -56,61 +56,40 @@ auto PercussionsModel::columnCount(const QModelIndex & /*parent_index*/) const return NUMBER_OF_PERCUSSION_COLUMNS; } -auto PercussionsModel::headerData(int column, Qt::Orientation orientation, - int role) const -> QVariant { - if (role == Qt::DisplayRole) { - if (orientation == Qt::Horizontal) { - switch (to_percussion_column(column)) { - case percussion_set_column: - return PercussionsModel::tr("Set"); - case percussion_instrument_column: - return PercussionsModel::tr("Instrument"); - case percussion_beats_column: - return PercussionsModel::tr("Beats"); - case percussion_velocity_ratio_column: - return PercussionsModel::tr("Velocity ratio"); - case percussion_tempo_ratio_column: - return PercussionsModel::tr("Tempo ratio"); - } - } - if (orientation == Qt::Vertical) { - return column + 1; - } +auto PercussionsModel::get_column_name(int column_number) const -> QString { + switch (to_percussion_column(column_number)) { + case percussion_set_column: + return PercussionsModel::tr("Set"); + case percussion_instrument_column: + return PercussionsModel::tr("Instrument"); + case percussion_beats_column: + return PercussionsModel::tr("Beats"); + case percussion_velocity_ratio_column: + return PercussionsModel::tr("Velocity ratio"); + case percussion_tempo_ratio_column: + return PercussionsModel::tr("Tempo ratio"); } - // no horizontal headers - // no headers for other roles - return {}; -} - -auto PercussionsModel::flags(const QModelIndex & /*index*/) const - -> Qt::ItemFlags { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; } auto PercussionsModel::data(const QModelIndex &index, int role) const -> QVariant { - auto child_number = get_child_number(index); - if (role == Qt::DisplayRole || role == Qt::EditRole) { - Q_ASSERT(percussions_pointer != nullptr); - const auto &percussion = percussions_pointer->at(child_number); - switch (get_percussion_column(index)) { - case percussion_set_column: - return QVariant::fromValue(percussion.percussion_set_pointer); - case percussion_instrument_column: - return QVariant::fromValue(percussion.percussion_instrument_pointer); - case (percussion_beats_column): - return QVariant::fromValue(percussion.beats); - case percussion_velocity_ratio_column: - return QVariant::fromValue(percussion.velocity_ratio); - case percussion_tempo_ratio_column: - return QVariant::fromValue(percussion.tempo_ratio); - default: - Q_ASSERT(false); - return {}; - } + if (role != Qt::DisplayRole && role != Qt::EditRole) { + return {}; + } + Q_ASSERT(percussions_pointer != nullptr); + const auto &percussion = percussions_pointer->at(get_row_number(index)); + switch (get_percussion_column(index)) { + case percussion_set_column: + return QVariant::fromValue(percussion.percussion_set_pointer); + case percussion_instrument_column: + return QVariant::fromValue(percussion.percussion_instrument_pointer); + case (percussion_beats_column): + return QVariant::fromValue(percussion.beats); + case percussion_velocity_ratio_column: + return QVariant::fromValue(percussion.velocity_ratio); + case percussion_tempo_ratio_column: + return QVariant::fromValue(percussion.tempo_ratio); } - // no data for other roles - return {}; } auto PercussionsModel::setData(const QModelIndex &index, @@ -120,7 +99,7 @@ auto PercussionsModel::setData(const QModelIndex &index, return false; } auto percussion_column = get_percussion_column(index); - auto percussion_number = get_child_number(index); + auto percussion_number = get_row_number(index); Q_ASSERT(percussions_pointer != nullptr); const auto &percussion = percussions_pointer->at(percussion_number); switch (percussion_column) { diff --git a/src/percussion/PercussionsModel.hpp b/src/percussion/PercussionsModel.hpp index 83484bf9..53f0512d 100644 --- a/src/percussion/PercussionsModel.hpp +++ b/src/percussion/PercussionsModel.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include "justly/PercussionColumn.hpp" @@ -27,10 +27,7 @@ struct PercussionsModel : public ItemModel { [[nodiscard]] auto columnCount(const QModelIndex &parent) const -> int override; - [[nodiscard]] auto headerData(int column, Qt::Orientation orientation, - int role) const -> QVariant override; - [[nodiscard]] auto - flags(const QModelIndex & /*index*/) const -> Qt::ItemFlags override; + [[nodiscard]] auto get_column_name(int column_number) const -> QString override; [[nodiscard]] auto data(const QModelIndex &index, int role) const -> QVariant override; [[nodiscard]] auto setData(const QModelIndex &index, diff --git a/src/percussion/SetPercussionsCells.cpp b/src/percussion/SetPercussionsCells.cpp index 99423646..ad9734be 100644 --- a/src/percussion/SetPercussionsCells.cpp +++ b/src/percussion/SetPercussionsCells.cpp @@ -77,5 +77,5 @@ void SetPercussionsCells::undo() { void SetPercussionsCells::redo() { replace_percussion_cells(*percussions_model_pointer, first_percussion_number, - left_column, right_column, old_percussions); + left_column, right_column, new_percussions); } diff --git a/src/percussion_instrument/PercussionInstrument.cpp b/src/percussion_instrument/PercussionInstrument.cpp index ec2d2151..b0c69381 100644 --- a/src/percussion_instrument/PercussionInstrument.cpp +++ b/src/percussion_instrument/PercussionInstrument.cpp @@ -8,7 +8,7 @@ auto get_all_percussion_instruments() -> const QList & { static const QList all_percussions({ PercussionInstrument( - {QString("Acoustic Bass Drum or Low Bass Drum"), 35}), + {QString("Acoustic or Low Bass Drum"), 35}), PercussionInstrument({QString("Acoustic Snare"), 38}), PercussionInstrument({QString("Belltree"), 84}), PercussionInstrument({QString("Castanets"), 85}), @@ -19,7 +19,7 @@ auto get_all_percussion_instruments() PercussionInstrument({QString("Crash Cymbal 2"), 57}), PercussionInstrument({QString("Drum sticks"), 31}), PercussionInstrument( - {QString("Electric Bass Drum or High Bass Drum"), 36}), + {QString("Electric or High Bass Drum"), 36}), PercussionInstrument({QString("Electric Snare or Rimshot"), 40}), PercussionInstrument({QString("Hand Clap"), 39}), PercussionInstrument({QString("High Floor Tom"), 43}), diff --git a/src/song/SongEditor.cpp b/src/song/SongEditor.cpp index 3ddc90e5..d7dc27ed 100644 --- a/src/song/SongEditor.cpp +++ b/src/song/SongEditor.cpp @@ -100,8 +100,6 @@ static const auto MIN_STARTING_TEMPO = 25; static const auto MAX_STARTING_TEMPO = 200; // mime types -static const auto CHORDS_MIME = "application/prs.chords+json"; -static const auto NOTES_MIME = "application/prs.notes+json"; static const auto CHORDS_CELLS_MIME = "application/prs.chords_cells+json"; static const auto NOTES_CELLS_MIME = "application/prs.notes_cells+json"; static const auto PERCUSSIONS_CELLS_MIME = @@ -149,12 +147,12 @@ static const unsigned int START_END_MILLISECONDS = 500; } [[nodiscard]] static auto -get_first_child_number(const QItemSelectionRange &range) -> qsizetype { +get_first_row_number(const QItemSelectionRange &range) -> qsizetype { return to_qsizetype(range.top()); } [[nodiscard]] static auto -get_number_of_children(const QItemSelectionRange &range) -> qsizetype { +get_number_of_rows(const QItemSelectionRange &range) -> qsizetype { return to_qsizetype(range.bottom() - range.top() + 1); } @@ -188,11 +186,11 @@ static void set_value_no_signals(QDoubleSpinBox *spin_box_pointer, template [[nodiscard]] static auto -copy_items(const QList &items, qsizetype first_child_number, - qsizetype number_of_children) -> QList { +copy_items(const QList &items, qsizetype first_row_number, + qsizetype number_of_rows) -> QList { QList copied; - std::copy(items.cbegin() + first_child_number, - items.cbegin() + first_child_number + number_of_children, + std::copy(items.cbegin() + first_row_number, + items.cbegin() + first_row_number + number_of_rows, std::back_inserter(copied)); return copied; } @@ -212,12 +210,6 @@ static void copy_json(const nlohmann::json &copied, const QString &mime_type) { [[nodiscard]] static auto get_mime_description(const QString &mime_type) -> QString { - if (mime_type == CHORDS_MIME) { - return SongEditor::tr("chords"); - } - if (mime_type == NOTES_MIME) { - return SongEditor::tr("notes"); - } if (mime_type == CHORDS_CELLS_MIME) { return SongEditor::tr("chords cells"); } @@ -440,8 +432,8 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) paste_action_pointer->setEnabled(false); connect(paste_action_pointer, &QAction::triggered, this, [this]() { - auto first_child_number = - get_first_child_number(get_only_range(table_view_pointer)); + auto first_row_number = + get_first_row_number(get_only_range(table_view_pointer)); if (current_model_type == chords_type) { const auto &chords = chords_model_pointer->chords; @@ -469,16 +461,16 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) auto number_of_chords = std::min({static_cast(json_chords.size()), - chords.size() - first_child_number}); + chords.size() - first_row_number}); QList new_chords; json_to_chords(new_chords, json_chords, number_of_chords); undo_stack_pointer->push( std::make_unique( - chords_model_pointer, first_child_number, + chords_model_pointer, first_row_number, to_chord_column(get_json_int(json_chords_cells, "left_column")), to_chord_column(get_json_int(json_chords_cells, "right_column")), - copy_items(chords, first_child_number, number_of_chords), + copy_items(chords, first_row_number, number_of_chords), std::move(new_chords)) .release()); } else if (current_model_type == notes_type) { @@ -508,15 +500,15 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) auto number_of_notes = std::min({static_cast(json_notes.size()), - notes_pointer->size() - first_child_number}); + notes_pointer->size() - first_row_number}); QList new_notes; json_to_notes(new_notes, json_notes, number_of_notes); undo_stack_pointer->push( std::make_unique( - notes_model_pointer, first_child_number, + notes_model_pointer, first_row_number, to_note_column(get_json_int(json_notes_cells, "left_column")), to_note_column(get_json_int(json_notes_cells, "right_column")), - copy_items(*notes_pointer, first_child_number, number_of_notes), + copy_items(*notes_pointer, first_row_number, number_of_notes), std::move(new_notes)) .release()); } else { @@ -543,25 +535,25 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) return; } - Q_ASSERT(json_percussions_cells.contains("chords")); - const auto &json_percussions = json_percussions_cells["chords"]; + Q_ASSERT(json_percussions_cells.contains("percussions")); + const auto &json_percussions = json_percussions_cells["percussions"]; - auto number_of_percurussions = + auto number_of_percussions = std::min({static_cast(json_percussions.size()), - percussions_pointer->size() - first_child_number}); + percussions_pointer->size() - first_row_number}); QList new_percussions; json_to_percussions(new_percussions, json_percussions, - number_of_percurussions); + number_of_percussions); undo_stack_pointer->push( std::make_unique( - percussions_model_pointer, first_child_number, + percussions_model_pointer, first_row_number, to_percussion_column( get_json_int(json_percussions_cells, "left_column")), to_percussion_column( get_json_int(json_percussions_cells, "right_column")), - copy_items((*percussions_pointer), first_child_number, - number_of_percurussions), + copy_items((*percussions_pointer), first_row_number, + number_of_percussions), std::move(new_percussions)) .release()); } @@ -599,24 +591,24 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) remove_rows_action_pointer->setShortcuts(QKeySequence::DeleteStartOfWord); connect(remove_rows_action_pointer, &QAction::triggered, this, [this]() { const auto &range = get_only_range(table_view_pointer); - auto first_child_number = get_first_child_number(range); - auto number_of_children = get_number_of_children(range); + auto first_row_number = get_first_row_number(range); + auto number_of_rows = get_number_of_rows(range); if (current_model_type == chords_type) { undo_stack_pointer->push( std::make_unique( - chords_model_pointer, first_child_number, - copy_items(chords_model_pointer->chords, first_child_number, - number_of_children)) + chords_model_pointer, first_row_number, + copy_items(chords_model_pointer->chords, first_row_number, + number_of_rows)) .release()); } else if (current_model_type == notes_type) { auto *notes_pointer = notes_model_pointer->notes_pointer; Q_ASSERT(notes_pointer != nullptr); undo_stack_pointer->push( - std::make_unique(notes_model_pointer, first_child_number, + std::make_unique(notes_model_pointer, first_row_number, copy_items(*notes_pointer, - first_child_number, - number_of_children)) + first_row_number, + number_of_rows)) .release()); } else { auto *percussions_pointer = @@ -624,9 +616,9 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) Q_ASSERT(percussions_pointer != nullptr); undo_stack_pointer->push( std::make_unique( - percussions_model_pointer, first_child_number, - copy_items((*percussions_pointer), first_child_number, - number_of_children)) + percussions_model_pointer, first_row_number, + copy_items((*percussions_pointer), first_row_number, + number_of_rows)) .release()); } }); @@ -665,19 +657,19 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) const auto &chords = chords_model_pointer->chords; const auto &range = get_only_range(table_view_pointer); - auto first_child_number = get_first_child_number(range); - auto number_of_children = get_number_of_children(range); + auto first_row_number = get_first_row_number(range); + auto number_of_rows = get_number_of_rows(range); stop_playing(); initialize_play(); if (current_model_type == chords_type) { - if (first_child_number > 0) { - for (qsizetype chord_number = 0; chord_number < first_child_number; + if (first_row_number > 0) { + for (qsizetype chord_number = 0; chord_number < first_row_number; chord_number = chord_number + 1) { modulate(chords.at(chord_number)); } } - play_chords(first_child_number, number_of_children); + play_chords(first_row_number, number_of_rows); } else if (current_model_type == notes_type) { if (current_chord_number > 0) { for (qsizetype chord_number = 0; chord_number < current_chord_number; @@ -687,8 +679,8 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) } const auto &chord = chords.at(current_chord_number); modulate(chord); - play_notes(current_chord_number, chord, first_child_number, - number_of_children); + play_notes(current_chord_number, chord, first_row_number, + number_of_rows); } else { if (current_chord_number > 0) { for (qsizetype chord_number = 0; chord_number < current_chord_number; @@ -698,8 +690,8 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) } const auto &chord = chords.at(current_chord_number); modulate(chord); - play_percussions(current_chord_number, chord, first_child_number, - number_of_children); + play_percussions(current_chord_number, chord, first_row_number, + number_of_rows); } }); play_menu_pointer->addAction(play_action_pointer); @@ -782,16 +774,13 @@ SongEditor::SongEditor(QWidget *parent_pointer, Qt::WindowFlags flags) connect(table_view_pointer, &QAbstractItemView::doubleClicked, this, [this](const QModelIndex &index) { - if (current_model_type == chords_type) { - auto row = index.row(); - auto column = index.column(); - if (column == chord_notes_column) { - undo_stack_pointer->push( - std::make_unique(this, row).release()); - } else if (column == chord_percussions_column) { - undo_stack_pointer->push( - std::make_unique(this, row).release()); - } + Q_ASSERT(current_model_type == chords_type); + auto row = index.row(); + auto column = index.column(); + if (column == chord_notes_column) { + trigger_edit_notes(row); + } else if (column == chord_percussions_column) { + trigger_edit_percussions(row); } }); @@ -865,6 +854,16 @@ void SongEditor::set_model(QAbstractItemModel *model_pointer) const { update_actions(); } +void SongEditor::trigger_edit_notes(qsizetype chord_number) { + undo_stack_pointer->push( + std::make_unique(this, chord_number).release()); +} + +void SongEditor::trigger_edit_percussions(qsizetype chord_number) { + undo_stack_pointer->push( + std::make_unique(this, chord_number).release()); +} + void SongEditor::edit_notes(qsizetype chord_number) { Q_ASSERT(current_model_type == chords_type); Q_ASSERT(notes_model_pointer->notes_pointer == nullptr); @@ -974,68 +973,68 @@ void SongEditor::set_starting_tempo(double new_value) { .release()); } -void SongEditor::insert_row(qsizetype child_number) { +void SongEditor::insert_row(qsizetype row_number) { if (current_model_type == chords_type) { undo_stack_pointer->push(std::make_unique( - chords_model_pointer, child_number, Chord()) + chords_model_pointer, row_number, Chord()) .release()); } else if (current_model_type == notes_type) { undo_stack_pointer->push( - std::make_unique(notes_model_pointer, child_number, Note()) + std::make_unique(notes_model_pointer, row_number, Note()) .release()); } else { undo_stack_pointer->push( std::make_unique(percussions_model_pointer, - child_number, Percussion()) + row_number, Percussion()) .release()); } } void SongEditor::delete_cells() { const auto &range = get_only_range(table_view_pointer); - auto first_child_number = get_first_child_number(range); - auto number_of_children = get_number_of_children(range); + auto first_row_number = get_first_row_number(range); + auto number_of_rows = get_number_of_rows(range); auto left_column = range.left(); auto right_column = range.right(); if (current_model_type == chords_type) { undo_stack_pointer->push( std::make_unique( - chords_model_pointer, first_child_number, + chords_model_pointer, first_row_number, to_chord_column(left_column), to_chord_column(right_column), - copy_items(chords_model_pointer->chords, first_child_number, - number_of_children), - QList(number_of_children)) + copy_items(chords_model_pointer->chords, first_row_number, + number_of_rows), + QList(number_of_rows)) .release()); } else if (current_model_type == notes_type) { auto *notes_pointer = notes_model_pointer->notes_pointer; Q_ASSERT(notes_pointer != nullptr); undo_stack_pointer->push( std::make_unique( - notes_model_pointer, first_child_number, + notes_model_pointer, first_row_number, to_note_column(left_column), to_note_column(right_column), - copy_items(*notes_pointer, first_child_number, number_of_children), - QList(number_of_children)) + copy_items(*notes_pointer, first_row_number, number_of_rows), + QList(number_of_rows)) .release()); } else { auto *percussions_pointer = percussions_model_pointer->percussions_pointer; Q_ASSERT(percussions_pointer != nullptr); undo_stack_pointer->push( std::make_unique( - percussions_model_pointer, first_child_number, + percussions_model_pointer, first_row_number, to_percussion_column(left_column), to_percussion_column(right_column), - copy_items((*percussions_pointer), first_child_number, - number_of_children), - QList(number_of_children)) + copy_items((*percussions_pointer), first_row_number, + number_of_rows), + QList(number_of_rows)) .release()); } } void SongEditor::copy() const { const auto &range = get_only_range(table_view_pointer); - auto first_child_number = get_first_child_number(range); - auto number_of_children = get_number_of_children(range); + auto first_row_number = get_first_row_number(range); + auto number_of_rows = get_number_of_rows(range); auto left_column = range.left(); auto right_column = range.right(); @@ -1044,8 +1043,8 @@ void SongEditor::copy() const { nlohmann::json({{"left_column", to_chord_column(left_column)}, {"right_column", to_chord_column(right_column)}, {"chords", chords_to_json(chords_model_pointer->chords, - first_child_number, - number_of_children)}}), + first_row_number, + number_of_rows)}}), CHORDS_CELLS_MIME); } else if (current_model_type == notes_type) { auto *notes_pointer = notes_model_pointer->notes_pointer; @@ -1053,8 +1052,8 @@ void SongEditor::copy() const { copy_json(nlohmann::json( {{"left_column", to_note_column(left_column)}, {"right_column", to_note_column(right_column)}, - {"notes", notes_to_json(*notes_pointer, first_child_number, - number_of_children)}}), + {"notes", notes_to_json(*notes_pointer, first_row_number, + number_of_rows)}}), NOTES_CELLS_MIME); } else { auto *percussions_pointer = percussions_model_pointer->percussions_pointer; @@ -1063,8 +1062,8 @@ void SongEditor::copy() const { {{"left_column", to_percussion_column(left_column)}, {"right_column", to_percussion_column(right_column)}, {"percussions", percussions_to_json((*percussions_pointer), - first_child_number, - number_of_children)}}), + first_row_number, + number_of_rows)}}), PERCUSSIONS_CELLS_MIME); } } @@ -1162,7 +1161,7 @@ void SongEditor::modulate(const Chord &chord) { current_tempo = current_tempo * rational_to_double(chord.tempo_ratio); } -void SongEditor::play_note(int channel_number, int16_t midi_number, +void SongEditor::play_note_or_percussion(int channel_number, int16_t midi_number, const Rational &beats, const Rational &velocity_ratio, const Rational &tempo_ratio, int time_offset, @@ -1226,7 +1225,7 @@ void SongEditor::play_notes(qsizetype chord_number, const Chord &chord, BEND_PER_HALFSTEP))); send_event_at(current_time + 1); - play_note(channel_number, midi_number, note.beats, note.velocity_ratio, + play_note_or_percussion(channel_number, midi_number, note.beats, note.velocity_ratio, note.tempo_ratio, 2, chord_number, note_index, SongEditor::tr("note")); } @@ -1254,7 +1253,7 @@ void SongEditor::play_percussions(qsizetype chord_number, const Chord &chord, percussion.percussion_instrument_pointer; Q_ASSERT(percussion_instrument_pointer != nullptr); - play_note(channel_number, percussion_instrument_pointer->midi_number, + play_note_or_percussion(channel_number, percussion_instrument_pointer->midi_number, percussion.beats, percussion.velocity_ratio, percussion.tempo_ratio, 1, chord_number, percussion_number, SongEditor::tr("percussion")); diff --git a/src/song/SongEditor.hpp b/src/song/SongEditor.hpp index 855efe83..67e6fb86 100644 --- a/src/song/SongEditor.hpp +++ b/src/song/SongEditor.hpp @@ -119,7 +119,9 @@ struct SongEditor : public QMainWindow { // mode methods void connect_model(const QAbstractItemModel *model_pointer) const; void set_model(QAbstractItemModel *model_pointer) const; + void trigger_edit_notes(qsizetype chord_number); void edit_notes(qsizetype chord_number); + void trigger_edit_percussions(qsizetype chord_number); void edit_percussions(qsizetype chord_number); void notes_to_chords(); void percussions_to_chords(); @@ -138,7 +140,7 @@ struct SongEditor : public QMainWindow { void set_starting_tempo(double new_value); // insert remove methods - void insert_row(qsizetype child_number); + void insert_row(qsizetype row_number); void delete_cells(); // copy paste methods @@ -158,7 +160,7 @@ struct SongEditor : public QMainWindow { int16_t preset_number) const; void update_final_time(double new_final_time); void modulate(const Chord &chord); - void play_note(int channel_number, int16_t midi_number, const Rational &beats, + void play_note_or_percussion(int channel_number, int16_t midi_number, const Rational &beats, const Rational &velocity_ratio, const Rational &tempo_ratio, int time_offset, int chord_number, int item_number, const QString &item_description); diff --git a/tests/Tester.cpp b/tests/Tester.cpp index fe284abc..f480c25e 100644 --- a/tests/Tester.cpp +++ b/tests/Tester.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -28,6 +27,10 @@ #include "justly/PercussionColumn.hpp" #include "justly/justly.hpp" +static const auto NUMBER_OF_CHORD_COLUMNS = 7; +static const auto NUMBER_OF_NOTE_COLUMNS = 6; +static const auto NUMBER_OF_PERCUSSION_COLUMNS = 5; + static const auto BIG_VELOCITY = 126; static const auto STARTING_KEY_1 = 401.0; @@ -41,12 +44,13 @@ static const auto STARTING_VELOCITY_2 = 80; static const auto WAIT_TIME = 500; -static const auto OVERLOAD_NUMBER = 15; +static const auto OVERLOAD_NUMBER = 17; static const auto NEW_GAIN_1 = 2; static const auto NEW_GAIN_2 = 3; -const auto A_MINUS_FREQUENCY = 216.8458; +static const auto A_MINUS_FREQUENCY = 216.8458; +static const auto A_PLUS_FREQUENCY = 223.2; static const auto A_FREQUENCY = 220.0; static const auto B_FLAT_FREQUENCY = 233.0819; static const auto B_FREQUENCY = 246.9417; @@ -60,33 +64,47 @@ static const auto F_SHARP_FREQUENCY = 369.9944; static const auto G_FREQUENCY = 391.9954; static const auto A_FLAT_FREQUENCY = 415.3047; +static const auto CHORDS_CELLS_MIME = "application/prs.chords_cells+json"; +static const auto NOTES_CELLS_MIME = "application/prs.notes_cells+json"; +static const auto PERCUSSIONS_CELLS_MIME = + "application/prs.percussions_cells+json"; + +// TODO: update instruments and sets static const auto *const SONG_TEXT = R""""({ "chords": [ - { - "notes": [ - {}, - {} - ] - }, - { - "notes": [ - {}, - {} - ] - }, + {}, { "beats": { - "numerator": 2 + "numerator": 3 }, "interval": { - "numerator": 2 + "numerator": 3 }, "notes": [ + { + "instrument": "Marimba" + }, + { + "beats": { + "numerator": 3 + }, + "instrument": "12-String Guitar", + "interval": { + "numerator": 3 + }, + "tempo_ratio": { + "numerator": 3 + }, + "velocity_ratio": { + "numerator": 3 + }, + "words": "1" + }, { "beats": { "denominator": 2 }, - "instrument": "5th Saw Wave", + "instrument": "Marimba", "interval": { "denominator": 2 }, @@ -95,15 +113,14 @@ static const auto *const SONG_TEXT = R""""({ }, "velocity_ratio": { "denominator": 2 - }, - "words": "1" + } }, { "beats": { "denominator": 2, "numerator": 3 }, - "instrument": "808 Tom", + "instrument": "Marimba", "interval": { "denominator": 2, "numerator": 3 @@ -115,68 +132,148 @@ static const auto *const SONG_TEXT = R""""({ "velocity_ratio": { "denominator": 2, "numerator": 3 - }, - "words": "2" - } - ], - "tempo_ratio": { - "numerator": 2 - }, - "velocity_ratio": { - "numerator": 2 - }, - "words": "0" - }, - { - "beats": { - "numerator": 2 - }, - "interval": { - "octave": 1 - }, - "notes": [ + } + }, { - "beats": { - "numerator": 2 - }, - "instrument": "Acoustic Bass", + "instrument": "Marimba", + "interval": { + "octave": 1 + } + }, + { + "instrument": "Marimba", + "interval": { + "numerator": 3, + "octave": 1 + } + }, + { + "instrument": "Marimba", "interval": { "numerator": 2, "octave": 1 + } + }, + { + "instrument": "Marimba", + "interval": { + "denominator": 2, + "numerator": 3, + "octave": 1 + } + } + ], + "percussions": [ + { + "instrument": "Tambourine", + "set": "Standard" + }, + { + "beats": { + "numerator": 3 }, + "instrument": "Acoustic or Low Bass Drum", + "set": "Brush", "tempo_ratio": { - "numerator": 2 + "numerator": 3 }, "velocity_ratio": { + "numerator": 3 + } + }, + { + "beats": { "denominator": 2 }, - "words": "4" + "instrument": "Tambourine", + "set": "Standard", + "tempo_ratio": { + "denominator": 2 + }, + "velocity_ratio": { + "denominator": 2 + } }, { "beats": { - "numerator": 2 - }, - "instrument": "12-String Guitar", - "interval": { "denominator": 2, - "octave": 1 + "numerator": 3 }, + "instrument": "Tambourine", + "set": "Standard", "tempo_ratio": { - "numerator": 2 + "denominator": 2, + "numerator": 3 }, "velocity_ratio": { - "numerator": 2 - }, - "words": "5" + "denominator": 2, + "numerator": 3 + } } ], "tempo_ratio": { - "numerator": 2 + "numerator": 3 }, "velocity_ratio": { + "numerator": 3 + }, + "words": "1" + }, + { + "beats": { + "denominator": 2 + }, + "interval": { + "denominator": 2 + }, + "tempo_ratio": { "denominator": 2 }, - "words": "3" + "velocity_ratio": { + "denominator": 2 + } + }, + { + "beats": { + "denominator": 2, + "numerator": 3 + }, + "interval": { + "denominator": 2, + "numerator": 3 + }, + "tempo_ratio": { + "denominator": 2, + "numerator": 3 + }, + "velocity_ratio": { + "denominator": 2, + "numerator": 3 + } + }, + { + "interval": { + "octave": 1 + } + }, + { + "interval": { + "numerator": 3, + "octave": 1 + } + }, + { + "interval": { + "denominator": 2, + "octave": 1 + } + }, + { + "interval": { + "denominator": 2, + "numerator": 3, + "octave": 1 + } } ], "gain": 1.0, @@ -185,6 +282,297 @@ static const auto *const SONG_TEXT = R""""({ "starting_velocity": 10.0 })""""; +struct TwoStringsRow { + const QString first_string; + const QString second_string; +}; + +struct ToStringRow { + const QModelIndex index; + const QString text; +}; + +struct CountRow { + const qsizetype chord_number; + const qsizetype number; +}; + +struct HeaderRow { + int column_number; + const QString column_name; +}; + +struct FlagRow { + int column_number; + bool is_editable; +}; + +struct FrequencyRow { + double frequency; + QString text; +}; + +struct TwoIndicesRow { + QModelIndex first_index; + QModelIndex second_index; +}; + +[[nodiscard]] static auto get_selector_pointer( + const QAbstractItemView *table_view_pointer) -> QItemSelectionModel * { + auto *selector_pointer = table_view_pointer->selectionModel(); + Q_ASSERT(selector_pointer != nullptr); + return selector_pointer; +} + +static void clear_selection(QItemSelectionModel *selector_pointer) { + Q_ASSERT(selector_pointer != nullptr); + selector_pointer->select(QModelIndex(), QItemSelectionModel::Clear); +} + +[[nodiscard]] static auto +get_indices(QAbstractItemModel *model_pointer, int item_number, + int number_of_columns) -> std::vector { + Q_ASSERT(model_pointer != nullptr); + + std::vector indices; + for (auto column_number = 0; column_number < number_of_columns; + column_number = column_number + 1) { + indices.push_back(model_pointer->index(item_number, column_number)); + } + return indices; +} + +[[nodiscard]] static auto +get_index_pairs(QAbstractItemModel *model_pointer, int first_row_number, + int second_row_number, + int number_of_columns) -> std::vector { + Q_ASSERT(model_pointer != nullptr); + + std::vector rows; + for (auto column_number = 0; column_number < number_of_columns; + column_number = column_number + 1) { + rows.push_back({model_pointer->index(first_row_number, column_number), + model_pointer->index(second_row_number, column_number)}); + } + return rows; +} + +static void test_column_flags_editable(const QAbstractItemModel *model_pointer, + int column, bool is_editable) { + Q_ASSERT(model_pointer != nullptr); + + qInfo() << is_editable; + auto uneditable_flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + QCOMPARE(model_pointer->index(0, column).flags(), is_editable ? (uneditable_flags | Qt::ItemIsEditable) : uneditable_flags); +} + +static void test_number_of_columns(const QAbstractItemModel *model_pointer, + int number_of_columns) { + QCOMPARE(model_pointer->columnCount(), number_of_columns); +} + +static void test_set_values(const SongEditor *song_editor_pointer, + const std::vector &rows) { + const auto* table_view_pointer = get_table_view_pointer(song_editor_pointer); + for (const auto &row : rows) { + + const auto ©_index = row.first_index; + const auto &paste_index = row.second_index; + qInfo() << copy_index.column(); + qInfo() << paste_index.column(); + + + auto copy_value = copy_index.data(); + auto paste_value = paste_index.data(); + QCOMPARE_NE(copy_value, paste_value); + + auto *cell_editor_pointer = create_editor(table_view_pointer, paste_index); + QCOMPARE(cell_editor_pointer->property( + cell_editor_pointer->metaObject()->userProperty().name()), + paste_value); + + set_editor(table_view_pointer, cell_editor_pointer, paste_index, + copy_value); + + QCOMPARE(paste_index.data(), copy_value); + undo(song_editor_pointer); + QCOMPARE(paste_index.data(), paste_value); + } +} + +static void test_get_unsupported_role(const QAbstractItemModel *model_pointer) { + Q_ASSERT(model_pointer != nullptr); + QCOMPARE(model_pointer->index(0, 0).data(Qt::DecorationRole), QVariant()); +} + +static void test_set_unsupported_role(QAbstractItemModel *model_pointer) { + Q_ASSERT(model_pointer != nullptr); + QVERIFY(!(model_pointer->setData(model_pointer->index(0, 1), QVariant(), + Qt::DecorationRole))); +}; + +static void test_column_headers(const QAbstractItemModel *model_pointer, + const std::vector &rows) { + Q_ASSERT(model_pointer != nullptr); + for (const auto &row : rows) { + QCOMPARE(model_pointer->headerData(row.column_number, Qt::Horizontal), + row.column_name); + } +} + +static void test_delete_cells(SongEditor *song_editor_pointer, + const std::vector &indices) { + auto *selector_pointer = + get_selector_pointer(get_table_view_pointer(song_editor_pointer)); + + for (const auto &index : indices) { + qInfo() << "Column number " << index.column(); + const auto &old_value = index.data(); + + selector_pointer->select(index, QItemSelectionModel::Select); + trigger_delete(song_editor_pointer); + clear_selection(selector_pointer); + + QCOMPARE_NE(index.data(), old_value); + undo(song_editor_pointer); + QCOMPARE(index.data(), old_value); + } +} + +static void test_copy_paste_cells(SongEditor *song_editor_pointer, + const std::vector &rows) { + auto *selector_pointer = + get_selector_pointer(get_table_view_pointer(song_editor_pointer)); + + for (const auto &row : rows) { + const auto ©_index = row.first_index; + const auto &paste_index = row.second_index; + + auto copy_value = copy_index.data(); + auto paste_value = paste_index.data(); + + QCOMPARE_NE(copy_value, paste_value); + + selector_pointer->select(copy_index, QItemSelectionModel::Select); + trigger_copy(song_editor_pointer); + clear_selection(selector_pointer); + + selector_pointer->select(paste_index, QItemSelectionModel::Select); + trigger_paste_cells_or_after(song_editor_pointer); + clear_selection(selector_pointer); + + QCOMPARE(paste_index.data(), copy_value); + undo(song_editor_pointer); + QCOMPARE(paste_index.data(), paste_value); + } +} + +static void test_cut_paste_cells(SongEditor *song_editor_pointer, + const std::vector &rows) { + auto *selector_pointer = + get_selector_pointer(get_table_view_pointer(song_editor_pointer)); + + for (const auto &row : rows) { + const auto &cut_index = row.first_index; + const auto &paste_index = row.second_index; + + const auto cut_value = cut_index.data(); + const auto paste_value = paste_index.data(); + QCOMPARE_NE(cut_value, paste_value); + + selector_pointer->select(cut_index, QItemSelectionModel::Select); + trigger_cut(song_editor_pointer); + clear_selection(selector_pointer); + + QCOMPARE_NE(cut_index.data(), cut_value); + + selector_pointer->select(paste_index, QItemSelectionModel::Select); + trigger_paste_cells_or_after(song_editor_pointer); + clear_selection(selector_pointer); + + QCOMPARE(paste_index.data(), cut_value); + undo(song_editor_pointer); + QCOMPARE(paste_index.data(), paste_value); + undo(song_editor_pointer); + QCOMPARE(cut_index.data(), cut_value); + } +} + +static void test_insert_into(SongEditor *song_editor_pointer, + QAbstractItemModel *model_pointer) { + Q_ASSERT(model_pointer != nullptr); + + auto *selector_pointer = + get_selector_pointer(get_table_view_pointer(song_editor_pointer)); + + auto chord_index = model_pointer->index(0, chord_interval_column); + auto old_row_count = model_pointer->rowCount(chord_index); + selector_pointer->select(chord_index, QItemSelectionModel::Select); + trigger_insert_into(song_editor_pointer); + clear_selection(selector_pointer); + + QCOMPARE(model_pointer->rowCount(chord_index), old_row_count + 1); + undo(song_editor_pointer); + QCOMPARE(model_pointer->rowCount(chord_index), old_row_count); +} + +static void test_insert_after(SongEditor *song_editor_pointer, + QAbstractItemModel *model_pointer) { + Q_ASSERT(model_pointer != nullptr); + + auto *selector_pointer = + get_selector_pointer(get_table_view_pointer(song_editor_pointer)); + + auto index = model_pointer->index(0, 0); + auto old_row_count = model_pointer->rowCount(); + + selector_pointer->select(index, QItemSelectionModel::Select); + trigger_insert_after(song_editor_pointer); + clear_selection(selector_pointer); + + QCOMPARE(model_pointer->rowCount(), old_row_count + 1); + undo(song_editor_pointer); + QCOMPARE(model_pointer->rowCount(), old_row_count); +} + +static void test_remove_rows(SongEditor *song_editor_pointer, + QAbstractItemModel *model_pointer) { + Q_ASSERT(model_pointer != nullptr); + + auto *selector_pointer = + get_selector_pointer(get_table_view_pointer(song_editor_pointer)); + + auto index = model_pointer->index(0, 0); + auto old_row_count = model_pointer->rowCount(); + + selector_pointer->select(index, QItemSelectionModel::Select); + trigger_remove_rows(song_editor_pointer); + clear_selection(selector_pointer); + + QCOMPARE(model_pointer->rowCount(), old_row_count - 1); + undo(song_editor_pointer); + QCOMPARE(model_pointer->rowCount(), old_row_count); +} + +static void test_plays(SongEditor *song_editor_pointer, + const std::vector &rows) { + for (const auto &row : rows) { + auto *selector_pointer = + get_selector_pointer(get_table_view_pointer(song_editor_pointer)); + + selector_pointer->select(QItemSelection(row.first_index, row.second_index), + QItemSelectionModel::Select); + trigger_play(song_editor_pointer); + // first cut off early + trigger_play(song_editor_pointer); + // now play for a while + QThread::msleep(WAIT_TIME); + trigger_stop_playing(song_editor_pointer); + + clear_selection(selector_pointer); + } +} + void Tester::close_message_later(const QString &expected_text) { auto waiting_before = waiting_for_message; waiting_for_message = true; @@ -206,10 +594,6 @@ void Tester::close_message_later(const QString &expected_text) { QVERIFY(!waiting_before); } -void Tester::clear_selection() const { - selector_pointer->select(QModelIndex(), QItemSelectionModel::Clear); -} - void Tester::open_text(const QString &json_song) const { QTemporaryFile json_file; if (json_file.open()) { @@ -221,69 +605,121 @@ void Tester::open_text(const QString &json_song) const { Tester::Tester() : song_editor_pointer(make_song_editor()), - table_view_pointer(get_table_view_pointer(song_editor_pointer)) {} + table_view_pointer(get_table_view_pointer(song_editor_pointer)), + chords_model_pointer(get_chords_model_pointer(song_editor_pointer)), + notes_model_pointer(get_notes_model_pointer(song_editor_pointer)), + percussions_model_pointer( + get_percussions_model_pointer(song_editor_pointer)) {} -Tester::~Tester() { - delete_song_editor(song_editor_pointer); -} +Tester::~Tester() { delete_song_editor(song_editor_pointer); } -void Tester::initTestCase() { - QCOMPARE_NE(table_view_pointer, nullptr); - register_converters(); - open_text(SONG_TEXT); -} +void Tester::test_bad_pastes(const QModelIndex &index, + const std::vector &rows) { + auto *selector_pointer = get_selector_pointer(table_view_pointer); + + for (const auto &rows : rows) { + auto *new_data_pointer = std::make_unique().release(); + + Q_ASSERT(new_data_pointer != nullptr); + new_data_pointer->setData(rows.mime_type, + rows.copied.toStdString().c_str()); -void Tester::test_to_string_template() const { - QFETCH(const QModelIndex, index); - QFETCH(const QString, text); + auto *clipboard_pointer = QGuiApplication::clipboard(); + Q_ASSERT(clipboard_pointer != nullptr); + clipboard_pointer->setMimeData(new_data_pointer); - QCOMPARE(chords_model_pointer->data(index, Qt::EditRole).toString(), text); + selector_pointer->select(index, QItemSelectionModel::Select); + close_message_later(rows.error_message); + trigger_paste_cells_or_after(song_editor_pointer); + clear_selection(selector_pointer); + } } -void Tester::test_to_string_template_data() const { - QTest::addColumn("index"); - QTest::addColumn("text"); +void Tester::initTestCase() const { + register_converters(); + open_text(SONG_TEXT); +} - QTest::newRow("denominator interval") - << get_note_index(song_editor_pointer, 0, note_interval_column) << "/2"; - QTest::newRow("numerator octave interval") - << get_note_index(song_editor_pointer, 0, note_interval_column) << "2o1"; - QTest::newRow("denominator rational") - << get_note_index(song_editor_pointer, 0, note_beats_column) << "/2"; - QTest::newRow("numerator denominator rational") - << get_note_index(song_editor_pointer, 1, note_beats_column) << "3/2"; +void Tester::test_to_strings() const { + for (const auto &row : std::vector({ + ToStringRow( + {chords_model_pointer->index(0, chord_interval_column), ""}), + ToStringRow( + {chords_model_pointer->index(1, chord_interval_column), "3"}), + ToStringRow( + {chords_model_pointer->index(2, chord_interval_column), "/2"}), + ToStringRow( + {chords_model_pointer->index(3, chord_interval_column), "3/2"}), + ToStringRow( + {chords_model_pointer->index(4, chord_interval_column), "o1"}), + ToStringRow( + {chords_model_pointer->index(5, chord_interval_column), "3o1"}), + ToStringRow( + {chords_model_pointer->index(6, chord_interval_column), "/2o1"}), + ToStringRow( + {chords_model_pointer->index(7, chord_interval_column), "3/2o1"}), + ToStringRow( + {chords_model_pointer->index(0, chord_beats_column), ""}), + ToStringRow( + {chords_model_pointer->index(1, chord_beats_column), "3"}), + ToStringRow( + {chords_model_pointer->index(2, chord_beats_column), "/2"}), + ToStringRow( + {chords_model_pointer->index(3, chord_beats_column), "3/2"}), + })) { + QCOMPARE(row.index.data().toString(), row.text); + } } void Tester::test_chords_count() const { - QCOMPARE(table_view_pointer->model()->rowCount(), 4); + QCOMPARE(chords_model_pointer->rowCount(), 8); +} + +void Tester::test_notes_count() const { + for (const auto &row : std::vector({ + CountRow({0, 0}), + CountRow({1, 8}), + CountRow({2, 0}), + CountRow({3, 0}), + CountRow({4, 0}), + CountRow({5, 0}), + CountRow({6, 0}), + CountRow({7, 0}) + })) { + trigger_edit_notes(song_editor_pointer, row.chord_number); + QCOMPARE(notes_model_pointer->rowCount(), row.number); + undo(song_editor_pointer); + } } -void Tester::test_notes_count_template() const { - QFETCH(const int, chord_number); - QFETCH(const int, row_count); - trigger_edit_notes(song_editor_pointer, chord_number); - QCOMPARE(table_view_pointer->model()->rowCount(), row_count); - undo(song_editor_pointer); +void Tester::test_percussions_count() const { + for (const auto &row : std::vector({ + CountRow({0, 0}), + CountRow({1, 4}), + CountRow({2, 0}), + CountRow({3, 0}), + CountRow({4, 0}), + CountRow({5, 0}), + CountRow({6, 0}), + CountRow({7, 0}) + })) { + trigger_edit_percussions(song_editor_pointer, row.chord_number); + QCOMPARE(percussions_model_pointer->rowCount(), row.number); + undo(song_editor_pointer); + } } -void Tester::test_notes_count_template_data() const { - QTest::addColumn("index"); - QTest::addColumn("row_count"); +void Tester::test_number_of_chord_columns() const { + test_number_of_columns(chords_model_pointer, NUMBER_OF_CHORD_COLUMNS); +} - QTest::newRow("first chord") << 0 << 2; - QTest::newRow("second chord") << 1 << 2; - QTest::newRow("third chord") << 2 << 2; - QTest::newRow("fourth chord") << 3 << 2; +void Tester::test_number_of_note_columns() const { + test_number_of_columns(notes_model_pointer, NUMBER_OF_NOTE_COLUMNS); } -void Tester::test_column_count() const { - QCOMPARE(table_view_pointer->model()->columnCount(), 7); - trigger_edit_notes(song_editor_pointer, 0); - QCOMPARE(table_view_pointer->model()->columnCount(), 6); - undo(song_editor_pointer); - trigger_edit_percussions(song_editor_pointer, 0); - QCOMPARE(table_view_pointer->model()->columnCount(), 5); - undo(song_editor_pointer); +void Tester::test_number_of_percussion_columns() const { + test_number_of_columns(percussions_model_pointer, + NUMBER_OF_PERCUSSION_COLUMNS); } void Tester::test_gain_control() const { @@ -340,1116 +776,392 @@ void Tester::test_starting_tempo_control() const { QCOMPARE(get_starting_tempo(song_editor_pointer), old_tempo); } -void Tester::test_chord_column_headers_template() const { - QFETCH(const int, position); - QFETCH(const Qt::Orientation, orientation); - QFETCH(const Qt::ItemDataRole, role); - QFETCH(const QVariant, value); - - QCOMPARE(table_view_pointer->model()->headerData(position, orientation, role), value); -} - -void Tester::test_chord_column_headers_template_data() { - QTest::addColumn("position"); - QTest::addColumn("orientation"); - QTest::addColumn("role"); - QTest::addColumn("value"); - - QTest::newRow("interval") << chord_interval_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Interval"); - QTest::newRow("beats") << chord_beats_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Beats"); - QTest::newRow("velocity") << chord_velocity_ratio_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Velocity ratio"); - QTest::newRow("tempo") << chord_tempo_ratio_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Tempo ratio"); - QTest::newRow("words") << chord_words_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Words"); - QTest::newRow("notes") << chord_notes_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Notes"); - QTest::newRow("percussions") << chord_notes_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Percussions"); - QTest::newRow("rows") - << 0 << Qt::Vertical << Qt::DisplayRole << QVariant(1); - QTest::newRow("wrong role") - << chord_interval_column << Qt::Horizontal << Qt::DecorationRole << QVariant(); -} - - -void Tester::test_note_column_headers_template() const { - QFETCH(const int, position); - QFETCH(const Qt::Orientation, orientation); - QFETCH(const Qt::ItemDataRole, role); - QFETCH(const QVariant, value); - - trigger_edit_notes(song_editor_pointer, 0); - QCOMPARE(table_view_pointer->model()->headerData(position, orientation, role), value); +void Tester::test_row_headers() const { + QCOMPARE(chords_model_pointer->headerData(0, Qt::Vertical), QVariant(1)); + QCOMPARE( + chords_model_pointer->headerData(0, Qt::Vertical, Qt::DecorationRole), + QVariant()); +} + +void Tester::test_chord_column_headers() const { + test_column_headers( + chords_model_pointer, + std::vector({HeaderRow({chord_interval_column, "Interval"}), + HeaderRow({chord_beats_column, "Beats"}), + HeaderRow({chord_velocity_ratio_column, "Velocity ratio"}), + HeaderRow({chord_tempo_ratio_column, "Tempo ratio"}), + HeaderRow({chord_words_column, "Words"}), + HeaderRow({chord_notes_column, "Notes"}), + HeaderRow({chord_percussions_column, "Percussions"})})); +} + +void Tester::test_note_column_headers() const { + trigger_edit_notes(song_editor_pointer, 1); + test_column_headers( + notes_model_pointer, + std::vector({HeaderRow({note_instrument_column, "Instrument"}), + HeaderRow({note_interval_column, "Interval"}), + HeaderRow({note_beats_column, "Beats"}), + HeaderRow({note_velocity_ratio_column, "Velocity ratio"}), + HeaderRow({note_tempo_ratio_column, "Tempo ratio"}), + HeaderRow({note_words_column, "Words"})})); undo(song_editor_pointer); } -void Tester::test_note_column_headers_template_data() { - QTest::addColumn("position"); - QTest::addColumn("orientation"); - QTest::addColumn("role"); - QTest::addColumn("value"); - - QTest::newRow("instrument") << note_instrument_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Instrument"); - QTest::newRow("interval") << note_interval_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Interval"); - QTest::newRow("beats") << note_beats_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Beats"); - QTest::newRow("velocity") << note_velocity_ratio_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Velocity ratio"); - QTest::newRow("tempo") << note_tempo_ratio_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Tempo ratio"); - QTest::newRow("words") << note_words_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Words"); - QTest::newRow("rows") - << 0 << Qt::Vertical << Qt::DisplayRole << QVariant(1); - QTest::newRow("wrong role") - << note_instrument_column << Qt::Horizontal << Qt::DecorationRole << QVariant(); -} - - -void Tester::test_percussion_column_headers_template() const { - QFETCH(const int, position); - QFETCH(const Qt::Orientation, orientation); - QFETCH(const Qt::ItemDataRole, role); - QFETCH(const QVariant, value); - - trigger_edit_percussions(song_editor_pointer, 0); - QCOMPARE(table_view_pointer->model()->headerData(position, orientation, role), value); +void Tester::test_percussion_column_headers() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_column_headers( + percussions_model_pointer, + std::vector( + {HeaderRow({percussion_set_column, "Set"}), + HeaderRow({percussion_instrument_column, "Instrument"}), + HeaderRow({percussion_beats_column, "Beats"}), + HeaderRow({percussion_velocity_ratio_column, "Velocity ratio"}), + HeaderRow({percussion_tempo_ratio_column, "Tempo ratio"})})); undo(song_editor_pointer); } -void Tester::test_percussion_column_headers_template_data() { - QTest::addColumn("position"); - QTest::addColumn("orientation"); - QTest::addColumn("role"); - QTest::addColumn("value"); - - QTest::newRow("set") << percussion_set_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Set"); - QTest::newRow("instrument") << percussion_instrument_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Instrument"); - QTest::newRow("beats") << percussion_beats_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Beats"); - QTest::newRow("velocity") << percussion_velocity_ratio_column << Qt::Horizontal - << Qt::DisplayRole << QVariant("Velocity ratio"); - QTest::newRow("tempo") << percussion_tempo_ratio_column << Qt::Horizontal << Qt::DisplayRole - << QVariant("Tempo ratio"); - QTest::newRow("rows") - << 0 << Qt::Vertical << Qt::DisplayRole << QVariant(1); - QTest::newRow("wrong role") - << percussion_set_column << Qt::Horizontal << Qt::DecorationRole << QVariant(); -} - -static auto get_flags(const QTableView* table_view_pointer, int column) { - const auto* model_pointer = table_view_pointer->model(); - return model_pointer->flags(model_pointer->index(0, column)); +void Tester::test_chord_flags() const { + for (const auto &row : + std::vector({FlagRow({chord_interval_column, true}), + FlagRow({chord_notes_column, false}), + FlagRow({chord_percussions_column, false})})) { + test_column_flags_editable(chords_model_pointer, row.column_number, + row.is_editable); + } } -void Tester::test_flags() { - trigger_edit_notes(song_editor_pointer, 0); - QCOMPARE(get_flags(table_view_pointer, note_interval_column), Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); +void Tester::test_note_flags() const { + trigger_edit_notes(song_editor_pointer, 1); + test_column_flags_editable(notes_model_pointer, note_interval_column, true); undo(song_editor_pointer); +} - trigger_edit_percussions(song_editor_pointer, 0); - QCOMPARE(get_flags(table_view_pointer, percussion_set_column), Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); +void Tester::test_percussion_flags() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_column_flags_editable(percussions_model_pointer, percussion_set_column, + true); undo(song_editor_pointer); } -void Tester::test_chord_flags_template() const { - QFETCH(const int, column); - QFETCH(const Qt::ItemFlags, item_flags); - QCOMPARE(get_flags(table_view_pointer, column), item_flags); +void Tester::test_chord_frequencies() const { + for (const auto &row : std::vector({ + FrequencyRow({A_MINUS_FREQUENCY, "216.8 Hz; A3 − 25 cents"}), + FrequencyRow({A_PLUS_FREQUENCY, "223.2 Hz; A3 + 25 cents"}), + FrequencyRow({A_FREQUENCY, "220 Hz; A3"}), + FrequencyRow({B_FLAT_FREQUENCY, "233.1 Hz; B♭3"}), + FrequencyRow({B_FREQUENCY, "246.9 Hz; B3"}), + FrequencyRow({C_FREQUENCY, "261.6 Hz; C4"}), + FrequencyRow({C_SHARP_FREQUENCY, "277.2 Hz; C♯4"}), + FrequencyRow({D_FREQUENCY, "293.7 Hz; D4"}), + FrequencyRow({E_FLAT_FREQUENCY, "311.1 Hz; E♭4"}), + FrequencyRow({E_FREQUENCY, "329.6 Hz; E4"}), + FrequencyRow({F_FREQUENCY, "349.2 Hz; F4"}), + FrequencyRow({F_SHARP_FREQUENCY, "370 Hz; F♯4"}), + FrequencyRow({G_FREQUENCY, "392 Hz; G4"}), + FrequencyRow({A_FLAT_FREQUENCY, "415.3 Hz; A♭4"}), + })) { + set_starting_key(song_editor_pointer, row.frequency); + QCOMPARE(chords_model_pointer->index(0, chord_interval_column) + .data(Qt::StatusTipRole), + row.text); + undo(song_editor_pointer); + } } -void Tester::test_chord_flags_template_data() const { - QTest::addColumn("column"); - QTest::addColumn("item_flags"); - - QTest::newRow("interval") - << chord_interval_column - << (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); - QTest::newRow("notes") - << chord_notes_column - << (Qt::ItemIsEnabled | Qt::ItemIsSelectable); - QTest::newRow("percussions") - << chord_percussions_column - << (Qt::ItemIsEnabled | Qt::ItemIsSelectable); +void Tester::test_get_unsupported_chord_role() const { + test_get_unsupported_role(chords_model_pointer); } -void Tester::test_status_template() const { - QFETCH(const double, frequency); - QFETCH(const QString, text); - - const auto* model_pointer = table_view_pointer->model(); - - set_starting_key(song_editor_pointer, frequency); - QCOMPARE(model_pointer->data(model_pointer->index(0, chord_interval_column), - Qt::StatusTipRole), - text); +void Tester::test_get_unsupported_note_role() const { + trigger_edit_notes(song_editor_pointer, 1); + test_get_unsupported_role(notes_model_pointer); undo(song_editor_pointer); } -void Tester::test_status_template_data() { - QTest::addColumn("frequency"); - QTest::addColumn("text"); - - QTest::newRow("A minus") << A_MINUS_FREQUENCY << "216.8 Hz; A3 − 25 cents"; - QTest::newRow("A") << A_FREQUENCY << "220 Hz; A3 + 0 cents"; - QTest::newRow("Bb") << B_FLAT_FREQUENCY << "233.1 Hz; B♭3 + 0 cents"; - QTest::newRow("B") << B_FREQUENCY << "246.9 Hz; B3 + 0 cents"; - QTest::newRow("C") << C_FREQUENCY << "261.6 Hz; C4 + 0 cents"; - QTest::newRow("C#") << C_SHARP_FREQUENCY << "277.2 Hz; C♯4 + 0 cents"; - QTest::newRow("D") << D_FREQUENCY << "293.7 Hz; D4 + 0 cents"; - QTest::newRow("Eb") << E_FLAT_FREQUENCY << "311.1 Hz; E♭4 + 0 cents"; - QTest::newRow("E") << E_FREQUENCY << "329.6 Hz; E4 + 0 cents"; - QTest::newRow("F") << F_FREQUENCY << "349.2 Hz; F4 + 0 cents"; - QTest::newRow("F#") << F_SHARP_FREQUENCY << "370 Hz; F♯4 + 0 cents"; - QTest::newRow("G") << G_FREQUENCY << "392 Hz; G4 + 0 cents"; - QTest::newRow("Ab") << A_FLAT_FREQUENCY << "415.3 Hz; A♭4 + 0 cents"; -} - -void Tester::test_get_value_template() const { - QFETCH(const QModelIndex, index); - QFETCH(const Qt::ItemDataRole, role); - QFETCH(const QVariant, value); - QCOMPARE(chords_model_pointer->data(index, role), value); -} - -void Tester::test_get_value_template_data() const { - QTest::addColumn("index"); - QTest::addColumn("role"); - QTest::addColumn("value"); - QTest::newRow("first chord symbol") - << get_chord_index(song_editor_pointer, 0, chord_interval_column) << Qt::DisplayRole << QVariant("Chord"); - QTest::newRow("first chord decoration") - << get_chord_index(song_editor_pointer, 0, chord_interval_column) << Qt::DecorationRole << QVariant(); - QTest::newRow("first note symbol") - << get_note_index(song_editor_pointer, 0, note_interval_column) << Qt::DisplayRole << QVariant("Note"); - QTest::newRow("first note decoration") - << get_note_index(song_editor_pointer, 0, note_interval_column) << Qt::DecorationRole << QVariant(); +void Tester::test_get_unsupported_percussion_role() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_get_unsupported_role(percussions_model_pointer); + undo(song_editor_pointer); } -void Tester::test_background() const { - QCOMPARE_NE(chords_model_pointer->data(get_chord_index(song_editor_pointer, 0, chord_interval_column), - Qt::BackgroundRole), - chords_model_pointer->data(get_note_index(song_editor_pointer, 0, note_interval_column), - Qt::BackgroundRole)); +void Tester::test_set_chord_values() const { + test_set_values( + song_editor_pointer, + get_index_pairs(chords_model_pointer, 0, 1, NUMBER_OF_CHORD_COLUMNS - 2)); } -void Tester::test_delegate_template() const { - QFETCH(const QModelIndex, copy_index); - QFETCH(const QModelIndex, paste_index); - - auto copy_value = chords_model_pointer->data(copy_index, Qt::EditRole); - auto paste_value = chords_model_pointer->data(paste_index, Qt::EditRole); - QCOMPARE_NE(copy_value, paste_value); - - auto *cell_editor_pointer = create_editor(song_editor_pointer, paste_index); - - QCOMPARE(cell_editor_pointer->property( - cell_editor_pointer->metaObject()->userProperty().name()), - paste_value); - - set_editor(song_editor_pointer, cell_editor_pointer, paste_index, copy_value); - - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), copy_value); +void Tester::test_set_note_values() const { + trigger_edit_notes(song_editor_pointer, 1); + test_set_values( + song_editor_pointer, + get_index_pairs(notes_model_pointer, 0, 1, NUMBER_OF_NOTE_COLUMNS)); undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), paste_value); } -void Tester::test_delegate_template_data() const { - QTest::addColumn("copy_index"); - QTest::addColumn("paste_index"); - - QTest::newRow("instrument") - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << get_note_index(song_editor_pointer, 1, note_instrument_column); - QTest::newRow("interval") << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << get_chord_index(song_editor_pointer, 2, chord_interval_column); - QTest::newRow("beats") << get_chord_index(song_editor_pointer, 0, chord_beats_column) - << get_chord_index(song_editor_pointer, 2, chord_beats_column); - QTest::newRow("velocity ratio") - << get_chord_index(song_editor_pointer, 0, chord_velocity_ratio_column) - << get_chord_index(song_editor_pointer, 2, chord_velocity_ratio_column); - QTest::newRow("tempo ratio") - << get_chord_index(song_editor_pointer, 0, chord_tempo_ratio_column) - << get_chord_index(song_editor_pointer, 2, chord_tempo_ratio_column); - QTest::newRow("words") << get_chord_index(song_editor_pointer, 0, chord_words_column) - << get_chord_index(song_editor_pointer, 2, chord_words_column); +void Tester::test_set_percussion_values() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_set_values(song_editor_pointer, + get_index_pairs(percussions_model_pointer, 0, 1, + NUMBER_OF_PERCUSSION_COLUMNS)); + undo(song_editor_pointer); } -void Tester::test_no_set_value() const { - // setData only works for the edit role - QVERIFY(!(chords_model_pointer->setData(get_chord_index(song_editor_pointer, 0, chord_interval_column), - QVariant(), Qt::DecorationRole))); +void Tester::test_set_unsupported_chord_role() const { + test_set_unsupported_role(chords_model_pointer); } -void Tester::test_set_value_template() const { - QFETCH(const QModelIndex, copy_index); - QFETCH(const QModelIndex, paste_index); - - auto copy_value = chords_model_pointer->data(copy_index, Qt::EditRole); - auto paste_value = chords_model_pointer->data(paste_index, Qt::EditRole); - QCOMPARE_NE(copy_value, paste_value); - - QVERIFY(chords_model_pointer->setData(paste_index, copy_value, Qt::EditRole)); - - QCOMPARE(chords_model_pointer->data(paste_index, Qt::DisplayRole), - copy_value); - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), copy_value); - - undo(song_editor_pointer); - - QCOMPARE(chords_model_pointer->data(paste_index, Qt::DisplayRole), - paste_value); - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), paste_value); -} - -void Tester::test_set_value_template_data() const { - QTest::addColumn("copy_index"); - QTest::addColumn("paste_index"); - - QTest::newRow("chord interval") - << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << get_chord_index(song_editor_pointer, 2, chord_interval_column); - QTest::newRow("chord beats") << get_chord_index(song_editor_pointer, 0, chord_beats_column) - << get_chord_index(song_editor_pointer, 2, chord_beats_column); - QTest::newRow("chord velocity ratio") - << get_chord_index(song_editor_pointer, 0, chord_velocity_ratio_column) - << get_chord_index(song_editor_pointer, 2, chord_velocity_ratio_column); - QTest::newRow("chord tempo ratio") - << get_chord_index(song_editor_pointer, 0, chord_tempo_ratio_column) - << get_chord_index(song_editor_pointer, 2, chord_tempo_ratio_column); - QTest::newRow("chord words") << get_chord_index(song_editor_pointer, 0, chord_words_column) - << get_chord_index(song_editor_pointer, 2, chord_words_column); - QTest::newRow("note instrument") - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << get_note_index(song_editor_pointer, 0, note_instrument_column); - QTest::newRow("note interval") - << get_note_index(song_editor_pointer, 0, note_interval_column) - << get_note_index(song_editor_pointer, 0, note_interval_column); - QTest::newRow("note beats") << get_note_index(song_editor_pointer, 0, note_beats_column) - << get_note_index(song_editor_pointer, 0, note_beats_column); - QTest::newRow("note velocity") - << get_note_index(song_editor_pointer, 0, note_velocity_ratio_column) - << get_note_index(song_editor_pointer, 0, note_velocity_ratio_column); - QTest::newRow("note tempo") - << get_note_index(song_editor_pointer, 0, note_tempo_ratio_column) - << get_note_index(song_editor_pointer, 0, note_tempo_ratio_column); - QTest::newRow("note words") << get_note_index(song_editor_pointer, 0, note_words_column) - << get_note_index(song_editor_pointer, 0, note_words_column); -} - -void Tester::test_delete_cell_template() const { - QFETCH(const QModelIndex, index); - - auto old_value = chords_model_pointer->data(index, Qt::EditRole); - QVERIFY(!old_value.toString().isEmpty()); - - selector_pointer->select(index, QItemSelectionModel::Select); - trigger_delete(song_editor_pointer); - clear_selection(); - - QVERIFY(chords_model_pointer->data(index, Qt::EditRole).toString().isEmpty()); +void Tester::test_set_unsupported_note_role() const { + trigger_edit_notes(song_editor_pointer, 1); + test_set_unsupported_role(notes_model_pointer); undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->data(index, Qt::EditRole), old_value); -}; - -void Tester::test_delete_cell_template_data() const { - QTest::addColumn("index"); - - QTest::newRow("third chord interval") - << get_chord_index(song_editor_pointer, 2, chord_interval_column); - - QTest::newRow("third chord beats") - << get_chord_index(song_editor_pointer, 2, chord_beats_column); - - QTest::newRow("third chord words") - << get_chord_index(song_editor_pointer, 2, chord_words_column); - - QTest::newRow("third note instrument") - << get_note_index(song_editor_pointer, 0, note_instrument_column); -}; - -void Tester::test_delete_3_groups_template() const { - QFETCH(const QModelIndex, top_left_index_1); - QFETCH(const QModelIndex, bottom_right_index_1); - QFETCH(const QModelIndex, top_left_index_2); - QFETCH(const QModelIndex, bottom_right_index_2); - QFETCH(const QModelIndex, top_left_index_3); - QFETCH(const QModelIndex, bottom_right_index_3); - - auto top_left_data_1 = - chords_model_pointer->data(top_left_index_1, Qt::EditRole); - auto bottom_right_data_1 = - chords_model_pointer->data(bottom_right_index_1, Qt::EditRole); - auto top_left_data_2 = - chords_model_pointer->data(top_left_index_2, Qt::EditRole); - auto bottom_right_data_2 = - chords_model_pointer->data(bottom_right_index_2, Qt::EditRole); - auto top_left_data_3 = - chords_model_pointer->data(top_left_index_3, Qt::EditRole); - auto bottom_right_data_3 = - chords_model_pointer->data(bottom_right_index_3, Qt::EditRole); - - QVERIFY(!chords_model_pointer->data(top_left_index_1, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(!chords_model_pointer->data(bottom_right_index_1, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(!chords_model_pointer->data(top_left_index_2, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(!chords_model_pointer->data(bottom_right_index_2, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(!chords_model_pointer->data(top_left_index_3, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(!chords_model_pointer->data(bottom_right_index_3, Qt::EditRole) - .toString() - .isEmpty()); - - selector_pointer->select( - QItemSelection(top_left_index_1, bottom_right_index_1), - QItemSelectionModel::Select); - selector_pointer->select( - QItemSelection(top_left_index_2, bottom_right_index_2), - QItemSelectionModel::Select); - selector_pointer->select( - QItemSelection(top_left_index_3, bottom_right_index_3), - QItemSelectionModel::Select); - trigger_delete(song_editor_pointer); - clear_selection(); - - QVERIFY(chords_model_pointer->data(top_left_index_1, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(chords_model_pointer->data(bottom_right_index_1, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(chords_model_pointer->data(top_left_index_2, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(chords_model_pointer->data(bottom_right_index_2, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(chords_model_pointer->data(top_left_index_3, Qt::EditRole) - .toString() - .isEmpty()); - QVERIFY(chords_model_pointer->data(bottom_right_index_3, Qt::EditRole) - .toString() - .isEmpty()); - undo(song_editor_pointer); - - QCOMPARE(chords_model_pointer->data(top_left_index_1, Qt::EditRole), - top_left_data_1); - QCOMPARE(chords_model_pointer->data(bottom_right_index_1, Qt::EditRole), - bottom_right_data_1); - QCOMPARE(chords_model_pointer->data(top_left_index_2, Qt::EditRole), - top_left_data_2); - QCOMPARE(chords_model_pointer->data(bottom_right_index_2, Qt::EditRole), - bottom_right_data_2); - QCOMPARE(chords_model_pointer->data(top_left_index_3, Qt::EditRole), - top_left_data_3); - QCOMPARE(chords_model_pointer->data(bottom_right_index_3, Qt::EditRole), - bottom_right_data_3); -} - -void Tester::test_delete_3_groups_template_data() const { - QTest::addColumn("top_left_index_1"); - QTest::addColumn("bottom_right_index_1"); - QTest::addColumn("top_left_index_2"); - QTest::addColumn("bottom_right_index_2"); - QTest::addColumn("top_left_index_3"); - QTest::addColumn("bottom_right_index_3"); - - QTest::newRow("note chord note") - << get_note_index(song_editor_pointer, 1, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column) - << get_chord_index(song_editor_pointer, 3, chord_interval_column) - << get_chord_index(song_editor_pointer, 3, chord_beats_column) - << get_note_index(song_editor_pointer, 0, note_interval_column) - << get_note_index(song_editor_pointer, 0, note_beats_column); - - QTest::newRow("chord notes chord") - << get_chord_index(song_editor_pointer, 2, chord_interval_column) - << get_chord_index(song_editor_pointer, 2, chord_beats_column) - << get_note_index(song_editor_pointer, 0, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column) - << get_chord_index(song_editor_pointer, 3, chord_interval_column) - << get_chord_index(song_editor_pointer, 3, chord_beats_column); -} - -void Tester::test_paste_cell_template() const { - QFETCH(const QModelIndex, copy_index); - QFETCH(const QModelIndex, paste_index); - - auto copy_value = chords_model_pointer->data(copy_index, Qt::EditRole); - auto paste_value = chords_model_pointer->data(paste_index, Qt::EditRole); - - QCOMPARE_NE(copy_value, paste_value); - - selector_pointer->select(copy_index, QItemSelectionModel::Select); - trigger_copy(song_editor_pointer); - clear_selection(); - - selector_pointer->select(paste_index, QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), copy_value); - undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), paste_value); } -void Tester::test_paste_cell_template_data() const { - QTest::addColumn("copy_index"); - QTest::addColumn("paste_index"); - - QTest::newRow("chord interval") - << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << get_chord_index(song_editor_pointer, 2, chord_interval_column); - - QTest::newRow("chord beats") << get_chord_index(song_editor_pointer, 0, chord_beats_column) - << get_chord_index(song_editor_pointer, 2, chord_beats_column); - - QTest::newRow("chord tempo ratio") - << get_chord_index(song_editor_pointer, 0, chord_tempo_ratio_column) - << get_chord_index(song_editor_pointer, 2, chord_tempo_ratio_column); - - QTest::newRow("chord velocity ratio") - << get_chord_index(song_editor_pointer, 0, chord_velocity_ratio_column) - << get_chord_index(song_editor_pointer, 2, chord_velocity_ratio_column); - - QTest::newRow("chord words") << get_chord_index(song_editor_pointer, 0, chord_words_column) - << get_chord_index(song_editor_pointer, 2, chord_words_column); - - QTest::newRow("note interval") - << get_note_index(song_editor_pointer, 0, note_interval_column) - << get_note_index(song_editor_pointer, 0, note_interval_column); - - QTest::newRow("note beats") << get_note_index(song_editor_pointer, 0, note_beats_column) - << get_note_index(song_editor_pointer, 0, note_beats_column); - - QTest::newRow("note tempo ratio") - << get_note_index(song_editor_pointer, 0, note_tempo_ratio_column) - << get_note_index(song_editor_pointer, 0, note_tempo_ratio_column); - - QTest::newRow("note velocity ratio") - << get_note_index(song_editor_pointer, 0, note_velocity_ratio_column) - << get_note_index(song_editor_pointer, 0, note_velocity_ratio_column); - - QTest::newRow("note words") << get_note_index(song_editor_pointer, 0, note_words_column) - << get_note_index(song_editor_pointer, 0, note_words_column); - - QTest::newRow("note instrument") - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << get_note_index(song_editor_pointer, 0, note_instrument_column); -} - -void Tester::test_cut_paste_cell_template() const { - QFETCH(const QModelIndex, cut_index); - QFETCH(const QModelIndex, paste_index); - - const auto cut_value = chords_model_pointer->data(cut_index); - const auto paste_value = chords_model_pointer->data(paste_index); - - QVERIFY(!cut_value.toString().isEmpty()); - QCOMPARE_NE(cut_value, paste_value); - - selector_pointer->select(cut_index, QItemSelectionModel::Select); - trigger_cut(song_editor_pointer); - clear_selection(); - - QVERIFY(chords_model_pointer->data(cut_index).toString().isEmpty()); - - selector_pointer->select(paste_index, QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), cut_value); - undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), paste_value); +void Tester::test_set_unsupported_percussion_role() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_set_unsupported_role(percussions_model_pointer); undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->data(cut_index, Qt::EditRole), cut_value); } -void Tester::test_cut_paste_cell_template_data() const { - QTest::addColumn("cut_index"); - QTest::addColumn("paste_index"); - - QTest::newRow("chord interval") - << get_chord_index(song_editor_pointer, 2, chord_interval_column) - << get_chord_index(song_editor_pointer, 0, chord_interval_column); +void Tester::test_delete_chord_cells() const { + test_delete_cells(song_editor_pointer, get_indices(chords_model_pointer, 1, + NUMBER_OF_CHORD_COLUMNS)); } -void Tester::test_paste_3_groups_template() const { - QFETCH(const QModelIndex, copy_top_left_index_1); - QFETCH(const QModelIndex, copy_bottom_right_index_1); - QFETCH(const QModelIndex, copy_top_left_index_2); - QFETCH(const QModelIndex, copy_bottom_right_index_2); - QFETCH(const QModelIndex, copy_top_left_index_3); - QFETCH(const QModelIndex, copy_bottom_right_index_3); - - QFETCH(const QModelIndex, paste_top_left_index_1); - QFETCH(const QModelIndex, paste_bottom_right_index_1); - QFETCH(const QModelIndex, paste_top_left_index_2); - QFETCH(const QModelIndex, paste_bottom_right_index_2); - QFETCH(const QModelIndex, paste_top_left_index_3); - QFETCH(const QModelIndex, paste_bottom_right_index_3); - - auto copy_top_left_data = - chords_model_pointer->data(copy_top_left_index_1, Qt::EditRole); - auto copy_bottom_right_data = - chords_model_pointer->data(copy_bottom_right_index_3, Qt::EditRole); - - auto paste_top_left_data = - chords_model_pointer->data(paste_top_left_index_1, Qt::EditRole); - auto paste_bottom_right_data = - chords_model_pointer->data(paste_bottom_right_index_3, Qt::EditRole); - - QCOMPARE_NE(copy_top_left_data, paste_top_left_data); - QCOMPARE_NE(copy_bottom_right_data, paste_bottom_right_data); - - selector_pointer->select( - QItemSelection(copy_top_left_index_1, copy_bottom_right_index_1), - QItemSelectionModel::Select); - selector_pointer->select( - QItemSelection(copy_top_left_index_2, copy_bottom_right_index_2), - QItemSelectionModel::Select); - selector_pointer->select( - QItemSelection(copy_top_left_index_3, copy_bottom_right_index_3), - QItemSelectionModel::Select); - trigger_copy(song_editor_pointer); - clear_selection(); - - selector_pointer->select( - QItemSelection(paste_top_left_index_1, paste_bottom_right_index_1), - QItemSelectionModel::Select); - selector_pointer->select( - QItemSelection(paste_top_left_index_2, paste_bottom_right_index_2), - QItemSelectionModel::Select); - selector_pointer->select( - QItemSelection(paste_top_left_index_3, paste_bottom_right_index_3), - QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->data(paste_top_left_index_1, Qt::EditRole), - copy_top_left_data); - QCOMPARE(chords_model_pointer->data(paste_bottom_right_index_3, Qt::EditRole), - copy_bottom_right_data); +void Tester::test_delete_note_cells() const { + trigger_edit_notes(song_editor_pointer, 1); + test_delete_cells(song_editor_pointer, get_indices(notes_model_pointer, 1, + NUMBER_OF_NOTE_COLUMNS)); undo(song_editor_pointer); +} - QCOMPARE(chords_model_pointer->data(paste_top_left_index_1, Qt::EditRole), - paste_top_left_data); - QCOMPARE(chords_model_pointer->data(paste_bottom_right_index_3, Qt::EditRole), - paste_bottom_right_data); -} - -void Tester::test_paste_3_groups_template_data() const { - QTest::addColumn("copy_top_left_index_1"); - QTest::addColumn("copy_bottom_right_index_1"); - QTest::addColumn("copy_top_left_index_2"); - QTest::addColumn("copy_bottom_right_index_2"); - QTest::addColumn("copy_top_left_index_3"); - QTest::addColumn("copy_bottom_right_index_3"); - QTest::addColumn("paste_top_left_index_1"); - QTest::addColumn("paste_bottom_right_index_1"); - QTest::addColumn("paste_top_left_index_2"); - QTest::addColumn("paste_bottom_right_index_2"); - QTest::addColumn("paste_top_left_index_3"); - QTest::addColumn("paste_bottom_right_index_3"); - - QTest::newRow("chord then note then chord") - << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << get_chord_index(song_editor_pointer, 0, chord_beats_column) - << get_note_index(song_editor_pointer, 0, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column) - << get_chord_index(song_editor_pointer, 1, chord_interval_column) - << get_chord_index(song_editor_pointer, 1, chord_beats_column) - << get_note_index(song_editor_pointer, 1, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column) - << get_chord_index(song_editor_pointer, 3, chord_interval_column) - << get_chord_index(song_editor_pointer, 3, chord_beats_column) - << get_note_index(song_editor_pointer, 1, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column); - - QTest::newRow("note then chord then note") - << get_note_index(song_editor_pointer, 1, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column) - << get_chord_index(song_editor_pointer, 1, chord_interval_column) - << get_chord_index(song_editor_pointer, 1, chord_beats_column) - << get_note_index(song_editor_pointer, 0, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column) - << get_chord_index(song_editor_pointer, 2, chord_interval_column) - << get_chord_index(song_editor_pointer, 2, chord_beats_column) - << get_note_index(song_editor_pointer, 0, note_interval_column) - << get_note_index(song_editor_pointer, 1, note_beats_column) - << get_chord_index(song_editor_pointer, 3, chord_interval_column) - << get_chord_index(song_editor_pointer, 3, chord_beats_column); -} - -void Tester::test_paste_truncate_template() const { - QFETCH(const QModelIndex, copy_top_index); - QFETCH(const QModelIndex, copy_bottom_index); - QFETCH(const QModelIndex, paste_index); - - auto copy_top_data = chords_model_pointer->data(copy_top_index, Qt::EditRole); - auto paste_value = chords_model_pointer->data(paste_index, Qt::EditRole); - - selector_pointer->select(QItemSelection(copy_top_index, copy_bottom_index), - QItemSelectionModel::Select); - trigger_copy(song_editor_pointer); - clear_selection(); - - selector_pointer->select(paste_index, QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), - copy_top_data); +void Tester::test_delete_percussion_cells() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_delete_cells( + song_editor_pointer, + get_indices(percussions_model_pointer, 1, NUMBER_OF_PERCUSSION_COLUMNS)); undo(song_editor_pointer); +} - QCOMPARE(chords_model_pointer->data(paste_index, Qt::EditRole), paste_value); -}; - -void Tester::test_paste_truncate_template_data() const { - QTest::addColumn("copy_top_index"); - QTest::addColumn("copy_bottom_index"); - QTest::addColumn("paste_index"); - - QTest::newRow("first notes") - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << get_note_index(song_editor_pointer, 1, note_instrument_column) - << get_note_index(song_editor_pointer, 1, note_instrument_column); -}; - -void Tester::test_paste_recycle_template() const { - QFETCH(const QModelIndex, copy_index); - QFETCH(const QModelIndex, paste_top_index); - QFETCH(const QModelIndex, paste_bottom_index); - - auto copy_data = chords_model_pointer->data(copy_index, Qt::EditRole); - auto paste_top_data = - chords_model_pointer->data(paste_top_index, Qt::EditRole); - auto paste_bottom_data = - chords_model_pointer->data(paste_bottom_index, Qt::EditRole); - - selector_pointer->select(copy_index, QItemSelectionModel::Select); - trigger_copy(song_editor_pointer); - clear_selection(); - - selector_pointer->select(QItemSelection(paste_top_index, paste_bottom_index), - QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); +void Tester::test_copy_paste_chord_cells() const { + test_copy_paste_cells( + song_editor_pointer, + get_index_pairs(chords_model_pointer, 0, 1, NUMBER_OF_CHORD_COLUMNS)); +} - QCOMPARE(chords_model_pointer->data(paste_top_index, Qt::EditRole), - copy_data); - QCOMPARE(chords_model_pointer->data(paste_bottom_index, Qt::EditRole), - copy_data); +void Tester::test_copy_paste_note_cells() const { + trigger_edit_notes(song_editor_pointer, 1); + test_copy_paste_cells( + song_editor_pointer, + get_index_pairs(notes_model_pointer, 0, 1, NUMBER_OF_NOTE_COLUMNS)); undo(song_editor_pointer); +} - QCOMPARE(chords_model_pointer->data(paste_top_index, Qt::EditRole), - paste_top_data); - QCOMPARE(chords_model_pointer->data(paste_bottom_index, Qt::EditRole), - paste_bottom_data); -}; - -void Tester::test_paste_recycle_template_data() const { - QTest::addColumn("copy_index"); - QTest::addColumn("paste_top_index"); - QTest::addColumn("paste_bottom_index"); - - QTest::newRow("first two notes") - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << get_note_index(song_editor_pointer, 1, note_instrument_column); -}; - -void Tester::test_insert_into() const { - auto chord_index = get_chord_index(song_editor_pointer, 0, chord_interval_column); - auto old_row_count = chords_model_pointer->rowCount(chord_index); - selector_pointer->select(chord_index, QItemSelectionModel::Select); - trigger_insert_into(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->rowCount(chord_index), old_row_count + 1); +void Tester::test_copy_paste_percussion_cells() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_copy_paste_cells(song_editor_pointer, + get_index_pairs(percussions_model_pointer, 0, 1, + NUMBER_OF_PERCUSSION_COLUMNS)); undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->rowCount(chord_index), old_row_count); } -void Tester::test_new_chord_from_chord() const { - auto chord_beats_value = - chords_model_pointer->data(get_chord_index(song_editor_pointer, 2, chord_beats_column)); - QVERIFY(!chord_beats_value.toString().isEmpty()); +void Tester::test_cut_paste_chord_cells() const { + test_cut_paste_cells( + song_editor_pointer, + get_index_pairs(chords_model_pointer, 1, 0, NUMBER_OF_CHORD_COLUMNS)); +} - selector_pointer->select(get_chord_index(song_editor_pointer, 2, chord_interval_column), - QItemSelectionModel::Select); - trigger_insert_after(song_editor_pointer); - clear_selection(); - QCOMPARE(chords_model_pointer->data( - get_chord_index(song_editor_pointer, 3, chord_beats_column), Qt::EditRole), - chord_beats_value); +void Tester::test_cut_paste_note_cells() const { + trigger_edit_notes(song_editor_pointer, 1); + test_cut_paste_cells( + song_editor_pointer, + get_index_pairs(notes_model_pointer, 1, 0, NUMBER_OF_NOTE_COLUMNS)); undo(song_editor_pointer); } -void Tester::test_new_note_from_chord() const { - auto chord_beats_value = - chords_model_pointer->data(get_chord_index(song_editor_pointer, 2, chord_beats_column)); - QVERIFY(!chord_beats_value.toString().isEmpty()); - - auto chord_words_value = - chords_model_pointer->data(get_chord_index(song_editor_pointer, 2, chord_words_column)); - QVERIFY(!chord_words_value.toString().isEmpty()); - - selector_pointer->select(get_chord_index(song_editor_pointer, 2, chord_interval_column), - QItemSelectionModel::Select); - trigger_insert_into(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->data( - get_note_index(song_editor_pointer, 0, note_beats_column), Qt::EditRole), - chord_beats_value); - QCOMPARE(chords_model_pointer->data( - get_note_index(song_editor_pointer, 0, note_words_column), Qt::EditRole), - chord_words_value); +void Tester::test_cut_paste_percussion_cells() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_cut_paste_cells(song_editor_pointer, + get_index_pairs(percussions_model_pointer, 1, 0, + NUMBER_OF_PERCUSSION_COLUMNS)); undo(song_editor_pointer); } -void Tester::test_new_note_from_note() const { - auto note_beats_value = chords_model_pointer->data( - get_note_index(song_editor_pointer, 0, note_beats_column)); - QVERIFY(!note_beats_value.toString().isEmpty()); - - auto note_velocity_ratio_value = chords_model_pointer->data( - get_note_index(song_editor_pointer, 0, note_velocity_ratio_column)); - QVERIFY(!note_velocity_ratio_value.toString().isEmpty()); - - auto note_tempo_ratio_value = chords_model_pointer->data( - get_note_index(song_editor_pointer, 0, note_tempo_ratio_column)); - QVERIFY(!note_tempo_ratio_value.toString().isEmpty()); - - auto note_words_value = chords_model_pointer->data( - get_note_index(song_editor_pointer, 0, note_words_column)); - QVERIFY(!note_words_value.toString().isEmpty()); +void Tester::test_chord_insert_into() const { + test_insert_into(song_editor_pointer, chords_model_pointer); +} - selector_pointer->select(get_note_index(song_editor_pointer, 0, note_interval_column), - QItemSelectionModel::Select); - trigger_insert_after(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->data( - get_note_index(song_editor_pointer, 1, note_beats_column), Qt::EditRole), - note_beats_value); - QCOMPARE(chords_model_pointer->data( - get_note_index(song_editor_pointer, 1, note_velocity_ratio_column), - Qt::EditRole), - note_velocity_ratio_value); - QCOMPARE( - chords_model_pointer->data( - get_note_index(song_editor_pointer, 1, note_tempo_ratio_column), Qt::EditRole), - note_tempo_ratio_value); - QCOMPARE(chords_model_pointer->data( - get_note_index(song_editor_pointer, 1, note_words_column), Qt::EditRole), - note_words_value); +void Tester::test_note_insert_into() const { + trigger_edit_notes(song_editor_pointer, 1); + test_insert_into(song_editor_pointer, notes_model_pointer); undo(song_editor_pointer); } -void Tester::test_insert_rows_template() const { - QFETCH(const QModelIndex, index); - - auto parent_index = chords_model_pointer->parent(index); - auto old_row_count = chords_model_pointer->rowCount(parent_index); - - selector_pointer->select(index, QItemSelectionModel::Select); - trigger_insert_after(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->rowCount(parent_index), old_row_count + 1); +void Tester::test_percussion_insert_into() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_insert_into(song_editor_pointer, percussions_model_pointer); undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->rowCount(parent_index), old_row_count); } -void Tester::test_insert_rows_template_data() const { - QTest::addColumn("index"); - - QTest::newRow("insert chord after") << get_chord_index(song_editor_pointer, 0, chord_interval_column); - QTest::newRow("insert note after") << get_note_index(song_editor_pointer, 0, note_interval_column); +void Tester::test_chord_insert_after() const { + test_insert_after(song_editor_pointer, chords_model_pointer); } -void Tester::test_delete_rows_template() const { - QFETCH(const QModelIndex, index); - - auto parent_index = chords_model_pointer->parent(index); - auto old_row_count = chords_model_pointer->rowCount(parent_index); - - selector_pointer->select(index, QItemSelectionModel::Select); - trigger_delete(song_editor_pointer); - clear_selection(); - - QCOMPARE(chords_model_pointer->rowCount(parent_index), old_row_count - 1); +void Tester::test_note_insert_after() const { + trigger_edit_notes(song_editor_pointer, 1); + test_insert_after(song_editor_pointer, notes_model_pointer); undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->rowCount(parent_index), old_row_count); } -void Tester::test_delete_rows_template_data() const { - QTest::addColumn("index"); - - QTest::newRow("delete chord") << get_chord_index(song_editor_pointer, 0, chord_interval_column); - QTest::newRow("delete note") << get_note_index(song_editor_pointer, 0, note_interval_column); +void Tester::test_percussion_insert_after() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_insert_after(song_editor_pointer, percussions_model_pointer); + undo(song_editor_pointer); } -void Tester::test_paste_rows_template() const { - QFETCH(const QModelIndex, index); - - auto parent_index = chords_model_pointer->parent(index); - auto old_row_count = chords_model_pointer->rowCount(parent_index); - - selector_pointer->select(index, QItemSelectionModel::Select); - trigger_copy(song_editor_pointer); - clear_selection(); - - selector_pointer->select(index, QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); +void Tester::test_chord_remove_rows() const { + test_remove_rows(song_editor_pointer, chords_model_pointer); +} - QCOMPARE(chords_model_pointer->rowCount(parent_index), old_row_count + 1); +void Tester::test_note_remove_rows() const { + trigger_edit_notes(song_editor_pointer, 1); + test_remove_rows(song_editor_pointer, notes_model_pointer); undo(song_editor_pointer); - QCOMPARE(chords_model_pointer->rowCount(parent_index), old_row_count); } -void Tester::test_paste_rows_template_data() const { - QTest::addColumn("index"); - QTest::addColumn("parent_row_count"); - - QTest::newRow("paste chord after") << get_chord_index(song_editor_pointer, 0, chord_interval_column); - QTest::newRow("paste note after") << get_note_index(song_editor_pointer, 0, note_interval_column); +void Tester::test_percussion_remove_rows() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_remove_rows(song_editor_pointer, percussions_model_pointer); + undo(song_editor_pointer); } -void Tester::test_bad_paste_template() { - QFETCH(const QString, copied); - QFETCH(const QString, mime_type); - QFETCH(const QModelIndex, index); - QFETCH(const QString, error_message); - - close_message_later(error_message); - - auto *new_data_pointer = std::make_unique().release(); - - Q_ASSERT(new_data_pointer != nullptr); - new_data_pointer->setData(mime_type, copied.toStdString().c_str()); - - auto *clipboard_pointer = QGuiApplication::clipboard(); - Q_ASSERT(clipboard_pointer != nullptr); - clipboard_pointer->setMimeData(new_data_pointer); - - selector_pointer->select(index, QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); -} - -void Tester::test_bad_paste_template_data() const { - QTest::addColumn("copied"); - QTest::addColumn("mime_type"); - QTest::addColumn("index"); - QTest::addColumn("error_message"); - - QTest::newRow("unparsable chord") - << "[" << "application/prs.chords+json" << get_chord_index(song_editor_pointer, 0, chord_interval_column) - - << "[json.exception.parse_error.101] parse error at line 1, column 2: " - "syntax error while parsing value - unexpected end of input; expected " - "'[', '{', or a literal"; - - QTest::newRow("empty chords") - << "[]" << "application/prs.chords+json" << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << "Nothing to paste!"; - - QTest::newRow("wrong type chord") - << "{\"a\": 1}" << "application/prs.chords+json" - << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << "At of {\"a\":1} - unexpected instance type\n"; - - QTest::newRow("unparsable note") - << "[" << "application/prs.notes+json" << get_note_index(song_editor_pointer, 0, note_interval_column) - << "[json.exception.parse_error.101] parse error at line 1, column 2: " - "syntax error while parsing value - unexpected end of input; expected " - "'[', '{', or a literal"; - - QTest::newRow("wrong type note") - << "{\"a\": 1}" << "application/prs.notes+json" - << get_note_index(song_editor_pointer, 0, note_interval_column) - << "At of {\"a\":1} - unexpected instance type\n"; - - QTest::newRow("wrong row mime type") - << "{\"a\": 1}" << "not a mime" << get_chord_index(song_editor_pointer, 0, chord_interval_column) - - << "Cannot paste not a mime into destination needing chords"; - - QTest::newRow("wrong cell mime type") - << "{\"a\": 1}" << "not a mime" - << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << "Cannot paste not a mime into destination needing cells"; - - QTest::newRow("unparsable interval") - << "[" << "application/prs.cells+json" - << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << "[json.exception.parse_error.101] parse error at line 1, column 2: " - "syntax error while parsing value - unexpected end of input; expected " - "'[', '{', or a literal"; - - QTest::newRow("unparsable rational") - << "[" << "application/prs.cells+json" - << get_chord_index(song_editor_pointer, 0, chord_beats_column) - << "[json.exception.parse_error.101] parse error at line 1, column 2: " - "syntax error while parsing value - unexpected end of input; expected " - "'[', '{', or a literal"; - - QTest::newRow("unparsable instrument") - << "[" << "application/prs.cells+json" - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << "[json.exception.parse_error.101] parse error at line 1, column 2: " - "syntax error while parsing value - unexpected end of input; expected " - "'[', '{', or a literal"; - - QTest::newRow("unparsable octave") - << "[" << "application/prs.cells+json" - << get_chord_index(song_editor_pointer, 0, chord_words_column) - - << "[json.exception.parse_error.101] parse error at line 1, column 2: " - "syntax error while parsing value - unexpected end of input; expected " - "'[', '{', or a literal"; - - QTest::newRow("wrong interval type") - << "[1]" << "application/prs.cells+json" - << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << "At of [1] - unexpected instance type\n"; - - QTest::newRow("wrong rational type") - << "[1]" << "application/prs.cells+json" - << get_chord_index(song_editor_pointer, 0, chord_beats_column) - << "At of [1] - unexpected instance type\n"; - - QTest::newRow("wrong instrument type") - << "[1]" << "application/prs.cells+json" - << get_note_index(song_editor_pointer, 0, note_instrument_column) - << "At of [1] - unexpected instance type\n"; - - QTest::newRow("wrong words type") - << "[1]" << "application/prs.cells+json" - << get_chord_index(song_editor_pointer, 0, chord_words_column) - << "At of [1] - unexpected instance type\n"; +void Tester::test_bad_chord_pastes() { + test_bad_pastes( + chords_model_pointer->index(0, 0), + std::vector({BadPasteRow({"", "not a mime", "Cannot paste not a mime into destination needing chords cells"}), + BadPasteRow({"", NOTES_CELLS_MIME, "Cannot paste notes cells into destination needing chords cells"}), + BadPasteRow({"[", CHORDS_CELLS_MIME, "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"}), + BadPasteRow({"[]", CHORDS_CELLS_MIME, "Nothing to paste!"}), + BadPasteRow({"{\"a\": 1}", CHORDS_CELLS_MIME, "At of {\"a\":1} - required property 'left_column' not found in object\n"})})); +} + +void Tester::test_bad_note_pastes() { + trigger_edit_notes(song_editor_pointer, 1); + test_bad_pastes( + notes_model_pointer->index(0, 0), + std::vector({BadPasteRow({"", "not a mime", "Cannot paste not a mime into destination needing notes cells"}), + BadPasteRow({"", CHORDS_CELLS_MIME, "Cannot paste chords cells into destination needing notes cells"}), + BadPasteRow({"[", NOTES_CELLS_MIME, "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"}), + BadPasteRow({"[]", NOTES_CELLS_MIME, "Nothing to paste!"}), + BadPasteRow({"{\"a\": 1}", NOTES_CELLS_MIME, "At of {\"a\":1} - required property 'left_column' not found in object\n"})})); + undo(song_editor_pointer); } -void Tester::test_paste_wrong_level_template() { - QFETCH(const QModelIndex, copy_index); - QFETCH(const QModelIndex, paste_index); - QFETCH(const QString, error_message); - - selector_pointer->select(copy_index, QItemSelectionModel::Select); - trigger_copy(song_editor_pointer); - clear_selection(); - - close_message_later(error_message); - - selector_pointer->select(paste_index, QItemSelectionModel::Select); - trigger_paste_cells_or_after(song_editor_pointer); - clear_selection(); -} - -void Tester::test_paste_wrong_level_template_data() const { - QTest::addColumn("copy_index"); - QTest::addColumn("paste_index"); - QTest::addColumn("error_message"); - - QTest::newRow("chords to notes") - << get_chord_index(song_editor_pointer, 0, chord_interval_column) << get_note_index(song_editor_pointer, 0, note_interval_column) - << "Cannot paste chords into destination needing notes"; - - QTest::newRow("notes to chords") - << get_note_index(song_editor_pointer, 0, note_interval_column) << get_chord_index(song_editor_pointer, 0, chord_interval_column) - << "Cannot paste notes into destination needing chords"; +void Tester::test_bad_percussion_pastes() { + trigger_edit_percussions(song_editor_pointer, 1); + test_bad_pastes( + percussions_model_pointer->index(0, 0), + std::vector({BadPasteRow({"", "not a mime", "Cannot paste not a mime into destination needing percussions cells"}), + BadPasteRow({"", CHORDS_CELLS_MIME, "Cannot paste chords cells into destination needing percussions cells"}), + BadPasteRow({"[", PERCUSSIONS_CELLS_MIME, "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"}), + BadPasteRow({"[]", PERCUSSIONS_CELLS_MIME, "Nothing to paste!"}), + BadPasteRow({"{\"a\": 1}", PERCUSSIONS_CELLS_MIME, "At of {\"a\":1} - required property 'left_column' not found in object\n"})})); + undo(song_editor_pointer); } void Tester::test_too_loud() { + trigger_edit_notes(song_editor_pointer, 1); + set_starting_velocity(song_editor_pointer, BIG_VELOCITY); + auto *selector_pointer = get_selector_pointer(table_view_pointer); + close_message_later( - "Velocity exceeds 127 for chord 3, note 2. Playing with velocity 127."); + "Velocity 378 exceeds 127 for chord 2, note 1. Playing with velocity 127."); - selector_pointer->select(get_note_index(song_editor_pointer, 1, note_interval_column), + selector_pointer->select(notes_model_pointer->index(0, note_interval_column), QItemSelectionModel::Select); trigger_play(song_editor_pointer); - clear_selection(); + clear_selection(selector_pointer); QThread::msleep(WAIT_TIME); trigger_stop_playing(song_editor_pointer); undo(song_editor_pointer); + + undo(song_editor_pointer); } void Tester::test_too_many_channels() { - for (auto index = 0; index < OVERLOAD_NUMBER; index = index + 1) { - selector_pointer->select(get_chord_index(song_editor_pointer, 0, chord_interval_column), - QItemSelectionModel::Select); + trigger_edit_notes(song_editor_pointer, 0); + + for (auto number = 0; number < OVERLOAD_NUMBER; number = number + 1) { trigger_insert_into(song_editor_pointer); - clear_selection(); } - close_message_later( - "Out of MIDI channels for chord 1, note 17. Not playing note."); + trigger_back_to_chords(song_editor_pointer); - selector_pointer->select(get_chord_index(song_editor_pointer, 0, chord_interval_column), - QItemSelectionModel::Select); + auto *selector_pointer = get_selector_pointer(table_view_pointer); + + close_message_later("Out of MIDI channels for chord 1, note 17. Not playing note."); + + selector_pointer->select( + chords_model_pointer->index(0, chord_interval_column), + QItemSelectionModel::Select); trigger_play(song_editor_pointer); - clear_selection(); + clear_selection(selector_pointer); QThread::msleep(WAIT_TIME); trigger_stop_playing(song_editor_pointer); + // undo back to chords + undo(song_editor_pointer); + for (auto index = 0; index < OVERLOAD_NUMBER; index = index + 1) { undo(song_editor_pointer); } -} - -void Tester::test_play_template() const { - QFETCH(const QModelIndex, first_index); - QFETCH(const QModelIndex, last_index); - selector_pointer->select(QItemSelection(first_index, last_index), - QItemSelectionModel::Select); - trigger_play(song_editor_pointer); - // first cut off early - trigger_play(song_editor_pointer); - // now play the whole thing - QThread::msleep(WAIT_TIME); - clear_selection(); + // undo edit notes + undo(song_editor_pointer); } -void Tester::test_play_template_data() const { - QTest::addColumn("first_index"); - QTest::addColumn("last_index"); - - QTest::newRow("first two chords") - << get_chord_index(song_editor_pointer, 0, chord_interval_column) << get_chord_index(song_editor_pointer, 1, chord_interval_column); - QTest::newRow("second chord") - << get_chord_index(song_editor_pointer, 1, chord_interval_column) << get_chord_index(song_editor_pointer, 1, chord_interval_column); - - auto first_chord_second_note_index = get_note_index(song_editor_pointer, 1, note_interval_column); - QTest::newRow("first chord second note") - << first_chord_second_note_index << first_chord_second_note_index; +void Tester::test_chord_plays() const { + test_plays(song_editor_pointer, + std::vector({ + TwoIndicesRow( + {chords_model_pointer->index(0, chord_interval_column), + chords_model_pointer->index(1, chord_interval_column)}), + TwoIndicesRow( + {chords_model_pointer->index(1, chord_interval_column), + chords_model_pointer->index(1, chord_interval_column)}), + })); +} + +void Tester::test_note_plays() const { + trigger_edit_notes(song_editor_pointer, 1); + test_plays( + song_editor_pointer, + std::vector({ + TwoIndicesRow({notes_model_pointer->index(0, note_interval_column), + notes_model_pointer->index(1, note_interval_column)}), + TwoIndicesRow({notes_model_pointer->index(1, note_interval_column), + notes_model_pointer->index(1, note_interval_column)}), + })); + undo(song_editor_pointer); +} - auto second_chord_first_note_index = get_note_index(song_editor_pointer, 0, note_interval_column); - QTest::newRow("first note") - << second_chord_first_note_index << second_chord_first_note_index; +void Tester::test_percussion_plays() const { + trigger_edit_percussions(song_editor_pointer, 1); + test_plays( + song_editor_pointer, + std::vector({ + TwoIndicesRow( + {percussions_model_pointer->index(0, percussion_set_column), + percussions_model_pointer->index(1, percussion_set_column)}), + TwoIndicesRow( + {percussions_model_pointer->index(1, percussion_set_column), + percussions_model_pointer->index(1, percussion_set_column)}), + })); + undo(song_editor_pointer); } void Tester::test_save() const { @@ -1478,24 +1190,15 @@ void Tester::test_export() const { export_to_file(song_editor_pointer, temp_json_file.fileName()); } -void Tester::test_broken_file_template() { - QFETCH(const QString, json_song); - QFETCH(const QString, error_message); - close_message_later(error_message); - open_text(json_song); -} - -void Tester::test_broken_file_template_data() { - QTest::addColumn("json_song"); - QTest::addColumn("error_message"); - - QTest::newRow("parse error") - << "{" - << "[json.exception.parse_error.101] parse error at line 1, column 2: " - "syntax error while parsing object key - unexpected end of input; " - "expected string literal"; - QTest::newRow("schema error") - << "[1]" << "At of [1] - unexpected instance type\n"; +void Tester::test_broken_file() { + for (const auto &row : std::vector({ + TwoStringsRow({"{", "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing object key - unexpected end of input; expected string literal"}), + TwoStringsRow({"[]", "At of [] - unexpected instance type\n"}), + TwoStringsRow({"[1]", "At of [1] - unexpected instance type\n"}), + })) { + close_message_later(row.second_string); + open_text(row.first_string); + } } void Tester::test_open_empty() const { diff --git a/tests/Tester.h b/tests/Tester.h index 7d8f0dbb..4f3b833a 100644 --- a/tests/Tester.h +++ b/tests/Tester.h @@ -2,123 +2,117 @@ #include #include +#include class QAbstractItemModel; -class QTableView; -class QItemSelectionModel; +class QAbstractItemView; +class QModelIndex; struct SongEditor; +struct BadPasteRow { + const QString copied; + const QString mime_type; + const QString error_message; +}; + struct Tester : public QObject { Q_OBJECT public: SongEditor *const song_editor_pointer; - QTableView *const table_view_pointer; + QAbstractItemView *const table_view_pointer; + QAbstractItemModel *const chords_model_pointer; + QAbstractItemModel *const notes_model_pointer; + QAbstractItemModel *const percussions_model_pointer; bool waiting_for_message = false; void close_message_later(const QString &expected_text); - void clear_selection() const; void open_text(const QString &json_song) const; Tester(); ~Tester() override; + void test_bad_pastes(const QModelIndex &index, + const std::vector &bad_paste_rows); + private slots: - void initTestCase(); + void initTestCase() const; - void test_to_string_template() const; - void test_to_string_template_data() const; + void test_to_strings() const; void test_chords_count() const; - void test_notes_count_template() const; - void test_notes_count_template_data() const; + void test_notes_count() const; + void test_percussions_count() const; - void test_column_count() const; + void test_number_of_chord_columns() const; + void test_number_of_note_columns() const; + void test_number_of_percussion_columns() const; void test_gain_control() const; void test_starting_key_control() const; void test_starting_velocity_control() const; void test_starting_tempo_control() const; - void test_chord_column_headers_template() const; - static void test_chord_column_headers_template_data(); - - void test_note_column_headers_template() const; - static void test_note_column_headers_template_data(); - - void test_percussion_column_headers_template() const; - static void test_percussion_column_headers_template_data(); - - void test_flags(); - void test_chord_flags_template() const; - void test_chord_flags_template_data() const; - - void test_status_template() const; - static void test_status_template_data(); - - void test_get_value_template() const; - void test_get_value_template_data() const; - - void test_background() const; - - void test_delegate_template() const; - void test_delegate_template_data() const; - - void test_no_set_value() const; - - void test_set_value_template() const; - void test_set_value_template_data() const; - - void test_delete_cell_template() const; - void test_delete_cell_template_data() const; + void test_row_headers() const; + void test_chord_column_headers() const; + void test_note_column_headers() const; + void test_percussion_column_headers() const; - void test_delete_3_groups_template() const; - void test_delete_3_groups_template_data() const; + void test_chord_flags() const; + void test_note_flags() const; + void test_percussion_flags() const; - void test_paste_cell_template() const; - void test_paste_cell_template_data() const; + void test_chord_frequencies() const; - void test_cut_paste_cell_template() const; - void test_cut_paste_cell_template_data() const; + void test_get_unsupported_chord_role() const; + void test_get_unsupported_note_role() const; + void test_get_unsupported_percussion_role() const; - void test_paste_3_groups_template() const; - void test_paste_3_groups_template_data() const; + void test_set_unsupported_chord_role() const; + void test_set_unsupported_note_role() const; + void test_set_unsupported_percussion_role() const; - void test_paste_truncate_template() const; - void test_paste_truncate_template_data() const; + void test_set_chord_values() const; + void test_set_note_values() const; + void test_set_percussion_values() const; - void test_paste_recycle_template() const; - void test_paste_recycle_template_data() const; + void test_delete_chord_cells() const; + void test_delete_note_cells() const; + void test_delete_percussion_cells() const; - void test_insert_into() const; - void test_new_chord_from_chord() const; - void test_new_note_from_chord() const; - void test_new_note_from_note() const; + void test_copy_paste_chord_cells() const; + void test_copy_paste_note_cells() const; + void test_copy_paste_percussion_cells() const; - void test_insert_rows_template() const; - void test_insert_rows_template_data() const; + void test_cut_paste_chord_cells() const; + void test_cut_paste_note_cells() const; + void test_cut_paste_percussion_cells() const; - void test_delete_rows_template() const; - void test_delete_rows_template_data() const; + void test_chord_insert_into() const; + void test_note_insert_into() const; + void test_percussion_insert_into() const; - void test_paste_rows_template() const; - void test_paste_rows_template_data() const; + void test_chord_insert_after() const; + void test_note_insert_after() const; + void test_percussion_insert_after() const; - void test_bad_paste_template(); - void test_bad_paste_template_data() const; + void test_chord_remove_rows() const; + void test_note_remove_rows() const; + void test_percussion_remove_rows() const; - void test_paste_wrong_level_template(); - void test_paste_wrong_level_template_data() const; + void test_bad_chord_pastes(); + void test_bad_note_pastes(); + void test_bad_percussion_pastes(); void test_too_loud(); void test_too_many_channels(); - void test_play_template() const; - void test_play_template_data() const; + void test_chord_plays() const; + void test_note_plays() const; + void test_percussion_plays() const; void test_save() const; void test_export() const; - void test_broken_file_template(); - static void test_broken_file_template_data(); + void test_broken_file(); void test_open_empty() const; };