From e2084952744efe4c6cf9137ba0c544c115064d5f Mon Sep 17 00:00:00 2001 From: "Tim E. Real" Date: Fri, 5 Aug 2011 08:28:29 +0000 Subject: Parts having hidden events now drawn using 'jagged' ends and special white edges. Improved border drawing guaranteed to show L/R touching part borders. TODO: Top and bottom borders. Muted parts now show names and events. Filled with a special brush pattern for easy recognition. Huge changes to PartCanvas::drawItem(). And to all View::XmapXXX methods for better accuracy. Changed bool Part::hasHiddenNotes() to int Part::hasHiddenEvents(). Added int Part::cachedHasHiddenEvents(). --- muse2/ChangeLog | 6 + muse2/muse/arranger/pcanvas.cpp | 909 +++++++++++++++++++++++++++++++++----- muse2/muse/arranger/pcanvas.h | 6 + muse2/muse/driver/jack.cpp | 18 +- muse2/muse/functions.cpp | 6 +- muse2/muse/midiedit/dcanvas.cpp | 4 +- muse2/muse/midiedit/prcanvas.cpp | 6 +- muse2/muse/midiedit/scoreedit.cpp | 4 +- muse2/muse/part.cpp | 40 +- muse2/muse/part.h | 13 +- muse2/muse/steprec.cpp | 4 +- muse2/muse/widgets/canvas.cpp | 143 +++++- muse2/muse/widgets/canvas.h | 1 + muse2/muse/widgets/view.cpp | 178 +++++++- muse2/muse/widgets/view.h | 5 + 15 files changed, 1185 insertions(+), 158 deletions(-) diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 625ab3f4..854d87bf 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,9 @@ +05.08.2011: + - Parts having hidden events now drawn using 'jagged' ends and special white edges. (Tim p4.0.29) + - Improved border drawing guaranteed to show L/R touching part borders. TODO: Top and bottom borders. (Tim) + - Muted parts now show names and events. Filled with a special brush pattern for easy recognition. (Tim) + - Huge changes to PartCanvas::drawItem(). And to all View::XmapXXX methods for better accuracy. (Tim) + - Changed bool Part::hasHiddenNotes() to int Part::hasHiddenEvents(). Added int Part::cachedHasHiddenEvents(). (Tim) 02.08.2011: - Changed behaviour of part clones (flo93) diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index 7298194c..402e8035 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: pcanvas.cpp,v 1.48.2.26 2009/11/22 11:08:33 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #include @@ -43,7 +44,6 @@ #include "midictrl.h" #include "utils.h" - //--------------------------------------------------------- // colorRect // paints a rectangular icon with a given color @@ -67,6 +67,9 @@ QIcon colorRect(const QColor& color, int width, int height) { NPart::NPart(Part* e) : CItem(Event(), e) { + leftBorderTouches = false; + rightBorderTouches = false; + int th = track()->height(); int y = track()->y(); @@ -416,17 +419,31 @@ QPoint PartCanvas::raster(const QPoint& p) const void PartCanvas::partsChanged() { items.clear(); - int idx = 0; for (iTrack t = tracks->begin(); t != tracks->end(); ++t) { PartList* pl = (*t)->parts(); for (iPart i = pl->begin(); i != pl->end(); ++i) { - NPart* np = new NPart(i->second); + Part* part = i->second; + NPart* np = new NPart(part); items.add(np); if (i->second->selected()) { selectItem(np, true); } - } - ++idx; + + // Check for touching borders. p4.0.29 + Part* pp; + for(ciPart ii = pl->begin(); ii != pl->end(); ++ii) + { + pp = ii->second; + if(pp == part) // Ignore this part + continue; + if(pp->tick() > part->endTick()) + break; + if(pp->endTick() == part->tick()) + np->leftBorderTouches = true; + if(pp->tick() == part->endTick()) + np->rightBorderTouches = true; + } + } } redraw(); } @@ -1273,6 +1290,7 @@ void PartCanvas::keyPress(QKeyEvent* event) // draws a part //--------------------------------------------------------- +#if 0 void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& rect) { int from = rect.x(); @@ -1296,84 +1314,215 @@ void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& rect) } QRect r = item->bbox(); + bool clone = part->events()->arefCount() > 1; + QBrush brush; - int i = part->colorIndex(); - p.setPen(Qt::black); - if (part->mute()) { - QColor c(Qt::white); + //QRect rr = map(r); + //QRect rr = p.transform().mapRect(r); + //printf("PartCanvas::drawItem called map rx:%d rw:%d rrx:%d rrw:%d\n", r.x(), r.width(), rr.x(), rr.width()); + //printf("PartCanvas::drawItem called map rx:%d rw:%d\n", r.x(), r.width()); + //p.save(); + //p.setWorldMatrixEnabled(false); + + // NOTE: Optimization: For each item, hasHiddenEvents() is called once in Canvas::draw(), and we use cachedHasHiddenEvents(). + // Not used for now. + //int het = part->cachedHasHiddenEvents(); + int het = part->hasHiddenEvents(); + + int y_1 = rmapyDev(1); // Hack, try to replace. + double xs_0 = r.x(); + double xe_0 = xs_0 + r.width(); + double xs_m0 = rmapx_f(xs_0); + double xe_m0 = rmapx_f(xe_0); + double xs_1 = rmapxDev_f(xs_m0 + 1.0); + if(xs_1 > xe_0) + xs_1 = xe_0; + double xs_2 = rmapxDev_f(xs_m0 + 2.0); + if(xs_2 > xe_0) + xs_2 = xe_0; + double xs_6 = rmapxDev_f(xs_m0 + 6.0); + if(xs_6 > xe_0) + xs_6 = xe_0; + + double xe_1 = rmapxDev_f(xe_m0 - 1.0); + if(xe_1 < xs_0) + xe_1 = xs_0; + double xe_2 = rmapxDev_f(xe_m0 - 2.0); + if(xe_2 < xs_0) + xe_2 = xs_0; + double xe_6 = rmapxDev_f(xe_m0 - 6.0); + if(xe_6 < xs_0) + xe_6 = xs_0; + + double ys_0 = r.y(); + double ye_0 = ys_0 + r.height(); + double ys_m0 = rmapy_f(ys_0); + double ye_m0 = rmapy_f(ye_0); + double ys_1 = rmapyDev_f(ys_m0 + 1.0); + if(ys_1 > ye_0) + ys_1 = ye_0; + double ys_2 = rmapyDev_f(ys_m0 + 2.0); + if(ys_2 > ye_0) + ys_2 = ye_0; + + double ye_1 = rmapyDev_f(ye_m0 - 1.0); + if(ye_1 < ys_0) + ye_1 = ys_0; + double ye_2 = rmapyDev_f(ye_m0 - 2.0); + if(ye_2 < ys_0) + ye_2 = ys_0; + + int cidx = part->colorIndex(); + if (item->isMoving()) + { + QColor c(Qt::gray); c.setAlpha(config.globalAlphaBlend); QLinearGradient gradient(r.topLeft(), r.bottomLeft()); gradient.setColorAt(0, c); gradient.setColorAt(1, c.darker()); - QBrush cc(gradient); - p.setBrush(cc); - - // NOTE: For one-pixel border use first line For two-pixel border use second. - p.drawRect(QRect(r.x(), r.y()-1, r.width(), r.height())); - - return; - } - if (item->isMoving()) { - QColor c(Qt::gray); + brush = QBrush(gradient); + } + else + if (part->selected()) + { + QColor c(Qt::black); + c.setAlpha(config.globalAlphaBlend); + QLinearGradient gradient(r.topLeft(), r.bottomLeft()); + // Use a colour only about 20% lighter than black, rather than the 50% we use in gGradientFromQColor + // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. + //c.setRgba(64, 64, 64, c.alpha()); + gradient.setColorAt(0, QColor(51, 51, 51, config.globalAlphaBlend)); + gradient.setColorAt(1, c); + brush = QBrush(gradient); + } + else + if (part->mute()) + { + QColor c(Qt::white); c.setAlpha(config.globalAlphaBlend); QLinearGradient gradient(r.topLeft(), r.bottomLeft()); gradient.setColorAt(0, c); gradient.setColorAt(1, c.darker()); - QBrush cc(gradient); - p.setBrush(cc); - - // NOTE: For one-pixel border use first line. For two-pixel border use second. - p.drawRect(QRect(r.x(), r.y()-1, r.width(), r.height())); - } - else if (part->selected()) { - bool clone = part->events()->arefCount() > 1; - - // NOTE: For one-pixel border use first line and don't bother with setCosmetic. - // For a two-pixel border use second line and MUST use setCosmetic! Tim. - //p.setPen(QPen(config.partColors[i], 0, clone ? Qt::DashLine : Qt::SolidLine)); - QPen pen(config.partColors[i], 2, clone ? Qt::DashLine : Qt::SolidLine); - pen.setCosmetic(true); - - p.setPen(pen); - // Hm, put some kind of lower limit? If so do that globally to the adjustment. - QColor c(Qt::black); - c.setAlpha(config.globalAlphaBlend); - - QLinearGradient gradient(r.topLeft(), r.bottomLeft()); - //gradient.setColorAt(0, c); - //gradient.setColorAt(1, c.darker()); - // Use a colour only about 20% lighter than black, rather than the 50% we use in gGradientFromQColor - // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. - //c.setRgba(64, 64, 64, c.alpha()); - gradient.setColorAt(0, QColor(51, 51, 51, config.globalAlphaBlend)); - gradient.setColorAt(1, c); - QBrush cc(gradient); - //QBrush cc(gGradientFromQColor(c, r.topLeft(), r.bottomLeft())); + brush = QBrush(gradient); - p.setBrush(cc); - p.drawRect(r); - } - else { - bool clone = part->events()->arefCount() > 1; - - // NOTE: Pixel width: See above note. - //p.setPen(QPen(Qt::black, 0, clone ? Qt::DashLine : Qt::SolidLine)); - QPen pen(Qt::black, 2, clone ? Qt::DashLine : Qt::SolidLine); - pen.setCosmetic(true); - - p.setPen(pen); - QColor c(config.partColors[i]); + // Not good. Aliasing missing lines happening at different mags. + // And it's too much. If notes + automation is displayed (by removing 'return' below), cross hatch interferes. + // TODO: Maybe try to draw a nice gradient SINGLE cross (or STAR) from corner to corner instead. + // Then remove the 'return' below and let it draw the name and notes. + //brush.setStyle(Qt::DiagCrossPattern); + //p.fillRect(rf, brush); + } + else + { + QColor c(config.partColors[cidx]); c.setAlpha(config.globalAlphaBlend); - //QLinearGradient gradient(r.topLeft(), r.bottomLeft()); - //gradient.setColorAt(0, c); - //gradient.setColorAt(1, c.darker()); - //QBrush cc(gradient); - QBrush cc(gGradientFromQColor(c, r.topLeft(), r.bottomLeft())); - p.setBrush(cc); - - p.drawRect(r); - } + brush = QBrush(gGradientFromQColor(c, r.topLeft(), r.bottomLeft())); + } + double h = r.height(); + double s = h / 4.0; + double y0 = r.y(); + double y1 = y0 + s; + double y2 = y0 + s * 2.0; + double y3 = y0 + s * 3.0; + double y4 = y0 + h; + + QPointF points[16]; + int pts; + + // + // Fill the part rectangles, accounting for hidden events by using 'jagged' edges... + // + + p.setBrush(brush); + p.setPen(Qt::NoPen); + if(het) + { + pts = 0; + if(het == (Part::LeftEventsHidden | Part::RightEventsHidden)) + { + points[pts++] = QPointF(xs_0, y0); + points[pts++] = QPointF(xe_0, y0); + points[pts++] = QPointF(xe_6, y1); + points[pts++] = QPointF(xe_0, y2); + points[pts++] = QPointF(xe_6, y3); + points[pts++] = QPointF(xe_0, y4); + points[pts++] = QPointF(xs_0, y4); + points[pts++] = QPointF(xs_6, y3); + points[pts++] = QPointF(xs_0, y2); + points[pts++] = QPointF(xs_6, y1); + p.drawConvexPolygon(points, pts); // Help says may be faster on some platforms (X11). + } + else + if(het == Part::LeftEventsHidden) + { + points[pts++] = QPointF(xs_0, y0); + points[pts++] = QPointF(xe_0, y0); + points[pts++] = QPointF(xe_0, y4); + points[pts++] = QPointF(xs_0, y4); + points[pts++] = QPointF(xs_6, y3); + points[pts++] = QPointF(xs_0, y2); + points[pts++] = QPointF(xs_6, y1); + p.drawConvexPolygon(points, pts); + } + else + if(het == Part::RightEventsHidden) + { + points[pts++] = QPointF(xs_0, y0); + points[pts++] = QPointF(xe_0, y0); + + points[pts++] = QPointF(xe_6, y1); + points[pts++] = QPointF(xe_0, y2); + points[pts++] = QPointF(xe_6, y3); + points[pts++] = QPointF(xe_0, y4); + points[pts++] = QPointF(xs_0, y4); + p.drawConvexPolygon(points, pts); + } + + // + // Draw remaining 'hidden events' decorations with 'jagged' edges... + // + + int part_r, part_g, part_b, brightness, color_brightness; + config.partColors[cidx].getRgb(&part_r, &part_g, &part_b); + brightness = part_r*29 + part_g*59 + part_b*12; + //if ((brightness < 12000 || part->selected()) && !part->mute() && !item->isMoving()) + // color_brightness=223; // too dark: use lighter color + //else + // color_brightness=32; // otherwise use dark color + if ((brightness >= 12000 && !part->selected())) + color_brightness=32; // too light: use dark color + else + color_brightness=223; // too dark: use lighter color + QColor c(color_brightness,color_brightness,color_brightness, config.globalAlphaBlend); + p.setBrush(QBrush(gGradientFromQColor(c, r.topLeft(), r.bottomLeft()))); + //p.setBrush(QBrush(c)); + if(het & Part::RightEventsHidden) + { + pts = 0; + points[pts++] = QPointF(xe_0, y0); + points[pts++] = QPointF(xe_0, y4); + points[pts++] = QPointF(xe_6, y3); + points[pts++] = QPointF(xe_0, y2); + points[pts++] = QPointF(xe_6, y1); + p.drawConvexPolygon(points, pts); + } + if(het & Part::LeftEventsHidden) + { + pts = 0; + points[pts++] = QPointF(xs_0, y0); + points[pts++] = QPointF(xs_6, y1); + points[pts++] = QPointF(xs_0, y2); + points[pts++] = QPointF(xs_6, y3); + points[pts++] = QPointF(xs_0, y4); + p.drawConvexPolygon(points, pts); + } + } + else + { + p.fillRect(r, brush); + } + MidiPart* mp = 0; WavePart* wp = 0; Track::TrackType type = part->track()->type(); @@ -1391,40 +1540,602 @@ void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& rect) drawMidiPart(p, rect, mp->events(), (MidiTrack*)part->track(), mp, r, mp->tick(), from, to); } + #if 0 + // + // Now draw the borders... + // Works great but requires clones be drawn with the highest priority on top of all other parts, in Canvas::draw. + // + + QPen pen(part->selected() ? config.partColors[i] : Qt::black, 2.0, clone ? Qt::DotLine : Qt::SolidLine); + pen.setCosmetic(true); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + p.drawRect(r); + + #else + // + // Now draw the borders, using custom segments... + // + + // FIXME NOTE: For 1-pixel wide lines, setting pen style to anything other than solid didn't work out well. + // Much too screwy - the single-width lines kept getting shifted one pixel over intermittently. + // I tried EVERYTHING to make sure x is proper but the painter keeps shifting them over. + // Meanwhile the fills are correct. Seems painter doesn't like line patterns, whether stock or custom. + // Therefore I was forced to manually draw the left and right segments. + // It works. Which seems to be more proof that painter is handling line patterns and pen widths badly... + // DO NOT ERASE COMMENTED CODE BELOW for now, in case it can be fixed. Tim. p4.0.29 + + p.setBrush(Qt::NoBrush); + + QColor pc((part->mute() || item->isMoving())? Qt::white : config.partColors[cidx]); + QPen penSelect1H(pc); + QPen penSelect2H(pc, 2.0); + QPen penSelect1V(pc); + QPen penSelect2V(pc, 2.0); + penSelect1H.setCosmetic(true); + penSelect2H.setCosmetic(true); + penSelect1V.setCosmetic(true); + penSelect2V.setCosmetic(true); + + pc = Qt::black; + QPen penNormal1H(pc); + QPen penNormal2H(pc, 2.0); + QPen penNormal1V(pc); + QPen penNormal2V(pc, 2.0); + penNormal1H.setCosmetic(true); + penNormal2H.setCosmetic(true); + penNormal1V.setCosmetic(true); + penNormal2V.setCosmetic(true); + + QVector customDashPattern; + if(clone) + { + customDashPattern << 4.0 << 8.0; + penSelect1H.setDashPattern(customDashPattern); + penNormal1H.setDashPattern(customDashPattern); + //penSelect1V.setDashPattern(customDashPattern); + //penNormal1V.setDashPattern(customDashPattern); + customDashPattern.clear(); + customDashPattern << 2.0 << 4.0; + penSelect2H.setDashPattern(customDashPattern); + penNormal2H.setDashPattern(customDashPattern); + //penSelect2V.setDashPattern(customDashPattern); + //penNormal2V.setDashPattern(customDashPattern); + } + + pc = Qt::white; + QPen penHidden1(pc); + QPen penHidden2(pc, 2.0); + penHidden2.setCosmetic(true); + //customDashPattern.clear(); + //customDashPattern << 2.0 << 10.0; + //penHidden1.setDashPattern(customDashPattern); + //customDashPattern.clear(); + //customDashPattern << 1.0 << 5.0; + //penHidden2.setDashPattern(customDashPattern); + + bool lbt = ((NPart*)item)->leftBorderTouches; + bool rbt = ((NPart*)item)->rightBorderTouches; + + QLineF l1( lbt?xs_1:xs_0, ys_0, rbt?xe_1:xe_0, ys_0); // Top + //QLineF l2(rbt?xe_1:xe_0, r.y() + (rbt?y_1:y_2) - 1, rbt?xe_1:xe_0, r.y() + r.height() - 1); // Right + QLineF l3( lbt?xs_1:xs_0, ye_0, rbt?xe_1:xe_0, ye_0); // Bottom + //QLineF l4(r.x(), r.y() + (lbt?y_1:y_2), r.x(), r.y() + r.height() - (lbt?y_1:y_2)); // Left + + if(het & Part::RightEventsHidden) + p.setPen(((NPart*)item)->rightBorderTouches ? penHidden1 : penHidden2); + else + { + if(((NPart*)item)->rightBorderTouches) + p.setPen(part->selected() ? penSelect1V : penNormal1V); + else + p.setPen(part->selected() ? penSelect2V : penNormal2V); + } + //p.drawLine(l2); // Right line + + double xx = rbt?xe_1:xe_0; + if(clone) + { + double yinc = 7.0 * y_1; + for(double yy = (rbt?ys_1:ys_2); yy < ye_2; yy += yinc) + { + double yi = (rbt?3.0:2.0) * y_1; + if(yy + yi > ye_2) + yi = ye_2 - yy; + p.drawLine(QPointF(xx, yy), QPointF(xx, yy + yi)); // Right dashed line + } + } + else + p.drawLine(QPointF(xx, rbt?ys_1:ys_2), QPointF(xx, rbt?ye_1:ye_2)); // Right line + + if(het & Part::LeftEventsHidden) + p.setPen(((NPart*)item)->leftBorderTouches ? penHidden1 : penHidden2); + else + { + if(((NPart*)item)->leftBorderTouches) + p.setPen(part->selected() ? penSelect1V : penNormal1V); + else + p.setPen(part->selected() ? penSelect2V : penNormal2V); + } + //p.drawLine(l4); // Left line + + xx = xs_0; + if(clone) + { + double yinc = 7.0 * y_1; + for(double yy = (lbt?ys_1:ys_2); yy < ye_2; yy += yinc) + { + double yi = (lbt?3.0:2.0) * y_1; + if(yy + yi > ye_2) + yi = ye_2 - yy; + p.drawLine(QPointF(xx, yy), QPointF(xx, yy + yi)); // Left dashed line + } + } + else + p.drawLine(QPointF(xx, lbt?ys_1:ys_2), QPointF(xx, lbt?ye_1:ye_2)); // Left line + + p.setPen(part->selected() ? penSelect2H : penNormal2H); + p.drawLine(l1); // Top line + p.drawLine(l3); // Bottom line + + #endif + + //p.restore(); + if (config.canvasShowPartType & 1) { // show names // draw name // FN: Set text color depending on part color (black / white) int part_r, part_g, part_b, brightness; - //config.partColors[i].getRgb(&part_r, &part_g, &part_b); // Since we'll draw the text on the bottom (to accommodate drum 'slivers'), // get the lowest colour in the gradient used to draw the part. QRect rr = map(r); rr.setX(rr.x() + 3); - gGradientFromQColor(config.partColors[i], rr.topLeft(), rr.bottomLeft()).stops().last().second.getRgb(&part_r, &part_g, &part_b); + gGradientFromQColor(config.partColors[cidx], rr.topLeft(), rr.bottomLeft()).stops().last().second.getRgb(&part_r, &part_g, &part_b); brightness = part_r*29 + part_g*59 + part_b*12; - //if (brightness < 12000 || part->selected()) - // p.setPen(Qt::white); /* too dark: use white for text color */ - //else - // p.setPen(Qt::black); /* otherwise use black */ - bool rev = brightness < 12000 || part->selected(); - //QRect rr = map(r); - //rr.setX(rr.x() + 3); + //bool rev = (brightness < 12000 || part->selected()) && !part->mute() && !item->isMoving(); + bool rev = brightness >= 12000 && !part->selected(); p.save(); p.setFont(config.fonts[1]); p.setWorldMatrixEnabled(false); if (rev) - p.setPen(Qt::black); - else p.setPen(Qt::white); + else + p.setPen(Qt::black); p.drawText(rr.translated(1, 1), Qt::AlignBottom|Qt::AlignLeft, part->name()); if (rev) - p.setPen(Qt::white); - else p.setPen(Qt::black); + else + p.setPen(Qt::white); p.drawText(rr, Qt::AlignBottom|Qt::AlignLeft, part->name()); p.restore(); } } +#endif + +void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& rect) + { + int from = rect.x(); + int to = from + rect.width(); + + //printf("from %d to %d\n", from,to); + Part* part = ((NPart*)item)->part(); + int pTick = part->tick(); + from -= pTick; + to -= pTick; + if(from < 0) + from = 0; + if((unsigned int)to > part->lenTick()) + to = part->lenTick(); + + // Item bounding box x is in tick coordinates, same as rectangle. + if(item->bbox().intersect(rect).isNull()) + { + //printf("PartCanvas::drawItem rectangle is null\n"); + return; + } + + QRect r = item->bbox(); + bool clone = part->events()->arefCount() > 1; + QBrush brush; + + //QRect rr = map(r); // Produced inconsistent positions. FIXME + //QRect rr = p.transform().mapRect(r); // Same. + QRect rr(QPoint(mapx(r.x()), mapy(r.y())), + QPoint(mapx(r.x() + r.width()) - 1, mapy(r.y() + r.height() - 1))); // Test OK so far. + + //printf("PartCanvas::drawItem called map rx:%d rw:%d rrx:%d rrw:%d\n", r.x(), r.width(), rr.x(), rr.width()); + //printf("PartCanvas::drawItem called map rx:%d rw:%d\n", r.x(), r.width()); + + p.save(); + p.setWorldMatrixEnabled(false); + + // NOTE: Optimization: For each item, hasHiddenEvents() is called once in Canvas::draw(), and we use cachedHasHiddenEvents(). + // Not used for now. + //int het = part->cachedHasHiddenEvents(); + int het = part->hasHiddenEvents(); + + int xs_0 = rr.x(); + int xe_0 = xs_0 + rr.width(); + int xs_1 = xs_0 + 1; + if(xs_1 > xe_0) + xs_1 = xe_0; + int xs_2 = xs_0 + 2; + if(xs_2 > xe_0) + xs_2 = xe_0; + int xs_6 = xs_0 + 6; + if(xs_6 > xe_0) + xs_6 = xe_0; + + int xe_1 = xe_0 - 1; + if(xe_1 < xs_0) + xe_1 = xs_0; + int xe_2 = xe_0 - 2; + if(xe_2 < xs_0) + xe_2 = xs_0; + int xe_6 = xe_0 - 6; + if(xe_6 < xs_0) + xe_6 = xs_0; + + int ys_0 = rr.y(); + int ye_0 = ys_0 + rr.height(); + int ys_1 = ys_0 + 1; + if(ys_1 > ye_0) + ys_1 = ye_0; + int ys_2 = ys_0 + 2; + if(ys_2 > ye_0) + ys_2 = ye_0; + + int ye_1 = ye_0 - 1; + if(ye_1 < ys_0) + ye_1 = ys_0; + int ye_2 = ye_0 - 2; + if(ye_2 < ys_0) + ye_2 = ys_0; + + int cidx = part->colorIndex(); + if (item->isMoving()) + { + QColor c(Qt::gray); + c.setAlpha(config.globalAlphaBlend); + QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); + gradient.setColorAt(0, c); + gradient.setColorAt(1, c.darker()); + brush = QBrush(gradient); + } + else + if (part->selected()) + { + QColor c(Qt::black); + c.setAlpha(config.globalAlphaBlend); + QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); + // Use a colour only about 20% lighter than black, rather than the 50% we use in gGradientFromQColor + // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. + //c.setRgba(64, 64, 64, c.alpha()); + gradient.setColorAt(0, QColor(51, 51, 51, config.globalAlphaBlend)); + gradient.setColorAt(1, c); + brush = QBrush(gradient); + } + else + if (part->mute()) + { + QColor c(Qt::white); + c.setAlpha(config.globalAlphaBlend); + QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); + gradient.setColorAt(0, c); + gradient.setColorAt(1, c.darker()); + brush = QBrush(gradient); + } + else + { + QColor c(config.partColors[cidx]); + c.setAlpha(config.globalAlphaBlend); + brush = QBrush(gGradientFromQColor(c, rr.topLeft(), rr.bottomLeft())); + } + + int h = rr.height(); + double s = double(h) / 4.0; + int y0 = rr.y(); + int y1 = y0 + lrint(s); + int y2 = y0 + lrint(s * 2.0); + int y3 = y0 + lrint(s * 3.0); + int y4 = y0 + h; + + QPoint points[16]; + int pts; + + // + // Fill the part rectangles, accounting for hidden events by using 'jagged' edges... + // + + p.setBrush(brush); + p.setPen(Qt::NoPen); + if(het) + { + pts = 0; + if(het == (Part::LeftEventsHidden | Part::RightEventsHidden)) + { + points[pts++] = QPoint(xs_0, y0); + points[pts++] = QPoint(xe_0, y0); + points[pts++] = QPoint(xe_6, y1); + points[pts++] = QPoint(xe_0, y2); + points[pts++] = QPoint(xe_6, y3); + points[pts++] = QPoint(xe_0, y4); + points[pts++] = QPoint(xs_0, y4); + points[pts++] = QPoint(xs_6, y3); + points[pts++] = QPoint(xs_0, y2); + points[pts++] = QPoint(xs_6, y1); + p.drawConvexPolygon(points, pts); // Help says may be faster on some platforms (X11). + } + else + if(het == Part::LeftEventsHidden) + { + points[pts++] = QPoint(xs_0, y0); + points[pts++] = QPoint(xe_0, y0); + points[pts++] = QPoint(xe_0, y4); + points[pts++] = QPoint(xs_0, y4); + points[pts++] = QPoint(xs_6, y3); + points[pts++] = QPoint(xs_0, y2); + points[pts++] = QPoint(xs_6, y1); + p.drawConvexPolygon(points, pts); + } + else + if(het == Part::RightEventsHidden) + { + points[pts++] = QPoint(xs_0, y0); + points[pts++] = QPoint(xe_0, y0); + + points[pts++] = QPoint(xe_6, y1); + points[pts++] = QPoint(xe_0, y2); + points[pts++] = QPoint(xe_6, y3); + points[pts++] = QPoint(xe_0, y4); + points[pts++] = QPoint(xs_0, y4); + p.drawConvexPolygon(points, pts); + } + + // + // Draw remaining 'hidden events' decorations with 'jagged' edges... + // + + int part_r, part_g, part_b, brightness, color_brightness; + config.partColors[cidx].getRgb(&part_r, &part_g, &part_b); + brightness = part_r*29 + part_g*59 + part_b*12; + //if ((brightness < 12000 || part->selected()) && !part->mute() && !item->isMoving()) + // color_brightness=223; // too dark: use lighter color + //else + // color_brightness=32; // otherwise use dark color + if ((brightness >= 12000 && !part->selected())) + color_brightness=32; // too light: use dark color + else + color_brightness=223; // too dark: use lighter color + QColor c(color_brightness,color_brightness,color_brightness, config.globalAlphaBlend); + p.setBrush(QBrush(gGradientFromQColor(c, rr.topLeft(), rr.bottomLeft()))); + //p.setBrush(QBrush(c)); + if(het & Part::RightEventsHidden) + { + pts = 0; + points[pts++] = QPoint(xe_0, y0); + points[pts++] = QPoint(xe_0, y4); + points[pts++] = QPoint(xe_6, y3); + points[pts++] = QPoint(xe_0, y2); + points[pts++] = QPoint(xe_6, y1); + p.drawConvexPolygon(points, pts); + } + if(het & Part::LeftEventsHidden) + { + pts = 0; + points[pts++] = QPoint(xs_0, y0); + points[pts++] = QPoint(xs_6, y1); + points[pts++] = QPoint(xs_0, y2); + points[pts++] = QPoint(xs_6, y3); + points[pts++] = QPoint(xs_0, y4); + p.drawConvexPolygon(points, pts); + } + } + else + { + p.fillRect(rr, brush); + } + + // Draw a pattern brush on muted parts... + if(part->mute()) + { + p.setPen(Qt::NoPen); + //brush.setStyle(Qt::DiagCrossPattern); + brush.setStyle(Qt::Dense7Pattern); + + p.fillRect(rr, brush); // FIXME: Some shifting going on + //p.fillRect(QRect(rr.x(), rr.y(), rr.width() + 1, rr.height() + 1), brush); // Same here + } + + p.setWorldMatrixEnabled(true); + + MidiPart* mp = 0; + WavePart* wp = 0; + Track::TrackType type = part->track()->type(); + if (type == Track::WAVE) { + wp =(WavePart*)part; + } + else { + mp = (MidiPart*)part; + } + + if (wp) + drawWavePart(p, rect, wp, r); + else if (mp) + { + drawMidiPart(p, rect, mp->events(), (MidiTrack*)part->track(), mp, r, mp->tick(), from, to); + } + + p.setWorldMatrixEnabled(false); + + #if 0 + // + // Now draw the borders... + // Works great but requires clones be drawn with the highest priority on top of all other parts, in Canvas::draw. + // + + QPen pen(part->selected() ? config.partColors[i] : Qt::black, 2.0, clone ? Qt::DotLine : Qt::SolidLine); + pen.setCosmetic(true); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + p.drawRect(r); + + #else + // + // Now draw the borders, using custom segments... + // + + // FIXME NOTE: For 1-pixel wide lines, setting pen style to anything other than solid didn't work out well. + // Much too screwy - the single-width lines kept getting shifted one pixel over intermittently. + // I tried EVERYTHING to make sure x is proper but the painter keeps shifting them over. + // Meanwhile the fills are correct. Seems painter doesn't like line patterns, whether stock or custom. + // Therefore I was forced to manually draw the left and right segments. + // It works. Which seems to be more proof that painter is handling line patterns and pen widths badly... + // DO NOT ERASE COMMENTED CODE BELOW for now, in case it can be fixed. Tim. p4.0.29 + + p.setBrush(Qt::NoBrush); + + QColor pc((part->mute() || item->isMoving())? Qt::white : config.partColors[cidx]); + QPen penSelect1H(pc); + QPen penSelect2H(pc, 2.0); + QPen penSelect1V(pc); + QPen penSelect2V(pc, 2.0); + penSelect1H.setCosmetic(true); + penSelect2H.setCosmetic(true); + penSelect1V.setCosmetic(true); + penSelect2V.setCosmetic(true); + + pc = Qt::black; + QPen penNormal1H(pc); + QPen penNormal2H(pc, 2.0); + QPen penNormal1V(pc); + QPen penNormal2V(pc, 2.0); + penNormal1H.setCosmetic(true); + penNormal2H.setCosmetic(true); + penNormal1V.setCosmetic(true); + penNormal2V.setCosmetic(true); + + QVector customDashPattern; + if(clone) + { + customDashPattern << 4.0 << 8.0; + penSelect1H.setDashPattern(customDashPattern); + penNormal1H.setDashPattern(customDashPattern); + //penSelect1V.setDashPattern(customDashPattern); + //penNormal1V.setDashPattern(customDashPattern); + customDashPattern.clear(); + customDashPattern << 2.0 << 4.0; + penSelect2H.setDashPattern(customDashPattern); + penNormal2H.setDashPattern(customDashPattern); + //penSelect2V.setDashPattern(customDashPattern); + //penNormal2V.setDashPattern(customDashPattern); + } + + pc = Qt::white; + QPen penHidden1(pc); + QPen penHidden2(pc, 2.0); + penHidden2.setCosmetic(true); + //customDashPattern.clear(); + //customDashPattern << 2.0 << 10.0; + //penHidden1.setDashPattern(customDashPattern); + //customDashPattern.clear(); + //customDashPattern << 1.0 << 5.0; + //penHidden2.setDashPattern(customDashPattern); + + bool lbt = ((NPart*)item)->leftBorderTouches; + bool rbt = ((NPart*)item)->rightBorderTouches; + + QLine l1( lbt?xs_1:xs_0, ys_0, rbt?xe_1:xe_0, ys_0); // Top + //QLine l2(rbt?xe_1:xe_0, r.y() + (rbt?y_1:y_2) - 1, rbt?xe_1:xe_0, r.y() + r.height() - 1); // Right + QLine l3( lbt?xs_1:xs_0, ye_0, rbt?xe_1:xe_0, ye_0); // Bottom + //QLine l4(r.x(), r.y() + (lbt?y_1:y_2), r.x(), r.y() + r.height() - (lbt?y_1:y_2)); // Left + + if(het & Part::RightEventsHidden) + p.setPen(((NPart*)item)->rightBorderTouches ? penHidden1 : penHidden2); + else + { + if(((NPart*)item)->rightBorderTouches) + p.setPen(part->selected() ? penSelect1V : penNormal1V); + else + p.setPen(part->selected() ? penSelect2V : penNormal2V); + } + //p.drawLine(l2); // Right line + + int xx = rbt?xe_1:xe_0; + if(clone) + { + int yinc = 7; + for(int yy = (rbt?ys_1:ys_2); yy < ye_2; yy += yinc) + { + int yi = rbt?3:2; + if(yy + yi > ye_2) + yi = ye_2 - yy; + p.drawLine(QPoint(xx, yy), QPoint(xx, yy + yi)); // Right dashed line + } + } + else + p.drawLine(QPoint(xx, rbt?ys_1:ys_2), QPoint(xx, rbt?ye_1:ye_2)); // Right line + + if(het & Part::LeftEventsHidden) + p.setPen(((NPart*)item)->leftBorderTouches ? penHidden1 : penHidden2); + else + { + if(((NPart*)item)->leftBorderTouches) + p.setPen(part->selected() ? penSelect1V : penNormal1V); + else + p.setPen(part->selected() ? penSelect2V : penNormal2V); + } + //p.drawLine(l4); // Left line + + xx = xs_0; + if(clone) + { + int yinc = 7; + for(int yy = (lbt?ys_1:ys_2); yy < ye_2; yy += yinc) + { + int yi = lbt?3:2; + if(yy + yi > ye_2) + yi = ye_2 - yy; + p.drawLine(QPoint(xx, yy), QPoint(xx, yy + yi)); // Left dashed line + } + } + else + p.drawLine(QPoint(xx, lbt?ys_1:ys_2), QPoint(xx, lbt?ye_1:ye_2)); // Left line + + p.setPen(part->selected() ? penSelect2H : penNormal2H); + p.drawLine(l1); // Top line + p.drawLine(l3); // Bottom line + + #endif + + if (config.canvasShowPartType & 1) { // show names + // draw name + // FN: Set text color depending on part color (black / white) + int part_r, part_g, part_b, brightness; + // Since we'll draw the text on the bottom (to accommodate drum 'slivers'), + // get the lowest colour in the gradient used to draw the part. + //QRect rr = map(r); + QRect tr = rr; + tr.setX(tr.x() + 3); + gGradientFromQColor(config.partColors[cidx], tr.topLeft(), tr.bottomLeft()).stops().last().second.getRgb(&part_r, &part_g, &part_b); + brightness = part_r*29 + part_g*59 + part_b*12; + //bool rev = (brightness < 12000 || part->selected()) && !part->mute() && !item->isMoving(); + bool rev = brightness >= 12000 && !part->selected(); + p.setFont(config.fonts[1]); + if (rev) + p.setPen(Qt::white); + else + p.setPen(Qt::black); + p.drawText(tr.translated(1, 1), Qt::AlignBottom|Qt::AlignLeft, part->name()); + if (rev) + p.setPen(Qt::black); + else + p.setPen(Qt::white); + p.drawText(tr, Qt::AlignBottom|Qt::AlignLeft, part->name()); + } + p.restore(); + p.setWorldMatrixEnabled(true); + } //--------------------------------------------------------- // drawMoving @@ -1434,19 +2145,11 @@ void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& rect) void PartCanvas::drawMoving(QPainter& p, const CItem* item, const QRect&) { p.setPen( Qt::black); - - //p.setBrush( Qt::NoBrush); - //QColor c(Qt::gray); Part* part = ((NPart*)item)->part(); - QColor c(config.partColors[part->colorIndex()]); - - ///c.setAlpha(config.globalAlphaBlend); + QColor c(part->mute() ? Qt::white : config.partColors[part->colorIndex()]); + //c.setAlpha(config.globalAlphaBlend); c.setAlpha(128); // Fix this regardless of global setting. Should be OK. - p.setBrush(c); - - // NOTE: For one-pixel border use second line. For two-pixel border use first. - //p.drawRect(item->mp().x(), item->mp().y()+1, item->width(), item->height()); p.drawRect(item->mp().x(), item->mp().y(), item->width(), item->height()); } @@ -1466,10 +2169,14 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi int part_r, part_g, part_b, brightness; config.partColors[pt->colorIndex()].getRgb(&part_r, &part_g, &part_b); brightness = part_r*29 + part_g*59 + part_b*12; - if (brightness < 12000 || pt->selected()) - color_brightness=192; // too dark: use lighter color + //if ((brightness < 12000 || pt->selected()) && !pt->mute()) + // color_brightness=192; // too dark: use lighter color + //else + // color_brightness=64; // otherwise use dark color + if (brightness >= 12000 && !pt->selected()) + color_brightness=64; // too bright: use dark color else - color_brightness=64; // otherwise use dark color + color_brightness=192; // too dark: use lighter color } else color_brightness=80; @@ -1654,9 +2361,11 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, EventList* events, Midi if (te < (from + pTick)) continue; - if (te > (to + pTick)) - te = to + pTick; - + //if (te > (to + pTick)) + // te = to + pTick; + if (te >= (to + pTick)) + te = lrint(rmapxDev_f(rmapx_f(to + pTick) - 1.0)); + EventType type = i->second.type(); if (type == Note) { int pitch = i->second.pitch(); diff --git a/muse2/muse/arranger/pcanvas.h b/muse2/muse/arranger/pcanvas.h index ccfb1fcc..5f0aabae 100644 --- a/muse2/muse/arranger/pcanvas.h +++ b/muse2/muse/arranger/pcanvas.h @@ -3,11 +3,14 @@ // Linux Music Editor // $Id: pcanvas.h,v 1.11.2.4 2009/05/24 21:43:44 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #ifndef __PCANVAS_H__ #define __PCANVAS_H__ +#include + #include "song.h" #include "canvas.h" #include "trackautomationview.h" @@ -33,6 +36,9 @@ class NPart : public CItem { const QString name() const { return part()->name(); } void setName(const QString& s) { part()->setName(s); } Track* track() const { return part()->track(); } + + bool leftBorderTouches; // Whether the borders touch other part borders. + bool rightBorderTouches; }; enum ControllerVals { doNothing, movingController, addNewController }; diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp index 4857ede5..529e8a60 100644 --- a/muse2/muse/driver/jack.cpp +++ b/muse2/muse/driver/jack.cpp @@ -977,7 +977,8 @@ void JackAudioDevice::graphChanged() // p3.3.37 //delete ports; - free(ports); + //free(ports); + jack_free(ports); // p4.0.29 ports = NULL; } @@ -1063,7 +1064,8 @@ void JackAudioDevice::graphChanged() // p3.3.37 //delete ports; - free(ports); + //free(ports); + jack_free(ports); // p4.0.29 ports = NULL; } @@ -1182,7 +1184,8 @@ void JackAudioDevice::graphChanged() // p3.3.55 // Done with ports. Free them. - free(ports); + //free(ports); + jack_free(ports); // p4.0.29 } } } @@ -1279,7 +1282,8 @@ void JackAudioDevice::graphChanged() } // p3.3.55 // Done with ports. Free them. - free(ports); + //free(ports); + jack_free(ports); // p4.0.29 } } } @@ -1662,7 +1666,8 @@ std::list JackAudioDevice::outputPorts(bool midi, int aliases) // p3.3.37 if(ports) - free(ports); + //free(ports); + jack_free(ports); // p4.0.29 return clientList; } @@ -1734,7 +1739,8 @@ std::list JackAudioDevice::inputPorts(bool midi, int aliases) // p3.3.37 if(ports) - free(ports); + //free(ports); + jack_free(ports); // p4.0.29 return clientList; } diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index 66257401..2568899a 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -307,7 +307,7 @@ bool modify_notelen(const set& parts, int range, int rate, int offset) if (len <= 0) len = 1; - if ((event.tick()+len > part->lenTick()) && (!part->hasHiddenNotes())) + if ((event.tick()+len > part->lenTick()) && (!part->hasHiddenEvents())) partlen[part]=event.tick()+len; // schedule auto-expanding if (event.lenTick() != len) @@ -510,7 +510,7 @@ bool move_notes(const set& parts, int range, signed int ticks) if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end: { - if (part->hasHiddenNotes()) // auto-expanding is forbidden, clip + if (part->hasHiddenEvents()) // auto-expanding is forbidden, clip { if (part->lenTick() > newEvent.tick()) newEvent.setLenTick(part->lenTick() - newEvent.tick()); @@ -764,7 +764,7 @@ void paste_at(Part* dest_part, const QString& pt, int pos) if (e.endTick() > dest_part->lenTick()) // event exceeds part? { - if (dest_part->hasHiddenNotes()) // auto-expanding is forbidden? + if (dest_part->hasHiddenEvents()) // auto-expanding is forbidden? { if (e.tick() < dest_part->lenTick()) e.setLenTick(dest_part->lenTick() - e.tick()); // clip diff --git a/muse2/muse/midiedit/dcanvas.cpp b/muse2/muse/midiedit/dcanvas.cpp index d0a5ee31..578e2616 100644 --- a/muse2/muse/midiedit/dcanvas.cpp +++ b/muse2/muse/midiedit/dcanvas.cpp @@ -160,7 +160,7 @@ Undo DrumCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtyp Part* opart = ip2c->first; int diff = ip2c->second.xdiff; - if (opart->hasHiddenNotes()) + if (opart->hasHiddenEvents()) { forbidden=true; break; @@ -352,7 +352,7 @@ void DrumCanvas::newItem(CItem* item, bool noSnap, bool replace) Undo operations; int diff = event.endTick()-part->lenTick(); - if (! ((diff > 0) && part->hasHiddenNotes()) ) //operation is allowed + if (! ((diff > 0) && part->hasHiddenEvents()) ) //operation is allowed { operations.push_back(UndoOp(UndoOp::AddEvent,event, part, false, false)); diff --git a/muse2/muse/midiedit/prcanvas.cpp b/muse2/muse/midiedit/prcanvas.cpp index 5fb59099..eba1a13c 100644 --- a/muse2/muse/midiedit/prcanvas.cpp +++ b/muse2/muse/midiedit/prcanvas.cpp @@ -316,7 +316,7 @@ Undo PianoCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dty Part* opart = ip2c->first; int diff = ip2c->second.xdiff; - if (opart->hasHiddenNotes()) + if (opart->hasHiddenEvents()) { forbidden=true; break; @@ -470,7 +470,7 @@ void PianoCanvas::newItem(CItem* item, bool noSnap) Undo operations; int diff = event.endTick()-part->lenTick(); - if (! ((diff > 0) && part->hasHiddenNotes()) ) //operation is allowed + if (! ((diff > 0) && part->hasHiddenEvents()) ) //operation is allowed { operations.push_back(UndoOp(UndoOp::AddEvent,event, part, false, false)); @@ -515,7 +515,7 @@ void PianoCanvas::resizeItem(CItem* item, bool noSnap, bool) // experime Undo operations; int diff = event.tick()+len-part->lenTick(); - if (! ((diff > 0) && part->hasHiddenNotes()) ) //operation is allowed + if (! ((diff > 0) && part->hasHiddenEvents()) ) //operation is allowed { newEvent.setLenTick(len); operations.push_back(UndoOp(UndoOp::ModifyEvent,newEvent, event, nevent->part(), false, false)); diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index 785ac7a5..b6b2c6d9 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -3931,7 +3931,7 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) unsigned newpartlen=dragged_event_part->lenTick(); if (tmp.endTick() > dragged_event_part->lenTick()) { - if (dragged_event_part->hasHiddenNotes()) // do not allow autoexpand + if (dragged_event_part->hasHiddenEvents()) // do not allow autoexpand { tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl; @@ -4481,7 +4481,7 @@ void staff_t::update_part_indices() * o add "move other notes" or "overwrite notes" or "mix with notes" to paste * * IMPORTANT TODO - * o draw the edge of parts hiding notes "jagged" (hasHiddenNotes() is interesting for this) + * o draw the edge of parts hiding notes "jagged" (hasHiddenEvents() is interesting for this) - Done. Tim. * o shrink a part from its beginning as well! watch out for clones! * o insert empty measure should also work inside parts, that is, * move notes _within_ parts diff --git a/muse2/muse/part.cpp b/muse2/muse/part.cpp index 67cf441e..3e371bf4 100644 --- a/muse2/muse/part.cpp +++ b/muse2/muse/part.cpp @@ -4,6 +4,7 @@ // $Id: part.cpp,v 1.12.2.17 2009/06/25 05:13:02 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #include @@ -655,6 +656,7 @@ Part* PartList::find(int idx) Part::Part(Track* t) { + _hiddenEvents = NoEventsHidden; _prevClone = this; _nextClone = this; setSn(newSn()); @@ -673,6 +675,7 @@ Part::Part(Track* t) Part::Part(Track* t, EventList* ev) { + _hiddenEvents = NoEventsHidden; _prevClone = this; _nextClone = this; setSn(newSn()); @@ -1169,15 +1172,38 @@ WavePart* WavePart::clone() const return new WavePart(*this); } +/* +bool Part::hasHiddenNotes() +{ + unsigned lastNote=0; + for (iEvent ev=events()->begin(); ev!=events()->end(); ev++) + if (ev->second.endTick() > lastNote) + lastNote=ev->second.endTick(); + + return lastNote > lenTick(); +} +*/ -bool Part::hasHiddenNotes() +//--------------------------------------------------------- +// hasHiddenEvents +// Returns combination of HiddenEventsType enum. +//--------------------------------------------------------- + +int Part::hasHiddenEvents() { - unsigned lastNote=0; + unsigned len = lenTick(); - for (iEvent ev=events()->begin(); ev!=events()->end(); ev++) - if (ev->second.endTick() > lastNote) - lastNote=ev->second.endTick(); - - return lastNote > lenTick(); + // TODO: For now, we don't support events before the left border, only events past the right border. + for(iEvent ev=events()->begin(); ev!=events()->end(); ev++) + { + if(ev->second.endTick() > len) + { + _hiddenEvents = RightEventsHidden; // Cache the result for later. + return _hiddenEvents; + } + } + _hiddenEvents = NoEventsHidden; // Cache the result for later. + return _hiddenEvents; } + diff --git a/muse2/muse/part.h b/muse2/muse/part.h index ff7091a6..67a67dbb 100644 --- a/muse2/muse/part.h +++ b/muse2/muse/part.h @@ -4,6 +4,7 @@ // $Id: part.h,v 1.5.2.4 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #ifndef __PART_H__ @@ -40,6 +41,10 @@ typedef CloneList::iterator iClone; //--------------------------------------------------------- class Part : public PosLen { + public: + enum HiddenEventsType { NoEventsHidden = 0, LeftEventsHidden, RightEventsHidden }; + + private: static int snGen; int _sn; @@ -48,6 +53,8 @@ class Part : public PosLen { bool _mute; int _colorIndex; + int _hiddenEvents; // Combination of HiddenEventsType. + protected: Track* _track; EventList* _events; @@ -82,7 +89,11 @@ class Part : public PosLen { void setPrevClone(Part* p) { _prevClone = p; } void setNextClone(Part* p) { _nextClone = p; } - bool hasHiddenNotes(); + // Returns combination of HiddenEventsType enum. + int hasHiddenEvents(); + // If repeated calls to hasHiddenEvents() are desired, then to avoid re-iteration of the event list, + // call this after hasHiddenEvents(). + int cachedHasHiddenEvents() const { return _hiddenEvents; } iEvent addEvent(Event& p); diff --git a/muse2/muse/steprec.cpp b/muse2/muse/steprec.cpp index c1fc23b1..45af3930 100644 --- a/muse2/muse/steprec.cpp +++ b/muse2/muse/steprec.cpp @@ -77,7 +77,7 @@ void StepRec::record(Part* part, int pitch, int len, int step, int velo, bool ct { // if we already entered the note, delete it // if we would find a note after part->lenTick(), the above "if" - // avoids this. this has to be avoided because then part->hasHiddenNotes() is true + // avoids this. this has to be avoided because then part->hasHiddenEvents() is true // which results in forbidding any action beyond its end EventRange range = events->equal_range(tick - part->tick()); for (iEvent i = range.first; i != range.second; ++i) @@ -173,7 +173,7 @@ void StepRec::record(Part* part, int pitch, int len, int step, int velo, bool ct } steprec_record_foot: - if (!((lasttick > part->lenTick()) && part->hasHiddenNotes())) // allowed? + if (!((lasttick > part->lenTick()) && part->hasHiddenEvents())) // allowed? { if (lasttick > part->lenTick()) // we have to expand the part? schedule_resize_all_same_len_clone_parts(part, lasttick, operations); diff --git a/muse2/muse/widgets/canvas.cpp b/muse2/muse/widgets/canvas.cpp index a74e8a8f..c2c329a9 100644 --- a/muse2/muse/widgets/canvas.cpp +++ b/muse2/muse/widgets/canvas.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: canvas.cpp,v 1.10.2.17 2009/05/03 04:14:01 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #include @@ -18,6 +19,8 @@ #include #include +#include + #include "song.h" #include "event.h" #include "citem.h" @@ -148,6 +151,11 @@ void Canvas::draw(QPainter& p, const QRect& rect) int h = rect.height(); int x2 = x + w; + std::vector list1; + std::vector list2; + //std::vector list3; + std::vector list4; + if (virt()) { drawCanvas(p, rect); @@ -157,22 +165,24 @@ void Canvas::draw(QPainter& p, const QRect& rect) iCItem to(items.lower_bound(x2)); + /* // Draw items from other parts behind all others. // Only for items with events (not arranger parts). for(iCItem i = items.begin(); i != to; ++i) { CItem* ci = i->second; + // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents(). + ci->part()->hasHiddenEvents(); if(!ci->event().empty() && ci->part() != curPart) - { drawItem(p, ci, rect); - } } - + + // Draw unselected parts behind selected. for (iCItem i = items.begin(); i != to; ++i) { CItem* ci = i->second; - // Draw unselected parts behind selected. - if(!ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + if((!ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + && !(ci->event().empty() && (ci->part()->events()->arefCount() > 1 || ci->part()->cachedHasHiddenEvents()))) // p4.0.29 { drawItem(p, ci, rect); } @@ -181,12 +191,57 @@ void Canvas::draw(QPainter& p, const QRect& rect) // Draw selected parts in front of unselected. for (iCItem i = items.begin(); i != to; ++i) { - CItem* ci = i->second; - if(ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) - { - drawItem(p, ci, rect); - } + CItem* ci = i->second; + if(ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + //if((ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + // || (ci->event().empty() && (ci->part()->events()->arefCount() > 1 || ci->part()->cachedHasHiddenEvents()))) + { + drawItem(p, ci, rect); + } + } + */ + + // p4.0.29 + for(iCItem i = items.begin(); i != to; ++i) + { + CItem* ci = i->second; + // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents(). + // Not required for now. + //ci->part()->hasHiddenEvents(); + + // Draw items from other parts behind all others. + // Only for items with events (not arranger parts). + if(!ci->event().empty() && ci->part() != curPart) + list1.push_back(ci); + else if(!ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + { + // Draw selected parts in front of all others. + if(ci->isSelected()) + list4.push_back(ci); + // Draw clone parts, and parts with hidden events, in front of others all except selected. + //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1 || ci->part()->cachedHasHiddenEvents())) + // Draw clone parts in front of others all except selected. + //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1)) + // list3.push_back(ci); + else + // Draw unselected parts. + list2.push_back(ci); + } } + int i; + int sz = list1.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list1[i], rect); + sz = list2.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list2[i], rect); + //sz = list3.size(); + //for(i = 0; i != sz; ++i) + // drawItem(p, list3[i], rect); + sz = list4.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list4[i], rect); + to = moving.lower_bound(x2); for (iCItem i = moving.begin(); i != to; ++i) { @@ -237,21 +292,22 @@ void Canvas::draw(QPainter& p, const QRect& rect) //--------------------------------------------------- // draw Canvas Items //--------------------------------------------------- - + + /* // Draw items from other parts behind all others. // Only for items with events (not arranger parts). for(iCItem i = items.begin(); i != items.end(); ++i) { CItem* ci = i->second; + // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents(). + ci->part()->hasHiddenEvents(); if(!ci->event().empty() && ci->part() != curPart) - { drawItem(p, ci, rect); - } - } - + } + + // Draw unselected parts behind selected. for (iCItem i = items.begin(); i != items.end(); ++i) { CItem* ci = i->second; - // Draw unselected parts behind selected. if(!ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) { drawItem(p, ci, rect); @@ -262,10 +318,55 @@ void Canvas::draw(QPainter& p, const QRect& rect) for (iCItem i = items.begin(); i != items.end(); ++i) { CItem* ci = i->second; if(ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) - { - drawItem(p, ci, rect); - } - } + //if((ci->isSelected() && !ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + // || (ci->event().empty() && (ci->part()->events()->arefCount() > 1 || ci->part()->cachedHasHiddenEvents()))) + { + drawItem(p, ci, rect); + } + } + */ + + // p4.0.29 + for(iCItem i = items.begin(); i != items.end(); ++i) + { + CItem* ci = i->second; + // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents(). + // Not required for now. + //ci->part()->hasHiddenEvents(); + + // Draw items from other parts behind all others. + // Only for items with events (not arranger parts). + if(!ci->event().empty() && ci->part() != curPart) + list1.push_back(ci); + else if(!ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + { + // Draw selected parts in front of all others. + if(ci->isSelected()) + list4.push_back(ci); + // Draw clone parts, and parts with hidden events, in front of others all except selected. + //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1 || ci->part()->cachedHasHiddenEvents())) + // Draw clone parts in front of others all except selected. + //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1)) + // list3.push_back(ci); + else + // Draw unselected parts. + list2.push_back(ci); + } + } + int i; + int sz = list1.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list1[i], rect); + sz = list2.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list2[i], rect); + //sz = list3.size(); + //for(i = 0; i != sz; ++i) + // drawItem(p, list3[i], rect); + sz = list4.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list4[i], rect); + for (iCItem i = moving.begin(); i != moving.end(); ++i) { drawItem(p, i->second, rect); @@ -273,7 +374,7 @@ void Canvas::draw(QPainter& p, const QRect& rect) drawTopItem(p, QRect(x,y,w,h)); p.save(); setPainter(p); - } + } //--------------------------------------------------- // draw marker diff --git a/muse2/muse/widgets/canvas.h b/muse2/muse/widgets/canvas.h index dbe13fcb..99bbabee 100644 --- a/muse2/muse/widgets/canvas.h +++ b/muse2/muse/widgets/canvas.h @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: canvas.h,v 1.3.2.8 2009/02/02 21:38:01 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #ifndef __CANVAS_H__ diff --git a/muse2/muse/widgets/view.cpp b/muse2/muse/widgets/view.cpp index fb480527..49f559c6 100644 --- a/muse2/muse/widgets/view.cpp +++ b/muse2/muse/widgets/view.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: view.cpp,v 1.3.2.2 2009/04/06 01:24:55 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #include "view.h" @@ -16,6 +17,8 @@ #include #include +#include "math.h" + // Don't use this, it was just for debugging. // It's much slower than muse-1 no matter how hard I tried. // The left/right pixmap shifters in seXPos setYPos @@ -545,9 +548,26 @@ void View::setPainter(QPainter& p) // map //--------------------------------------------------------- +QRect View::mapDev(const QRect& r) const + { + return QRect(mapxDev(r.x()), mapyDev(r.y()), + rmapxDev(r.width()), rmapyDev(r.height())); + } + +QPoint View::mapDev(const QPoint& r) const + { + return QPoint(mapxDev(r.x()), mapyDev(r.y())); + } + +#if 0 + // + // Calculations using integer rounding methods... + // + QRect View::map(const QRect& r) const { int x, y, w, h; + //printf("View::map xmag:%d xpos:%d xorg:%d\n", xmag, xpos, xorg); if (xmag < 0) { x = r.x()/(-xmag) - (xpos + rmapx(xorg)); // round down w = (r.width()-xmag-1) / (-xmag); // round up @@ -585,17 +605,6 @@ QPoint View::map(const QPoint& p) const return QPoint(x, y); } -QRect View::mapDev(const QRect& r) const - { - return QRect(mapxDev(r.x()), mapyDev(r.y()), - rmapxDev(r.width()), rmapyDev(r.height())); - } - -QPoint View::mapDev(const QPoint& r) const - { - return QPoint(mapxDev(r.x()), mapyDev(r.y())); - } - int View::mapx(int x) const { if (xmag < 0) { @@ -664,6 +673,153 @@ int View::rmapyDev(int y) const return (y + ymag/2) / ymag; } + +#else + // + // Calculations using more accurate methods... p4.0.29 Tim. + // + +QRect View::map(const QRect& r) const + { + int x, y, w, h; + //printf("View::map xmag:%d xpos:%d xorg:%d\n", xmag, xpos, xorg); + if (xmag < 0) { + x = lrint(double(r.x())/double(-xmag) - rmapx_f(xorg)) - xpos; + w = lrint(double(r.width()) / double(-xmag)); + } + else { + x = r.x()*xmag - xpos - lrint(rmapx_f(xorg)); + w = r.width() * xmag; + } + if (ymag < 0) { + y = lrint(double(r.y())/double(-ymag) - rmapy_f(yorg)) - ypos; + h = lrint(double(r.height()) / double(-ymag)); + } + else { + y = r.y()*ymag - ypos - lrint(rmapy_f(yorg)); + h = r.height() * ymag; + } + return QRect(x, y, w, h); + } + +QPoint View::map(const QPoint& p) const + { + int x, y; + if (xmag < 0) { + x = lrint(double(p.x())/double(-xmag) - rmapx_f(xorg)) - xpos; + } + else { + x = p.x()*xmag - xpos - lrint(rmapx_f(xorg)); + } + if (ymag < 0) { + y = lrint(double(p.y())/double(-ymag) - rmapy_f(yorg)) - ypos; + } + else { + y = p.y()*ymag - ypos - lrint(rmapy_f(yorg)); + } + return QPoint(x, y); + } + +int View::mapx(int x) const + { + if (xmag < 0) { + return lrint(double(x)/double(-xmag) - rmapx_f(xorg)) - xpos; + } + else { + return x*xmag - xpos - lrint(rmapx_f(xorg)); + } + } +int View::mapy(int y) const + { + if (ymag < 0) { + return lrint(double(y)/double(-ymag) - rmapy_f(yorg)) - ypos; + } + else { + return y*ymag - ypos - lrint(rmapy_f(yorg)); + } + } +int View::mapxDev(int x) const + { + int val; + if (xmag <= 0) + val = lrint((double(x + xpos) + rmapx_f(xorg)) * double(-xmag)); + else + val = lrint((double(x + xpos) + rmapx_f(xorg)) / double(xmag)); + if (val < 0) // DEBUG + val = 0; + return val; + } + +int View::mapyDev(int y) const + { + if (ymag <= 0) + return lrint((double(y + ypos) + rmapy_f(yorg)) * double(-ymag)); + else + return lrint((double(y + ypos) + rmapy_f(yorg)) / double(ymag)); + } + +// r == relative conversion +int View::rmapx(int x) const + { + if (xmag < 0) + return lrint(double(x) / double(-xmag)); + else + return x * xmag; + } +int View::rmapy(int y) const + { + if (ymag < 0) + return lrint(double(y) / double(-ymag)); + else + return y * ymag; + } +int View::rmapxDev(int x) const + { + if (xmag <= 0) + return x * (-xmag); + else + return lrint(double(x) / double(xmag)); + } +int View::rmapyDev(int y) const + { + if (ymag <= 0) + return y * (-ymag); + else + return lrint(double(y) / double(ymag)); + } +#endif + +double View::rmapx_f(double x) const + { + if (xmag < 0) + return x / double(-xmag); + else + return x * double(xmag); + } +double View::rmapy_f(double y) const + { + if (ymag < 0) + return y / double(-ymag); + else + return y * double(ymag); + } +double View::rmapxDev_f(double x) const + { + if (xmag <= 0) + return x * double(-xmag); + else + return x / double(xmag); + } +double View::rmapyDev_f(double y) const + { + if (ymag <= 0) + return y * double(-ymag); + else + return y / double(ymag); + } + + + /* QRect View::devToVirt(const QRect& r) { diff --git a/muse2/muse/widgets/view.h b/muse2/muse/widgets/view.h index f53c4c72..ad0a6cf9 100644 --- a/muse2/muse/widgets/view.h +++ b/muse2/muse/widgets/view.h @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: view.h,v 1.2.2.1 2008/01/26 07:23:21 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) //========================================================= #ifndef __VIEW_H__ @@ -77,6 +78,10 @@ class View : public QWidget { int rmapy(int y) const; int rmapyDev(int y) const; //QRect devToVirt(const QRect&); + double rmapx_f(double x) const; + double rmapy_f(double y) const; + double rmapxDev_f(double x) const; + double rmapyDev_f(double y) const; void setPainter(QPainter& p); -- cgit v1.2.3