From cd03ccbd8f3a9fb57c665014f62d779c5c3f8418 Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Mon, 30 May 2011 08:57:30 +0000 Subject: implemented cut,copy'n'paste and "select foo" to the score editor --- muse2/ChangeLog | 3 + muse2/muse/functions.cpp | 65 ++++++++++++++++- muse2/muse/functions.h | 6 ++ muse2/muse/midiedit/scoreedit.cpp | 142 ++++++++++++++++++++++++++++++++++---- muse2/muse/midiedit/scoreedit.h | 32 ++++++++- 5 files changed, 231 insertions(+), 17 deletions(-) (limited to 'muse2') diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 9f7d2e07..fae1936d 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,6 @@ +30.05.2011: + - implemented cut,copy'n'paste and the "select foo" entries in the score editor (flo93) + - added select_foo() functions to functions.cpp (flo93) 29.05.2011: - moved cut,copy'n'paste to functions.cpp, removed unneccessary duplication (flo93) - changed behaviour of paste: now the pasted, not the original notes are selected (flo93) diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index 94b7a52d..8b17bd8d 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -709,7 +709,7 @@ void paste_at(Part* dest_part, const QString& pt, int pos) { case Xml::Error: case Xml::End: - return; + goto end_of_paste_at; case Xml::TagStart: if (tag == "eventlist") @@ -726,7 +726,7 @@ void paste_at(Part* dest_part, const QString& pt, int pos) { printf("ERROR: trying to add event before current part!\n"); song->endUndo(SC_EVENT_INSERTED); - return; + goto end_of_paste_at; } e.setTick(tick); @@ -745,7 +745,7 @@ void paste_at(Part* dest_part, const QString& pt, int pos) audio->msgAddEvent(e, dest_part, false, false, false); } song->endUndo(modified); - return; + goto end_of_paste_at; } else xml.unknown("pasteAt"); @@ -757,8 +757,67 @@ void paste_at(Part* dest_part, const QString& pt, int pos) break; } } + + end_of_paste_at: + song->update(SC_SELECTION); +} + +void select_all(const std::set& parts) +{ + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(true); + } + song->update(SC_SELECTION); } +void select_none(const std::set& parts) +{ + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(false); + } + song->update(SC_SELECTION); +} + +void select_invert(const std::set& parts) +{ + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(!event.selected()); + } + song->update(SC_SELECTION); +} + +void select_in_loop(const std::set& parts) +{ + select_none(parts); + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected((event.tick()>=song->lpos() && event.endTick()<=song->rpos())); + } + song->update(SC_SELECTION); +} + +void select_not_in_loop(const std::set& parts) +{ + select_none(parts); + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + { + Event& event=ev_it->second; + event.setSelected(!(event.tick()>=song->lpos() && event.endTick()<=song->rpos())); + } + song->update(SC_SELECTION); +} void read_function_dialog_config(Xml& xml) diff --git a/muse2/muse/functions.h b/muse2/muse/functions.h index b08c2c39..6826a9b7 100644 --- a/muse2/muse/functions.h +++ b/muse2/muse/functions.h @@ -76,6 +76,12 @@ void paste_notes(Part* dest_part); QMimeData* selected_events_to_mime(const std::set& parts, int range); void paste_at(Part* dest_part, const QString& pt, int pos); +//functions for selections +void select_all(const std::set& parts); +void select_none(const std::set& parts); +void select_invert(const std::set& parts); +void select_in_loop(const std::set& parts); +void select_not_in_loop(const std::set& parts); //functions for reading and writing default values class Xml; diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index e0fe6491..7bb4737f 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -55,9 +55,9 @@ using namespace std; #include "cmd.h" #include "sig.h" #include "song.h" +#include "shortcuts.h" //#include "../ctrl/ctrledit.h" -//#include "shortcuts.h" string IntToStr(int i); @@ -340,9 +340,59 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) quant_toolbar->addWidget(px_per_whole_spinbox); px_per_whole_spinbox->setValue(300); + QMenu* edit_menu = menuBar()->addMenu(tr("&Edit")); + + edit_menu->addActions(undoRedo->actions()); + edit_menu->addSeparator(); + + cut_action = edit_menu->addAction(QIcon(*editcutIconSet), tr("C&ut")); + menu_mapper->setMapping(cut_action, CMD_CUT); + connect(cut_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + copy_action = edit_menu->addAction(QIcon(*editcopyIconSet), tr("&Copy")); + menu_mapper->setMapping(copy_action, CMD_COPY); + connect(copy_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + paste_action = edit_menu->addAction(QIcon(*editpasteIconSet), tr("&Paste")); + menu_mapper->setMapping(paste_action, CMD_PASTE); + connect(paste_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + edit_menu->addSeparator(); + + del_action = edit_menu->addAction(tr("Delete &Events")); + menu_mapper->setMapping(del_action, CMD_ERASE); + connect(del_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + edit_menu->addSeparator(); + + QMenu* select_menu = edit_menu->addMenu(QIcon(*selectIcon), tr("&Select")); + + select_all_action = select_menu->addAction(QIcon(*select_allIcon), tr("Select &All")); + menu_mapper->setMapping(select_all_action, CMD_SELECT_ALL); + connect(select_all_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_none_action = select_menu->addAction(QIcon(*select_deselect_allIcon), tr("&Deselect All")); + menu_mapper->setMapping(select_none_action, CMD_SELECT_NONE); + connect(select_none_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_invert_action = select_menu->addAction(QIcon(*select_invert_selectionIcon), tr("Invert &Selection")); + menu_mapper->setMapping(select_invert_action, CMD_SELECT_INVERT); + connect(select_invert_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_menu->addSeparator(); + + select_iloop_action = select_menu->addAction(QIcon(*select_inside_loopIcon), tr("&Inside Loop")); + menu_mapper->setMapping(select_iloop_action, CMD_SELECT_ILOOP); + connect(select_iloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_oloop_action = select_menu->addAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop")); + menu_mapper->setMapping(select_oloop_action, CMD_SELECT_OLOOP); + connect(select_oloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + QMenu* settings_menu = menuBar()->addMenu(tr("&Settings")); - QMenu* color_menu = settings_menu->addMenu(tr("Note head &colors")); + color_menu = settings_menu->addMenu(tr("Note head &colors")); color_actions = new QActionGroup(this); color_black_action = color_menu->addAction(tr("&Black"), menu_mapper, SLOT(map())); color_velo_action = color_menu->addAction(tr("&Velocity"), menu_mapper, SLOT(map())); @@ -378,16 +428,16 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) QMenu* functions_menu = menuBar()->addMenu(tr("&Functions")); - QAction* func_quantize_action = functions_menu->addAction(tr("&Quantize"), menu_mapper, SLOT(map())); - QAction* func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); - QAction* func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); - QAction* func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); - QAction* func_transpose_action = functions_menu->addAction(tr("Transpose"), menu_mapper, SLOT(map())); - QAction* func_erase_action = functions_menu->addAction(tr("Erase Events"), menu_mapper, SLOT(map())); - QAction* func_move_action = functions_menu->addAction(tr("Move Notes"), menu_mapper, SLOT(map())); - QAction* func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length"), menu_mapper, SLOT(map())); - QAction* func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps"), menu_mapper, SLOT(map())); - QAction* func_legato_action = functions_menu->addAction(tr("Legato"), menu_mapper, SLOT(map())); + func_quantize_action = functions_menu->addAction(tr("&Quantize"), menu_mapper, SLOT(map())); + func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); + func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); + func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); + func_transpose_action = functions_menu->addAction(tr("Transpose"), menu_mapper, SLOT(map())); + func_erase_action = functions_menu->addAction(tr("Erase Events"), menu_mapper, SLOT(map())); + func_move_action = functions_menu->addAction(tr("Move Notes"), menu_mapper, SLOT(map())); + func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length"), menu_mapper, SLOT(map())); + func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps"), menu_mapper, SLOT(map())); + func_legato_action = functions_menu->addAction(tr("Legato"), menu_mapper, SLOT(map())); menu_mapper->setMapping(func_quantize_action, CMD_QUANTIZE); menu_mapper->setMapping(func_notelen_action, CMD_NOTELEN); menu_mapper->setMapping(func_velocity_action, CMD_VELOCITY); @@ -398,7 +448,16 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) menu_mapper->setMapping(func_fixed_len_action, CMD_FIXED_LEN); menu_mapper->setMapping(func_del_overlaps_action, CMD_DELETE_OVERLAPS); menu_mapper->setMapping(func_legato_action, CMD_LEGATO); + + init_shortcuts(); + + connect(muse, SIGNAL(configChanged()), SLOT(init_shortcuts())); + QClipboard* cb = QApplication::clipboard(); + connect(cb, SIGNAL(dataChanged()), SLOT(clipboard_changed())); + + clipboard_changed(); + selection_changed(); if (!default_toolbar_state.isEmpty()) restoreState(default_toolbar_state); @@ -417,6 +476,32 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) apply_velo=true; } +void ScoreEdit::init_shortcuts() +{ + cut_action->setShortcut(shortcuts[SHRT_CUT].key); + copy_action->setShortcut(shortcuts[SHRT_COPY].key); + paste_action->setShortcut(shortcuts[SHRT_PASTE].key); + del_action->setShortcut(shortcuts[SHRT_DELETE].key); + + select_all_action->setShortcut(shortcuts[SHRT_SELECT_ALL].key); + select_none_action->setShortcut(shortcuts[SHRT_SELECT_NONE].key); + select_invert_action->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); + select_iloop_action->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); + select_oloop_action->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); + + color_menu->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); + + func_quantize_action->setShortcut(shortcuts[SHRT_QUANTIZE].key); + func_notelen_action->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); + func_velocity_action->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); + func_transpose_action->setShortcut(shortcuts[SHRT_TRANSPOSE].key); + func_erase_action->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); + func_move_action->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); + func_fixed_len_action->setShortcut(shortcuts[SHRT_FIXED_LEN].key); + func_del_overlaps_action->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); +} + + void ScoreEdit::add_parts(PartList* pl, bool all_in_one) { score_canvas->add_staves(pl, all_in_one); @@ -522,6 +607,9 @@ void ScoreEdit::song_changed(int flags) if (velo_off>=0) velo_off_spinbox->setValue(velo_off); } } + + if (flags & SC_SELECTION) + selection_changed(); } void ScoreEdit::canvas_width_changed(int width) @@ -607,6 +695,21 @@ void ScoreEdit::menu_command(int cmd) } break; + case CMD_SELECT_ALL: select_all(score_canvas->get_all_parts()); break; + case CMD_SELECT_NONE: select_none(score_canvas->get_all_parts()); break; + case CMD_SELECT_INVERT: select_invert(score_canvas->get_all_parts()); break; + case CMD_SELECT_ILOOP: select_in_loop(score_canvas->get_all_parts()); break; + case CMD_SELECT_OLOOP: select_not_in_loop(score_canvas->get_all_parts()); break; + + case CMD_CUT: + copy_notes(score_canvas->get_all_parts(), 1); + erase_notes(score_canvas->get_all_parts(), 1); + break; + case CMD_COPY: copy_notes(score_canvas->get_all_parts(), 1); break; + case CMD_PASTE: + menu_command(CMD_SELECT_NONE); + paste_notes(score_canvas->get_selected_part()); + break; case CMD_QUANTIZE: quantize_notes(score_canvas->get_all_parts()); break; case CMD_VELOCITY: modify_velocity(score_canvas->get_all_parts()); break; case CMD_CRESCENDO: crescendo(score_canvas->get_all_parts()); break; @@ -623,6 +726,19 @@ void ScoreEdit::menu_command(int cmd) } } +void ScoreEdit::clipboard_changed() +{ + paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); +} + +void ScoreEdit::selection_changed() +{ + bool flag = !get_events(score_canvas->get_all_parts(),1).empty(); + cut_action->setEnabled(flag); + copy_action->setEnabled(flag); + del_action->setEnabled(flag); +} + //duplicated from songfile.cpp's MusE::readPart(); the only differences: //"none" is supported and tag_name is settable @@ -4117,6 +4233,7 @@ void ScoreCanvas::menu_command(int cmd) case CMD_NOTELEN_16: new_len=TICKS_PER_WHOLE/16; break; case CMD_NOTELEN_32: new_len=TICKS_PER_WHOLE/32; break; case CMD_NOTELEN_LAST: new_len=-1; break; + default: cerr << "ERROR: ILLEGAL FUNCTION CALL: ScoreCanvas::menu_command called with unknown command ("<