From 38c5cccc7273247353264bb7dc97f42296d8e259 Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Fri, 9 Sep 2011 15:55:09 +0000 Subject: began with improved pasting in editors still TODO: - show a dialog - test it - add generated parts to editor automatically --- muse2/muse/functions.cpp | 198 ++++++++++++++++++++++++++------------ muse2/muse/functions.h | 4 +- muse2/muse/midiedit/drumedit.cpp | 4 +- muse2/muse/midiedit/ecanvas.cpp | 6 +- muse2/muse/midiedit/pianoroll.cpp | 4 +- muse2/muse/midiedit/scoreedit.cpp | 10 +- 6 files changed, 154 insertions(+), 72 deletions(-) diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index d410844a..bacd6e34 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -8,6 +8,7 @@ #include "functions.h" #include "song.h" #include "undo.h" +#include "helper.h" #include "event.h" #include "audio.h" @@ -848,34 +849,28 @@ void copy_notes(const set& parts, int range) QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard); } -void paste_notes(Part* dest_part) +void paste_notes(int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part) { - QString tmp="x-muse-eventlist"; // QClipboard::text() expects a QString&, not a QString :( + QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :( QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim. - paste_at(dest_part, s, song->cpos()); + paste_at(s, song->cpos(), max_distance, always_new_part, never_new_part, paste_into_part); } + +// if nothing is selected/relevant, this function returns NULL QMimeData* selected_events_to_mime(const set& parts, int range) { - map events=get_events(parts,range); - - //--------------------------------------------------- - // generate event list from selected events - //--------------------------------------------------- - - EventList el; - unsigned startTick = MAXINT; //will be the tick of the first event or MAXINT if no events are there - - for (map::iterator it=events.begin(); it!=events.end(); it++) - { - Event& e = *it->first; - - if (e.tick() < startTick) - startTick = e.tick(); - - el.add(e); - } - + unsigned start_tick = MAXINT; //will be the tick of the first event or MAXINT if no events are there + + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++) + if (is_relevant(ev->second, *part, range)) + if (ev->second.tick() < start_tick) + start_tick=ev->second.tick(); + + if (start_tick == MAXINT) + return NULL; + //--------------------------------------------------- // write events as XML into tmp file //--------------------------------------------------- @@ -890,10 +885,14 @@ QMimeData* selected_events_to_mime(const set& parts, int range) Xml xml(tmp); int level = 0; - xml.tag(level++, "eventlist"); - for (ciEvent e = el.begin(); e != el.end(); ++e) - e->second.write(level, xml, -startTick); - xml.etag(--level, "eventlist"); + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + { + xml.tag(level++, "eventlist part_id=\"%d\"", (*part)->sn()); + for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++) + if (is_relevant(ev->second, *part, range)) + ev->second.write(level, xml, -start_tick); + xml.etag(--level, "eventlist"); + } //--------------------------------------------------- // read tmp file into drag Object @@ -903,7 +902,7 @@ QMimeData* selected_events_to_mime(const set& parts, int range) struct stat f_stat; if (fstat(fileno(tmp), &f_stat) == -1) { - fprintf(stderr, "PianoCanvas::copy() fstat failed:<%s>\n", + fprintf(stderr, "copy_notes() fstat failed:<%s>\n", strerror(errno)); fclose(tmp); return 0; @@ -916,7 +915,7 @@ QMimeData* selected_events_to_mime(const set& parts, int range) QByteArray data(fbuf); QMimeData* md = new QMimeData(); - md->setData("text/x-muse-eventlist", data); + md->setData("text/x-muse-groupedeventlists", data); munmap(fbuf, n); fclose(tmp); @@ -924,10 +923,52 @@ QMimeData* selected_events_to_mime(const set& parts, int range) return md; } -void paste_at(Part* dest_part, const QString& pt, int pos) +bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id) // true on success, false on failure +{ + *part_id = -1; + + for (;;) + { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + return false; + + case Xml::Attribut: + if (tag == "part_id") + *part_id = xml.s2().toInt(); + else + printf("unknown attribute '%s' in read_eventlist_and_part(), ignoring it...\n", tag.toAscii().data()); + break; + + case Xml::TagStart: + if (tag == "event") + { + Event e(Note); + e.read(xml); + el->add(e); + } + else + xml.unknown("read_eventlist_and_part"); + break; + + case Xml::TagEnd: + if (tag == "eventlist") + return true; + + default: + break; + } + } +} + +void paste_at(const QString& pt, int pos, int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part) { Undo operations; - unsigned newpartlen=dest_part->lenTick(); + map expand_map; Xml xml(pt.toLatin1().constData()); for (;;) @@ -938,53 +979,82 @@ void paste_at(Part* dest_part, const QString& pt, int pos) { case Xml::Error: case Xml::End: - goto end_of_paste_at; + goto out_of_paste_at_for; case Xml::TagStart: if (tag == "eventlist") { EventList el; - el.read(xml, "eventlist", true); - for (iEvent i = el.begin(); i != el.end(); ++i) + int part_id; + + if (read_eventlist_and_part(xml, &el, &part_id)) { - Event e = i->second; - int tick = e.tick() + pos - dest_part->tick(); - if (tick<0) + Part* dest_part; + Track* dest_track; + + if (paste_into_part == NULL) + dest_part = partFromSerialNumber(part_id); + else + dest_part=paste_into_part; + + if (dest_part == NULL) { - printf("ERROR: trying to add event before current part!\n"); - goto end_of_paste_at; + printf("ERROR: destination part wasn't found. ignoring these events\n"); } - - e.setTick(tick); - e.setSelected(true); - - if (e.endTick() > dest_part->lenTick()) // event exceeds part? + else { - if (dest_part->hasHiddenEvents()) // auto-expanding is forbidden? + dest_track=dest_part->track(); + + unsigned first_paste_tick = el.begin()->first + pos; + if ( (dest_part->tick() > first_paste_tick) || // dest_part begins too late + ( ( (dest_part->endTick() + max_distance < first_paste_tick) || // dest_part is too far away + always_new_part ) && !never_new_part ) ) { - if (e.tick() < dest_part->lenTick()) - e.setLenTick(dest_part->lenTick() - e.tick()); // clip - else - e.setLenTick(0); // don't insert that note at all + dest_part = dest_track->newPart(); + dest_part->setTick(AL::sigmap.raster1(first_paste_tick, config.division)); + operations.push_back(UndoOp(UndoOp::AddPart, dest_part)); } - else + + for (iEvent i = el.begin(); i != el.end(); ++i) { - if (e.endTick() > newpartlen) - newpartlen=e.endTick(); + Event e = i->second; + int tick = e.tick() + pos - dest_part->tick(); + if (tick<0) + { + printf("ERROR: trying to add event before current part! ignoring this event\n"); + continue; + } + + e.setTick(tick); + e.setSelected(true); + + if (e.endTick() > dest_part->lenTick()) // event exceeds part? + { + if (dest_part->hasHiddenEvents()) // auto-expanding is forbidden? + { + if (e.tick() < dest_part->lenTick()) + e.setLenTick(dest_part->lenTick() - e.tick()); // clip + else + e.setLenTick(0); // don't insert that note at all + } + else + { + if (e.endTick() > expand_map[dest_part]) + expand_map[dest_part]=e.endTick(); + } + } + + if (e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false)); } } - - if (e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false)); } - - if (newpartlen != dest_part->lenTick()) - schedule_resize_all_same_len_clone_parts(dest_part, newpartlen, operations); - - song->applyOperationGroup(operations); - goto end_of_paste_at; + else + { + printf("ERROR: reading eventlist from clipboard failed. ignoring this one...\n"); + } } else - xml.unknown("paste_at"); + xml.unknown("paste_at"); break; case Xml::Attribut: @@ -994,7 +1064,13 @@ void paste_at(Part* dest_part, const QString& pt, int pos) } } - end_of_paste_at: + out_of_paste_at_for: + + for (map::iterator it = expand_map.begin(); it!=expand_map.end(); it++) + if (it->second != it->first->lenTick()) + schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); + + song->applyOperationGroup(operations); song->update(SC_SELECTION); } diff --git a/muse2/muse/functions.h b/muse2/muse/functions.h index 1d3233b7..cc2768cb 100644 --- a/muse2/muse/functions.h +++ b/muse2/muse/functions.h @@ -69,9 +69,9 @@ bool legato(); //functions for copy'n'paste void copy_notes(const std::set& parts, int range); -void paste_notes(Part* dest_part); +void paste_notes(int max_distance=3072, bool always_new_part=false, bool never_new_part=false, Part* paste_into_part=NULL); QMimeData* selected_events_to_mime(const std::set& parts, int range); -void paste_at(Part* dest_part, const QString& pt, int pos); +void paste_at(const QString& pt, int pos, int max_distance=3072, bool always_new_part=false, bool never_new_part=false, Part* paste_into_part=NULL); //functions for selections void select_all(const std::set& parts); diff --git a/muse2/muse/midiedit/drumedit.cpp b/muse2/muse/midiedit/drumedit.cpp index 14419e0f..1553ec2b 100644 --- a/muse2/muse/midiedit/drumedit.cpp +++ b/muse2/muse/midiedit/drumedit.cpp @@ -904,7 +904,7 @@ void DrumEdit::cmd(int cmd) case DrumCanvas::CMD_COPY: copy_notes(partlist_to_set(parts()), 1); break; case DrumCanvas::CMD_PASTE: ((DrumCanvas*)canvas)->cmd(DrumCanvas::CMD_SELECT_NONE); - paste_notes(canvas->part()); + paste_notes(); // (canvas->part()); TODO FINDMICHJETZT break; case DrumCanvas::CMD_LOAD: load(); break; case DrumCanvas::CMD_SAVE: save(); break; @@ -936,7 +936,7 @@ void DrumEdit::cmd(int cmd) void DrumEdit::clipboardChanged() { - pasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); + pasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); } //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/ecanvas.cpp b/muse2/muse/midiedit/ecanvas.cpp index a829650c..a0545412 100644 --- a/muse2/muse/midiedit/ecanvas.cpp +++ b/muse2/muse/midiedit/ecanvas.cpp @@ -379,13 +379,13 @@ void EventCanvas::viewDropEvent(QDropEvent* event) //event->ignore(); // TODO CHECK Tim. return; } - if (event->mimeData()->hasFormat("text/x-muse-eventlist")) { - text = QString(event->mimeData()->data("text/x-muse-eventlist")); + if (event->mimeData()->hasFormat("text/x-muse-groupedeventlists")) { + text = QString(event->mimeData()->data("text/x-muse-groupedeventlists")); int x = editor->rasterVal(event->pos().x()); if (x < 0) x = 0; - paste_at(curPart, text, x); + paste_at(text,x); //(curPart, text, x); TODO FINDMICHJETZT //event->accept(); // TODO } else { diff --git a/muse2/muse/midiedit/pianoroll.cpp b/muse2/muse/midiedit/pianoroll.cpp index 89f84062..258efe19 100644 --- a/muse2/muse/midiedit/pianoroll.cpp +++ b/muse2/muse/midiedit/pianoroll.cpp @@ -617,7 +617,7 @@ void PianoRoll::cmd(int cmd) case PianoCanvas::CMD_COPY: copy_notes(partlist_to_set(parts()), 1); break; case PianoCanvas::CMD_PASTE: ((PianoCanvas*)canvas)->cmd(PianoCanvas::CMD_SELECT_NONE); - paste_notes(canvas->part()); + paste_notes(); //(canvas->part()); TODO FINDMICHJETZT break; case PianoCanvas::CMD_MODIFY_GATE_TIME: modify_notelen(partlist_to_set(parts())); break; case PianoCanvas::CMD_MODIFY_VELOCITY: modify_velocity(partlist_to_set(parts())); break; @@ -1180,7 +1180,7 @@ void PianoRoll::setEventColorMode(int mode) void PianoRoll::clipboardChanged() { - editPasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); + editPasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); } //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index b65192e5..72740a8e 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -679,7 +679,7 @@ void ScoreEdit::menu_command(int cmd) 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()); + paste_notes(); //(score_canvas->get_selected_part()); TODO FINDMICHJETZT break; case CMD_QUANTIZE: quantize_notes(score_canvas->get_all_parts()); break; case CMD_VELOCITY: modify_velocity(score_canvas->get_all_parts()); break; @@ -700,7 +700,7 @@ void ScoreEdit::menu_command(int cmd) void ScoreEdit::clipboard_changed() { - paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); + paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); } void ScoreEdit::selection_changed() @@ -4486,10 +4486,16 @@ void ScoreEdit::keyPressEvent(QKeyEvent* event) * changing "share" status, the changed state isn't stored * * CURRENT TODO + * o when pasting and creating new parts, inform the editors about that! + * o TEST pasting in editors! + * o pasting in editors: add dialogs + * * ! o fix sigedit boxes * M o remove that ugly "bool initalizing" stuff. it's probably unneeded (watch out for the FINDMICH message) * o mirror most menus to an additional right-click context menu to avoid the long mouse pointer * journey to the menu bar. try to find a way which does not involve duplicate code! + * o sane defaults for toolbars + * o paste in midi editors * o implement borland-style maximize: free windows do not cover the main menu, even when maximized * o smart range selection: if range markers have been used recently (that is, a dialog with * "range" setting, or they've been modified), default to "in range" or "selected in range" -- cgit v1.2.3