From 9187899632c14d64b3fae6477b7f941240f912a6 Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Thu, 2 Jun 2011 16:37:28 +0000 Subject: the score editor now supports batch-movements the functions return as bool if undo has been triggered --- muse2/ChangeLog | 6 ++ muse2/muse/functions.cpp | 87 ++++++++++++++++----- muse2/muse/functions.h | 23 +++--- muse2/muse/midiedit/scoreedit.cpp | 160 +++++++++++++++++++------------------- muse2/muse/midiedit/scoreedit.h | 16 ++-- 5 files changed, 174 insertions(+), 118 deletions(-) (limited to 'muse2') diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 41bfbb73..1af47f28 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,9 @@ +02.06.2011: + - the score editor now allows batch-movements, that is, + you can transpose or move the whole selection and not + only single notes (flo93) + - the functions now return as boolean, if they have triggered + undo (flo93) 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) diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index 8b17bd8d..6fab2ee4 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -64,6 +64,13 @@ set partlist_to_set(PartList* pl) return result; } +set part_to_set(Part* p) +{ + set result; + result.insert(p); + return result; +} + bool is_relevant(const Event& event, const Part* part, int range) { unsigned tick; @@ -206,7 +213,7 @@ bool legato(const set& parts) -void modify_velocity(const set& parts, int range, int rate, int offset) +bool modify_velocity(const set& parts, int range, int rate, int offset) { map events = get_events(parts, range); @@ -239,10 +246,13 @@ void modify_velocity(const set& parts, int range, int rate, int offset) } song->endUndo(SC_EVENT_MODIFIED); + return true; } + else + return false; } -void modify_off_velocity(const set& parts, int range, int rate, int offset) +bool modify_off_velocity(const set& parts, int range, int rate, int offset) { map events = get_events(parts, range); @@ -275,10 +285,13 @@ void modify_off_velocity(const set& parts, int range, int rate, int offse } song->endUndo(SC_EVENT_MODIFIED); + return true; } + else + return false; } -void modify_notelen(const set& parts, int range, int rate, int offset) +bool modify_notelen(const set& parts, int range, int rate, int offset) { map events = get_events(parts, range); @@ -309,12 +322,15 @@ void modify_notelen(const set& parts, int range, int rate, int offset) } song->endUndo(SC_EVENT_MODIFIED); + return true; } + else + return false; } -void set_notelen(const set& parts, int range, int len) +bool set_notelen(const set& parts, int range, int len) { - modify_notelen(parts, range, 0, len); + return modify_notelen(parts, range, 0, len); } unsigned quantize_tick(unsigned tick, unsigned raster, int swing) @@ -339,7 +355,7 @@ unsigned quantize_tick(unsigned tick, unsigned raster, int swing) return tick_dest3; } -void quantize_notes(const set& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold) +bool quantize_notes(const set& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold) { map events = get_events(parts, range); bool undo_started=false; @@ -388,9 +404,11 @@ void quantize_notes(const set& parts, int range, int raster, bool quant_l if (undo_started) song->endUndo(SC_EVENT_MODIFIED); } + + return undo_started; } -void erase_notes(const set& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used) +bool erase_notes(const set& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used) { map events = get_events(parts, range); @@ -409,16 +427,19 @@ void erase_notes(const set& parts, int range, int velo_threshold, bool ve } song->endUndo(SC_EVENT_REMOVED); + return true; } + else + return false; } -void transpose_notes(const set& parts, int range, signed int halftonesteps) +bool transpose_notes(const set& parts, int range, signed int halftonesteps, bool do_undo) { map events = get_events(parts, range); if ( (!events.empty()) && (halftonesteps!=0) ) { - song->startUndo(); + if (do_undo) song->startUndo(); for (map::iterator it=events.begin(); it!=events.end(); it++) { @@ -434,11 +455,14 @@ void transpose_notes(const set& parts, int range, signed int halftonestep audio->msgChangeEvent(event, newEvent, part, false, false, false); } - song->endUndo(SC_EVENT_MODIFIED); + if (do_undo) song->endUndo(SC_EVENT_MODIFIED); + return do_undo; } + else + return false; } -void crescendo(const set& parts, int range, int start_val, int end_val, bool absolute) +bool crescendo(const set& parts, int range, int start_val, int end_val, bool absolute) { map events = get_events(parts, range); @@ -473,33 +497,56 @@ void crescendo(const set& parts, int range, int start_val, int end_val, b } song->endUndo(SC_EVENT_MODIFIED); + return true; } + else + return false; } -void move_notes(const set& parts, int range, signed int ticks) //TODO FINDMICH: safety checks +bool move_notes(const set& parts, int range, signed int ticks, bool do_undo) //TODO: clipping { map events = get_events(parts, range); if ( (!events.empty()) && (ticks!=0) ) { - song->startUndo(); + if (do_undo) song->startUndo(); for (map::iterator it=events.begin(); it!=events.end(); it++) { Event& event=*(it->first); Part* part=it->second; + bool del=false; Event newEvent = event.clone(); - newEvent.setTick(event.tick()+ticks); - // Indicate no undo, and do not do port controller values and clone parts. - audio->msgChangeEvent(event, newEvent, part, false, false, false); + if ((signed)event.tick()+ticks < 0) //don't allow moving before the part's begin + newEvent.setTick(0); + else + newEvent.setTick(event.tick()+ticks); + + if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end, clip + { + if (part->lenTick() > newEvent.tick()) + newEvent.setLenTick(part->lenTick() - newEvent.tick()); + else + del=true; //if the new length would be <= 0, erase the note + } + + if (del==false) + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgChangeEvent(event, newEvent, part, false, false, false); + else + // Indicate no undo, and do not do port controller values and clone parts. + audio->msgDeleteEvent(event, part, false, false, false); } - song->endUndo(SC_EVENT_MODIFIED); + if (do_undo) song->endUndo(SC_EVENT_MODIFIED); + return do_undo; } + else + return false; } -void delete_overlaps(const set& parts, int range) +bool delete_overlaps(const set& parts, int range) { map events = get_events(parts, range); bool undo_started=false; @@ -556,9 +603,10 @@ void delete_overlaps(const set& parts, int range) if (undo_started) song->endUndo(SC_EVENT_MODIFIED); } + return undo_started; } -void legato(const set& parts, int range, int min_len, bool dont_shorten) +bool legato(const set& parts, int range, int min_len, bool dont_shorten) { map events = get_events(parts, range); bool undo_started=false; @@ -610,6 +658,7 @@ void legato(const set& parts, int range, int min_len, bool dont_shorten) if (undo_started) song->endUndo(SC_EVENT_MODIFIED); } + return undo_started; } diff --git a/muse2/muse/functions.h b/muse2/muse/functions.h index 6826a9b7..226c43f7 100644 --- a/muse2/muse/functions.h +++ b/muse2/muse/functions.h @@ -40,20 +40,21 @@ void init_function_dialogs(QWidget* parent); std::set partlist_to_set(PartList* pl); +std::set part_to_set(Part* p); std::map get_events(const std::set& parts, int range); //these functions simply do their job, non-interactively -void modify_velocity(const std::set& parts, int range, int rate, int offset=0); -void modify_off_velocity(const std::set& parts, int range, int rate, int offset=0); -void modify_notelen(const std::set& parts, int range, int rate, int offset=0); -void quantize_notes(const std::set& parts, int range, int raster, bool len=false, int strength=100, int swing=0, int threshold=0); -void erase_notes(const std::set& parts, int range, int velo_threshold=0, bool velo_thres_used=false, int len_threshold=0, bool len_thres_used=false); -void delete_overlaps(const std::set& parts, int range); -void set_notelen(const std::set& parts, int range, int len); -void move_notes(const std::set& parts, int range, signed int ticks); -void transpose_notes(const std::set& parts, int range, signed int halftonesteps); -void crescendo(const std::set& parts, int range, int start_val, int end_val, bool absolute); -void legato(const std::set& parts, int range, int min_len=1, bool dont_shorten=false); +bool modify_velocity(const std::set& parts, int range, int rate, int offset=0); +bool modify_off_velocity(const std::set& parts, int range, int rate, int offset=0); +bool modify_notelen(const std::set& parts, int range, int rate, int offset=0); +bool quantize_notes(const std::set& parts, int range, int raster, bool len=false, int strength=100, int swing=0, int threshold=0); +bool erase_notes(const std::set& parts, int range, int velo_threshold=0, bool velo_thres_used=false, int len_threshold=0, bool len_thres_used=false); +bool delete_overlaps(const std::set& parts, int range); +bool set_notelen(const std::set& parts, int range, int len); +bool move_notes(const std::set& parts, int range, signed int ticks, bool do_undo=true); +bool transpose_notes(const std::set& parts, int range, signed int halftonesteps, bool do_undo=true); +bool crescendo(const std::set& parts, int range, int start_val, int end_val, bool absolute); +bool legato(const std::set& parts, int range, int min_len=1, bool dont_shorten=false); //the below functions automatically open the dialog diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index e15e2ec4..0660ebc5 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -353,7 +353,7 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) edit_menu->addSeparator(); del_action = edit_menu->addAction(tr("Delete &Events")); - menu_mapper->setMapping(del_action, CMD_ERASE); + menu_mapper->setMapping(del_action, CMD_DEL); connect(del_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); edit_menu->addSeparator(); @@ -702,6 +702,7 @@ void ScoreEdit::menu_command(int cmd) case CMD_NOTELEN: modify_notelen(score_canvas->get_all_parts()); break; case CMD_TRANSPOSE: transpose_notes(score_canvas->get_all_parts()); break; case CMD_ERASE: erase_notes(score_canvas->get_all_parts()); break; + case CMD_DEL: erase_notes(score_canvas->get_all_parts(),1); break; case CMD_MOVE: move_notes(score_canvas->get_all_parts()); break; case CMD_FIXED_LEN: set_notelen(score_canvas->get_all_parts()); break; case CMD_DELETE_OVERLAPS: delete_overlaps(score_canvas->get_all_parts()); break; @@ -1183,13 +1184,12 @@ ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget) : View(parent_wi x_left=0; y_pos=0; have_lasso=false; + inserting=false; dragging=false; drag_cursor_changed=false; mouse_erases_notes=false; mouse_inserts_notes=true; - undo_started=false; - undo_flags=0; selected_part=NULL; @@ -3520,10 +3520,9 @@ int ScoreCanvas::y_to_pitch(int y, int t, clef_t clef) void ScoreCanvas::mousePressEvent (QMouseEvent* event) { - keystate=((QInputEvent*)event)->modifiers(); - + keystate=event->modifiers(); bool ctrl=keystate & Qt::ControlModifier; - + // den errechneten tick immer ABrunden! // denn der "bereich" eines schlags geht von schlag_begin bis nächsterschlag_begin-1 // noten werden aber genau in die mitte dieses bereiches gezeichnet @@ -3534,10 +3533,6 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) int x=event->x()+x_pos-x_left; int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); - if (event->button()==Qt::LeftButton) - if (!ctrl) - deselect_all(); - if (staff_it!=staves.end()) { if (event->x() <= x_left) //clicked in the preamble? @@ -3627,9 +3622,10 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) + clicked_event_ptr=set_it->source_event; dragged_event=*set_it->source_event; + original_dragged_event=dragged_event.clone(); dragged_event_part=set_it->source_part; - dragged_event_original_pitch=dragged_event.pitch(); if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase? { @@ -3637,14 +3633,9 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) } else if (event->button()==Qt::LeftButton) //edit? { - set_it->source_event->setSelected(!set_it->source_event->selected()); - song_changed(SC_SELECTION); - setMouseTracking(true); dragging=true; drag_cursor_changed=false; - undo_started=false; - undo_flags=SC_EVENT_MODIFIED; } } else //we found nothing? @@ -3676,11 +3667,9 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) if (relative_tick<0) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl; song->startUndo(); - undo_started=true; - undo_flags=SC_EVENT_INSERTED | SC_EVENT_MODIFIED; - //stopping undo at the end of this function is unneccessary - //because we'll begin a drag right after it. finishing - //this drag will stop undo as well (in mouseReleaseEvent) + + if (!ctrl) + deselect_all(); Event newevent(Note); newevent.setPitch(y_to_pitch(y,tick, staff_it->clef)); @@ -3689,7 +3678,7 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) newevent.setTick(relative_tick); newevent.setLenTick((new_len>0)?new_len:last_len); newevent.setSelected(true); - + if (flo_quantize(newevent.lenTick(), quant_ticks()) <= 0) { newevent.setLenTick(quant_ticks()); @@ -3707,7 +3696,7 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) dragged_event_part=curr_part; dragged_event=newevent; - dragged_event_original_pitch=newevent.pitch(); + original_dragged_event=dragged_event.clone(); mouse_down_pos=event->pos(); mouse_operation=NO_OP; @@ -3717,9 +3706,12 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) setMouseTracking(true); dragging=true; + inserting=true; drag_cursor_changed=true; setCursor(Qt::SizeAllCursor); - //song->startUndo(); unneccessary because we have started it already above + + song->endUndo(SC_EVENT_INSERTED); + song->update(SC_SELECTION); } } else // !mouse_inserts_notes. open a lasso @@ -3733,16 +3725,16 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) } } } - - if (event->button()==Qt::LeftButton) - song->update(SC_SELECTION); } void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) { + keystate=event->modifiers(); + bool ctrl=keystate & Qt::ControlModifier; + if (dragging && event->button()==Qt::LeftButton) { - if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //also BEGIN can change the len by clipping + if (mouse_operation==LENGTH) { if (flo_quantize(dragged_event.lenTick(), quant_ticks()) <= 0) { @@ -3753,15 +3745,29 @@ void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) { last_len=flo_quantize(dragged_event.lenTick(), quant_ticks()); } + + if (undo_started) + song->endUndo(SC_EVENT_MODIFIED | SC_EVENT_REMOVED); } + - if (undo_started) - song->endUndo(undo_flags); + if (mouse_operation==NO_OP && !inserting) + { + if (event->button()==Qt::LeftButton) + if (!ctrl) + deselect_all(); + + clicked_event_ptr->setSelected(!clicked_event_ptr->selected()); + + song->update(SC_SELECTION); + } setMouseTracking(false); unsetCursor(); + inserting=false; dragging=false; drag_cursor_changed=false; + undo_started=false; x_scroll_speed=0; x_scroll_pos=0; } @@ -3791,6 +3797,9 @@ void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) if (have_lasso && event->button()==Qt::LeftButton) { + if (!ctrl) + deselect_all(); + set already_processed; for (list::iterator it=staves.begin(); it!=staves.end(); it++) @@ -3808,6 +3817,9 @@ void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) { + keystate=event->modifiers(); + bool ctrl=keystate & Qt::ControlModifier; + if (dragging) { int dx=event->x()-mouse_down_pos.x(); @@ -3838,6 +3850,22 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) mouse_operation=PITCH; setCursor(Qt::SizeVerCursor); } + + if (mouse_operation!=NO_OP) + { + if (!inserting && clicked_event_ptr->selected()==false) + { + if (!ctrl) + deselect_all(); + + clicked_event_ptr->setSelected(true); + + song->update(SC_SELECTION); + } + + old_pitch=-1; + old_dest_tick=MAXINT; + } } int new_pitch; @@ -3848,70 +3876,41 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) break; case PITCH: - if (debugMsg) cout << "changing pitch, delta="< dragged_event_part->lenTick()) + if (dest_tick != old_dest_tick) { - signed new_len=dragged_event_part->lenTick() - tmp.tick(); - if (new_len>=0) - { - tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); - if (debugMsg) cout << "moved note would exceed its part; clipping length to " << tmp.lenTick() << endl; - } - else - { - tmp.setLenTick(0); - if (debugMsg) cout << "moved note would exceed its part; clipping length to 0 (actually negative)" << endl; - } + if (undo_started) song->undo(); + undo_started=move_notes(part_to_set(dragged_event_part),1, (signed)dest_tick-original_dragged_event.tick()); + old_dest_tick=dest_tick; } - - audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); - dragged_event=tmp; - - fully_recalculate(); } break; @@ -4332,14 +4331,6 @@ void ScoreCanvas::deselect_all() song->update(SC_SELECTION); } -void ScoreCanvas::keyPressEvent(QKeyEvent* event) -{ - if (event->key()==Qt::Key_Delete) - { - erase_notes(get_all_parts(), 1); // 1 means "all selected" - } -} - bool staff_t::cleanup_parts() { bool did_something=false; @@ -4469,8 +4460,13 @@ void staff_t::update_part_indices() * between, for example, when a cis is tied to a des * * CURRENT TODO + * o batch-movements: they may be destructive: if you move a chord + * upwards, so that some notes get clipped, + * they'll appear "damaged" in undo/redo + * maybe DO apply stuff with undo/redo, but count + * n_steps, and undo all steps before really + * applying the operation then. * o allow batch-movements in score editor - * o in main win: make "Ch" column editable with a line edit * o either remove these "hidden notes", or deal with them in the score editor * o investigate with valgrind * o controller view in score editor diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index cd08d4b0..1c45f578 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -64,7 +64,7 @@ enum {CMD_COLOR_BLACK, CMD_COLOR_VELO, CMD_COLOR_PART, CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN, CMD_TRANSPOSE, CMD_ERASE, CMD_MOVE, CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO, - CMD_CUT, CMD_COPY, CMD_PASTE, + CMD_CUT, CMD_COPY, CMD_PASTE, CMD_DEL, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP}; @@ -697,19 +697,24 @@ class ScoreCanvas : public View bool mouse_erases_notes; bool mouse_inserts_notes; + bool inserting; bool dragging; bool drag_cursor_changed; Part* dragged_event_part; Event dragged_event; - int dragged_event_original_pitch; + Event original_dragged_event; + Event* clicked_event_ptr; + + int old_pitch; + unsigned old_dest_tick; + + bool undo_started; + bool temp_undo; bool have_lasso; QPoint lasso_start; QRect lasso; - bool undo_started; - int undo_flags; - bool srec; bool held_notes[128]; @@ -783,7 +788,6 @@ class ScoreCanvas : public View virtual void mouseMoveEvent (QMouseEvent* event); virtual void mouseReleaseEvent (QMouseEvent* event); virtual void resizeEvent(QResizeEvent*); - virtual void keyPressEvent(QKeyEvent* event); public: ScoreCanvas(ScoreEdit*, QWidget*); -- cgit v1.2.3