You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
486 lines
14 KiB
486 lines
14 KiB
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
|
|
|
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
|
Bown to claim authorship of this work have been asserted.
|
|
|
|
Other copyrights also apply to some parts of this work. Please
|
|
see the AUTHORS file and individual file headers for details.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version. See the file
|
|
COPYING included with this distribution for more information.
|
|
*/
|
|
|
|
|
|
#include "NotationCanvasView.h"
|
|
#include "misc/Debug.h"
|
|
|
|
#include "misc/Strings.h"
|
|
#include "gui/general/LinedStaffManager.h"
|
|
#include "gui/general/RosegardenCanvasView.h"
|
|
#include "gui/kdeext/QCanvasGroupableItem.h"
|
|
#include "gui/kdeext/QCanvasSimpleSprite.h"
|
|
#include "NotationElement.h"
|
|
#include "NotationProperties.h"
|
|
#include "NotationStaff.h"
|
|
#include <tqcanvas.h>
|
|
#include <tqcolor.h>
|
|
#include <tqpainter.h>
|
|
#include <tqpen.h>
|
|
#include <tqpoint.h>
|
|
#include <tqrect.h>
|
|
#include <tqsize.h>
|
|
#include <tqstring.h>
|
|
#include <tqwidget.h>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
NotationCanvasView::NotationCanvasView(const LinedStaffManager &staffmgr,
|
|
TQCanvas *viewing, TQWidget *parent,
|
|
const char *name, WFlags f) :
|
|
RosegardenCanvasView(viewing, parent, name, f),
|
|
m_linedStaffManager(staffmgr),
|
|
m_lastYPosNearStaff(0),
|
|
m_currentStaff(0),
|
|
m_currentHeight( -1000),
|
|
m_legerLineOffset(false),
|
|
m_heightTracking(false)
|
|
{
|
|
// -- switching mandolin-sonatina first staff to page mode:
|
|
// default params (I think 16,100): render 1000ms position 1070ms
|
|
// 64,100: 1000ms 980ms
|
|
// 8, 100: 1140ms 1140ms
|
|
// 128, 100: 1060ms 980ms
|
|
// 256, 100: 1060ms 980ms / 930ms 920ms
|
|
|
|
// canvas()->retune(256, 100);
|
|
|
|
viewport()->setMouseTracking(true);
|
|
|
|
m_heightMarker = new TQCanvasItemGroup(viewing);
|
|
|
|
m_vert1 = new TQCanvasLineGroupable(viewing, m_heightMarker);
|
|
m_vert1->setPoints(0, 0, 0, 8);
|
|
m_vert1->setPen(TQPen(TQColor(64, 64, 64), 1));
|
|
|
|
m_vert2 = new TQCanvasLineGroupable(viewing, m_heightMarker);
|
|
m_vert2->setPoints(17, 0, 17, 8);
|
|
m_vert2->setPen(TQPen(TQColor(64, 64, 64), 1));
|
|
|
|
m_heightMarker->hide();
|
|
}
|
|
|
|
NotationCanvasView::~NotationCanvasView()
|
|
{
|
|
// All canvas items are deleted in ~NotationView()
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::setHeightTracking(bool t)
|
|
{
|
|
m_heightTracking = t;
|
|
if (!t) {
|
|
m_heightMarker->hide();
|
|
canvas()->update();
|
|
}
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::contentsMouseReleaseEvent(TQMouseEvent *e)
|
|
{
|
|
emit mouseReleased(e);
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::contentsMouseMoveEvent(TQMouseEvent *e)
|
|
{
|
|
NotationStaff *prevStaff = m_currentStaff;
|
|
int prevHeight = m_currentHeight;
|
|
|
|
m_currentStaff = dynamic_cast<NotationStaff *>
|
|
(m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
|
|
|
|
if (!m_currentStaff) {
|
|
|
|
emit hoveredOverNoteChanged(TQString());
|
|
if (prevStaff) {
|
|
m_heightMarker->hide();
|
|
canvas()->update();
|
|
}
|
|
|
|
} else {
|
|
|
|
m_currentHeight = m_currentStaff->getHeightAtCanvasCoords(e->x(), e->y());
|
|
|
|
int x = e->x() - 8; // magic based on mouse cursor size
|
|
bool needUpdate = (m_heightTracking && (m_heightMarker->x() != x));
|
|
m_heightMarker->setX(x);
|
|
|
|
if (prevStaff != m_currentStaff ||
|
|
prevHeight != m_currentHeight) {
|
|
|
|
if (m_heightTracking) {
|
|
setHeightMarkerHeight(e);
|
|
m_heightMarker->show();
|
|
needUpdate = true;
|
|
}
|
|
|
|
emit hoveredOverNoteChanged
|
|
(strtoqstr
|
|
(m_currentStaff->getNoteNameAtCanvasCoords(e->x(), e->y())));
|
|
}
|
|
|
|
if (needUpdate)
|
|
canvas()->update();
|
|
}
|
|
|
|
NotationElement *elt = getElementAtXCoord(e);
|
|
if (elt) {
|
|
emit hoveredOverAbsoluteTimeChanged(elt->getViewAbsoluteTime());
|
|
}
|
|
|
|
// if(tracking) ??
|
|
emit mouseMoved(e);
|
|
}
|
|
|
|
void NotationCanvasView::contentsMousePressEvent(TQMouseEvent *e)
|
|
{
|
|
NOTATION_DEBUG << "NotationCanvasView::contentsMousePressEvent() - btn : "
|
|
<< e->button() << " - state : " << e->state()
|
|
<< endl;
|
|
|
|
TQCanvasItemList itemList = canvas()->collisions(e->pos());
|
|
|
|
// We don't want to use m_currentStaff/Height, because we want
|
|
// to make sure the event happens at the point we clicked at
|
|
// rather than the last point for which contentsMouseMoveEvent
|
|
// happened to be called
|
|
|
|
NotationStaff *staff = dynamic_cast<NotationStaff *>
|
|
(m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
|
|
|
|
TQCanvasItemList::Iterator it;
|
|
NotationElement *clickedNote = 0;
|
|
NotationElement *clickedVagueNote = 0;
|
|
NotationElement *clickedNonNote = 0;
|
|
|
|
bool haveClickHeight = false;
|
|
int clickHeight = 0;
|
|
if (staff) {
|
|
clickHeight = staff->getHeightAtCanvasCoords(e->x(), e->y());
|
|
haveClickHeight = true;
|
|
}
|
|
|
|
for (it = itemList.begin(); it != itemList.end(); ++it) {
|
|
|
|
if ((*it)->active()) {
|
|
emit activeItemPressed(e, *it);
|
|
return ;
|
|
}
|
|
|
|
TQCanvasNotationSprite *sprite =
|
|
dynamic_cast<TQCanvasNotationSprite*>(*it);
|
|
if (!sprite) {
|
|
if (dynamic_cast<TQCanvasNonElementSprite *>(*it)) {
|
|
emit nonNotationItemPressed(e, *it);
|
|
return ;
|
|
} else if (dynamic_cast<TQCanvasText *>(*it)) {
|
|
emit textItemPressed(e, *it);
|
|
return ;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
NotationElement &el = sprite->getNotationElement();
|
|
|
|
// #957364 (Notation: Hard to select upper note in chords of
|
|
// seconds) -- adjust x-coord for shifted note head
|
|
|
|
double cx = el.getCanvasX();
|
|
int nbw = 10;
|
|
|
|
if (staff) {
|
|
nbw = staff->getNotePixmapFactory(false).getNoteBodyWidth();
|
|
bool shifted = false;
|
|
|
|
if (el.event()->get
|
|
<Bool>
|
|
(staff->getProperties().NOTE_HEAD_SHIFTED, shifted) && shifted) {
|
|
cx += nbw;
|
|
}
|
|
}
|
|
|
|
if (el.isNote() && haveClickHeight) {
|
|
long eventHeight = 0;
|
|
if (el.event()->get
|
|
<Int>
|
|
(NotationProperties::HEIGHT_ON_STAFF, eventHeight)) {
|
|
|
|
if (eventHeight == clickHeight) {
|
|
|
|
if (!clickedNote &&
|
|
e->x() >= cx &&
|
|
e->x() <= cx + nbw) {
|
|
clickedNote = ⪙
|
|
} else if (!clickedVagueNote &&
|
|
e->x() >= cx - 2 &&
|
|
e->x() <= cx + nbw + 2) {
|
|
clickedVagueNote = ⪙
|
|
}
|
|
|
|
} else if (eventHeight - 1 == clickHeight ||
|
|
eventHeight + 1 == clickHeight) {
|
|
if (!clickedVagueNote)
|
|
clickedVagueNote = ⪙
|
|
}
|
|
}
|
|
} else if (!el.isNote()) {
|
|
if (!clickedNonNote)
|
|
clickedNonNote = ⪙
|
|
}
|
|
}
|
|
|
|
int staffNo = -1;
|
|
if (staff)
|
|
staffNo = staff->getId();
|
|
|
|
if (clickedNote)
|
|
handleMousePress(clickHeight, staffNo, e, clickedNote);
|
|
else if (clickedNonNote)
|
|
handleMousePress(clickHeight, staffNo, e, clickedNonNote);
|
|
else if (clickedVagueNote)
|
|
handleMousePress(clickHeight, staffNo, e, clickedVagueNote);
|
|
else
|
|
handleMousePress(clickHeight, staffNo, e);
|
|
}
|
|
|
|
void NotationCanvasView::contentsMouseDoubleClickEvent(TQMouseEvent* e)
|
|
{
|
|
NOTATION_DEBUG << "NotationCanvasView::contentsMouseDoubleClickEvent()\n";
|
|
|
|
contentsMousePressEvent(e);
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::processActiveItems(TQMouseEvent* e,
|
|
TQCanvasItemList itemList)
|
|
{
|
|
TQCanvasItem* pressedItem = 0;
|
|
TQCanvasItemList::Iterator it;
|
|
|
|
for (it = itemList.begin(); it != itemList.end(); ++it) {
|
|
|
|
TQCanvasItem *item = *it;
|
|
if (item->active() && !pressedItem) {
|
|
NOTATION_DEBUG << "mousepress : got active item\n";
|
|
pressedItem = item;
|
|
}
|
|
}
|
|
|
|
if (pressedItem)
|
|
emit activeItemPressed(e, pressedItem);
|
|
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::handleMousePress(int height,
|
|
int staffNo,
|
|
TQMouseEvent *e,
|
|
NotationElement *el)
|
|
{
|
|
NOTATION_DEBUG << "NotationCanvasView::handleMousePress() at height "
|
|
<< height << endl;
|
|
|
|
emit itemPressed(height, staffNo, e, el);
|
|
}
|
|
|
|
bool
|
|
NotationCanvasView::posIsTooFarFromStaff(const TQPoint &pos)
|
|
{
|
|
// return true if pos.y is more than m_staffLineThreshold away from
|
|
// the last pos for which a collision was detected
|
|
//
|
|
return (pos.y() > m_lastYPosNearStaff) ?
|
|
(pos.y() - m_lastYPosNearStaff) > (int)m_staffLineThreshold :
|
|
(m_lastYPosNearStaff - pos.y()) > (int)m_staffLineThreshold;
|
|
|
|
}
|
|
|
|
int
|
|
NotationCanvasView::getLegerLineCount(int height, bool &offset)
|
|
{
|
|
//!!! This is far too specifically notation-related to be here, really
|
|
|
|
if (height < 0) {
|
|
|
|
offset = (( -height % 2) == 1);
|
|
return height / 2;
|
|
|
|
} else if (height > 8) {
|
|
|
|
offset = ((height % 2) == 1);
|
|
return (height - 8) / 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::setHeightMarkerHeight(TQMouseEvent *e)
|
|
{
|
|
NotationStaff *staff = dynamic_cast<NotationStaff *>
|
|
(m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
|
|
|
|
int height = staff->getHeightAtCanvasCoords(e->x(), e->y());
|
|
int lineY = staff->getCanvasYForHeight(height, e->x(), e->y());
|
|
|
|
// NOTATION_DEBUG << "NotationCanvasView::setHeightMarkerHeight: "
|
|
// << e->y() << " snapped to line -> " << lineY
|
|
// << " (height " << height << ")" << endl;
|
|
|
|
int spacing = staff->getLineSpacing() - 1;
|
|
|
|
m_staffLineThreshold = spacing;
|
|
m_vert1->setPoints(0, -spacing / 2, 0, spacing / 2);
|
|
m_vert2->setPoints(17, -spacing / 2, 17, spacing / 2); // magic based on mouse cursor size
|
|
m_heightMarker->setY(lineY);
|
|
|
|
bool legerLineOffset = false;
|
|
int legerLineCount = getLegerLineCount(height, legerLineOffset);
|
|
|
|
if (legerLineCount != (int)m_legerLines.size() ||
|
|
legerLineOffset != m_legerLineOffset) {
|
|
|
|
bool above = false;
|
|
if (legerLineCount < 0) {
|
|
above = true;
|
|
legerLineCount = -legerLineCount;
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < (int)m_legerLines.size(); ++i) {
|
|
delete m_legerLines[i];
|
|
}
|
|
m_legerLines.clear();
|
|
|
|
for (i = 0; i < legerLineCount; ++i) {
|
|
|
|
TQCanvasLineGroupable *line =
|
|
new TQCanvasLineGroupable(canvas(), m_heightMarker);
|
|
|
|
line->setPen(TQPen(TQColor(64, 64, 64), 1));
|
|
|
|
int y = (int)m_heightMarker->y() +
|
|
(above ? -1 : 1) * (i * (spacing + 1));
|
|
int x = (int)m_heightMarker->x() + 1;
|
|
|
|
if (legerLineOffset) {
|
|
if (above)
|
|
y -= spacing / 2 + 1;
|
|
else
|
|
y += spacing / 2;
|
|
}
|
|
|
|
line->setPoints(x, y, x + 15, y); // magic based on mouse cursor size
|
|
m_legerLines.push_back(line);
|
|
}
|
|
|
|
m_legerLineOffset = legerLineOffset;
|
|
}
|
|
}
|
|
|
|
NotationElement *
|
|
NotationCanvasView::getElementAtXCoord(TQMouseEvent *e) // any old element
|
|
{
|
|
TQRect threshold(e->pos(), TQSize(4, 100)); //!!!
|
|
threshold.moveCenter(e->pos());
|
|
|
|
TQCanvasItemList itemList = canvas()->collisions(threshold);
|
|
|
|
TQCanvasItemList::Iterator it;
|
|
TQCanvasNotationSprite* sprite = 0;
|
|
|
|
for (it = itemList.begin(); it != itemList.end(); ++it)
|
|
{
|
|
|
|
TQCanvasItem *item = *it;
|
|
|
|
if ((sprite = dynamic_cast<TQCanvasNotationSprite*>(item))) {
|
|
return & (sprite->getNotationElement());
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::viewportPaintEvent(TQPaintEvent *e)
|
|
{
|
|
int cx(e->rect().x()),
|
|
cy(e->rect().y()),
|
|
cw(e->rect().width()) /*,
|
|
ch(e->rect().height())*/;
|
|
// NOTATION_DEBUG << "NotationCanvasView::viewportPaintEvent: (" << cx << ","
|
|
// << cy << ") size (" << cw << "x" << ch << ")" << endl;
|
|
TQCanvasView::viewportPaintEvent(e);
|
|
|
|
cx += contentsX();
|
|
cy += contentsY();
|
|
m_lastRender = e->rect();
|
|
emit renderRequired(std::min(contentsX(), cx),
|
|
std::max(contentsX() + visibleWidth(), cx + cw));
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::drawContents(TQPainter *p, int cx, int cy, int cw, int ch)
|
|
{
|
|
/*
|
|
m_lastRender = TQRect(cx, cy, cw, ch);
|
|
NOTATION_DEBUG << "NotationCanvasView::drawContents: (" << cx << ","
|
|
<< cy << ") size (" << cw << "x" << ch << ")" << endl;
|
|
*/
|
|
TQCanvasView::drawContents(p, cx, cy, cw, ch);
|
|
/*
|
|
emit renderRequired(std::min(contentsX(), cx),
|
|
std::max(contentsX() + visibleWidth(), cx + cw));
|
|
*/
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::slotRenderComplete()
|
|
{
|
|
/* TQPainter painter(viewport());
|
|
int cx(m_lastRender.x()),
|
|
cy(m_lastRender.y()),
|
|
cw(m_lastRender.width()),
|
|
ch(m_lastRender.height());
|
|
NOTATION_DEBUG << "NotationCanvasView::slotRenderComplete: (" << cx << ","
|
|
<< cy << ") size (" << cw << "x" << ch << ")" << endl;
|
|
TQCanvasView::drawContents(&painter, cx, cy, cw, ch);
|
|
*/
|
|
TQPaintEvent ev(m_lastRender);
|
|
TQCanvasView::viewportPaintEvent(&ev);
|
|
}
|
|
|
|
void
|
|
NotationCanvasView::slotExternalWheelEvent(TQWheelEvent* e)
|
|
{
|
|
wheelEvent(e);
|
|
}
|
|
|
|
}
|
|
#include "NotationCanvasView.moc"
|