summaryrefslogtreecommitdiff
path: root/muse2/muse
diff options
context:
space:
mode:
authorFlorian Jung <flo@windfisch.org>2011-09-09 15:55:09 +0000
committerFlorian Jung <flo@windfisch.org>2011-09-09 15:55:09 +0000
commit38c5cccc7273247353264bb7dc97f42296d8e259 (patch)
treeaa148b85e0169c6a10a680678ad7bdb8aec62b5d /muse2/muse
parentb95aeb4245e9bc6e67df6cd489c5f29192c962cf (diff)
began with improved pasting in editors
still TODO: - show a dialog - test it - add generated parts to editor automatically
Diffstat (limited to 'muse2/muse')
-rw-r--r--muse2/muse/functions.cpp198
-rw-r--r--muse2/muse/functions.h4
-rw-r--r--muse2/muse/midiedit/drumedit.cpp4
-rw-r--r--muse2/muse/midiedit/ecanvas.cpp6
-rw-r--r--muse2/muse/midiedit/pianoroll.cpp4
-rw-r--r--muse2/muse/midiedit/scoreedit.cpp10
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<Part*>& 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<Part*>& parts, int range)
{
- map<Event*, Part*> 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<Event*, Part*>::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<Part*>::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<Part*>& 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<Part*>::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<Part*>& 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<Part*>& 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<Part*>& 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<Part*, unsigned> 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<Part*, unsigned>::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<Part*>& 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<Part*>& 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<Part*>& 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"