diff options
Diffstat (limited to 'attic/muse2-oom/muse2/muse/arranger')
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/CMakeLists.txt | 82 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/alayout.cpp | 200 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/alayout.h | 60 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/arranger.cpp | 1104 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/arranger.h | 173 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/pcanvas.cpp | 2977 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/pcanvas.h | 139 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/tlist.cpp | 1595 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/tlist.h | 115 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/trackautomationview.cpp | 52 | ||||
-rw-r--r-- | attic/muse2-oom/muse2/muse/arranger/trackautomationview.h | 20 |
11 files changed, 6517 insertions, 0 deletions
diff --git a/attic/muse2-oom/muse2/muse/arranger/CMakeLists.txt b/attic/muse2-oom/muse2/muse/arranger/CMakeLists.txt new file mode 100644 index 00000000..21a06698 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/CMakeLists.txt @@ -0,0 +1,82 @@ +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 2002-2006 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +#============================================================================= + +# +# Expand Qt macros +# +QT4_WRAP_CPP (arranger_mocs + alayout.h + arranger.h + pcanvas.h + tlist.h + ) + +# +# List of source files to compile +# +file (GLOB arranger_source_files + alayout.cpp + arranger.cpp + pcanvas.cpp + tlist.cpp + ) + +# +# Define target +# +add_library ( arranger SHARED + ${arranger_source_files} + ${arranger_mocs} + ) + +# +# Append to the list of translations +# +set (FILES_TO_TRANSLATE + ${FILES_TO_TRANSLATE} + ${arranger_source_files} + CACHE INTERNAL "" + ) + +# +# Compilation flags and target name +# +set_target_properties( arranger + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h -fPIC" + OUTPUT_NAME muse_arranger + ) + +# +# Linkage +# +target_link_libraries ( arranger + ${QT_LIBRARIES} + awl + mixer + widgets + ) + +# +# Install location +# +install(TARGETS arranger + DESTINATION ${MusE_MODULES_DIR} + ) + diff --git a/attic/muse2-oom/muse2/muse/arranger/alayout.cpp b/attic/muse2-oom/muse2/muse/arranger/alayout.cpp new file mode 100644 index 00000000..c7e1e4e3 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/alayout.cpp @@ -0,0 +1,200 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: alayout.cpp,v 1.8 2004/02/28 14:58:24 wschweer Exp $ +// (C) Copyright 2002 Werner Schweer (ws@seh.de) +//========================================================= + +#include "alayout.h" +#include "arranger.h" + +#include <QScrollBar> + +//--------------------------------------------------------- +// wadd +//--------------------------------------------------------- + +void TLLayout::wadd(int idx, QWidget* w) + { + li[idx] = new QWidgetItem(w); + if (idx == 0) + stack = (WidgetStack*)w; + if (idx == 1) + sb = (QScrollBar*)w; + addItem(li[idx]); + } + +#if 0 +//--------------------------------------------------------- +// TLLayoutIterator +//--------------------------------------------------------- + +class TLLayoutIterator // : public QGLayoutIterator ddskrjo + { + int idx; + QList<QLayoutItem*> list; + + public: + TLLayoutIterator(QList<QLayoutItem*> l) : idx(0), list(l) {} + QLayoutItem *current() { return idx < int(list->count()) ? list->at(idx) : 0; } + QLayoutItem *next() { idx++; return current(); } + QLayoutItem *takeCurrent() { return list->take( idx ); } + }; + +//--------------------------------------------------------- +// iterator +//--------------------------------------------------------- + +QLayoutIterator TLLayout::iterator() + { + return QLayoutIterator(0); //new TLLayoutIterator(&ilist)); ddskrjo + } + +void TLLayout::addItem(QLayoutItem *item) + { + ilist.append(item); + } + +TLLayout::~TLLayout() + { + deleteAllItems(); + } + +#endif + +//--------------------------------------------------------- +// setGeometry +// perform geometry management for tracklist: +// +// 0 1 2 +// +-----------+--------+---------+ +// | Trackinfo | scroll | header 2| +// | | bar +---------+ y1 +// | ^ | | ^ | +// | | | <list> | +// | 0 | 1 | 3 | +// +-----------+--------+---------+ y2 +// | hline 4 | +// +----------+-------------------+ y3 +// | button 5 | | +// +----------+-------------------+ +//--------------------------------------------------------- + +void TLLayout::setGeometry(const QRect &rect) + { + //if(_inSetGeometry) // p4.0.11 Tim + // return; + //_inSetGeometry = true; + + int w = rect.width(); + int h = rect.height(); + + QSize s0; + if (stack->visibleWidget()) { + s0 = stack->visibleWidget()->minimumSizeHint(); + if (!s0.isValid()) // widget has no geometry management + s0 = stack->visibleWidget()->size(); + } + else + s0 = stack->minimumSizeHint(); + + QSize s1 = li[1]->sizeHint(); + QSize s2 = li[2]->sizeHint(); + QSize s3 = li[3]->sizeHint(); + QSize s4 = li[4]->sizeHint(); + QSize s5 = li[5]->sizeHint(); + + int y1 = 30; // fixed header height + int ah = h - s5.height() - s4.height() - y1; // list height + int aw = w - s1.width() - s0.width(); // list width + + int y2 = ah + s2.height(); + int y3 = y2 + s4.height(); + int x1 = s0.width(); + int x2 = x1 + s1.width(); + + li[0]->setGeometry(QRect(0, 0, s0.width(), y2)); + + QWidget* widget = stack->visibleWidget(); + int range = s0.height() - y2; + if (range < 0) + range = 0; + // Note this appears to cause a single recursive call to this function - jumps to beginning, + // because now the scroll bar wants to be put in the layout. + sb->setVisible(range != 0); + if (range) + sb->setMaximum(range); + + if (widget) { + //QSize r(s0.width(), y2); + QSize r(s0.width(), y2 < s0.height() ? s0.height() : y2); // p4.0.11 Tim + widget->setGeometry(0, 0, r.width(), r.height()); + } + + li[1]->setGeometry(QRect(x1, 0, s1.width(), y2)); + li[2]->setGeometry(QRect(x2, 0, aw, s2.height())); + li[3]->setGeometry(QRect(x2, y1, aw, ah)); + li[4]->setGeometry(QRect(0, y2, w, s4.height())); + li[5]->setGeometry(QRect(3, y3, s5.width(), s5.height())); + + //_inSetGeometry = false; + } + +//--------------------------------------------------------- +// sizeHint +//--------------------------------------------------------- + +QSize TLLayout::sizeHint() const + { + return QSize(150, 100); + // p4.0.11 Tim. 100 was allowing vertically shrunk trackinfo widgets. Nope, no help. + //return minimumSize(); + } + +//--------------------------------------------------------- +// minimumSize +//--------------------------------------------------------- + +QSize TLLayout::minimumSize() const + { + int w = stack->minimumSizeHint().width(); + w += li[1]->sizeHint().width(); + + return QSize(w, 50); + // p4.0.11 Tim. 50 was allowing vertically shrunk trackinfo widgets. Nope, no help. + //return QSize(w, stack->minimumSizeHint().height()); + } + +//--------------------------------------------------------- +// maximumSize +//--------------------------------------------------------- + +QSize TLLayout::maximumSize() const + { + return QSize(440, 100000); + } + +//--------------------------------------------------------- +// takeAt +//--------------------------------------------------------- + +QLayoutItem* TLLayout::takeAt(int i) + { + if (i >= 0 && i < ilist.size()) + return ilist.takeAt(i); + else + return 0; + } + +//--------------------------------------------------------- +// clear +//--------------------------------------------------------- + +void TLLayout::clear() + { + QLayoutItem* child; + while ((child = takeAt(0)) != 0) { + delete child->widget(); + delete child; + } + } diff --git a/attic/muse2-oom/muse2/muse/arranger/alayout.h b/attic/muse2-oom/muse2/muse/arranger/alayout.h new file mode 100644 index 00000000..8ba1a829 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/alayout.h @@ -0,0 +1,60 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: alayout.h,v 1.3.2.1 2008/01/19 13:33:46 wschweer Exp $ +// (C) Copyright 2002 Werner Schweer (ws@seh.de) +//========================================================= + +#ifndef __ALAYOUT_H__ +#define __ALAYOUT_H__ + +#include <QLayout> +#include <QList> + +class QLayoutItem; +class QScrollBar; + +class WidgetStack; + +//--------------------------------------------------------- +// TLLayout +// arranger trackList layout manager +//--------------------------------------------------------- + +class TLLayout : public QLayout + { + Q_OBJECT + + bool _inSetGeometry; + QList<QLayoutItem*> ilist; + QLayoutItem* li[6]; + QScrollBar* sb; + WidgetStack* stack; + + public: + //TLLayout(QWidget *parent) : QLayout(parent, 0, -1) {} + TLLayout(QWidget *parent) : QLayout(parent) { _inSetGeometry = false; setContentsMargins(0, 0, 0, 0); setSpacing(-1); } + ~TLLayout() { clear(); } + + void addItem(QLayoutItem *item) { ilist.append(item); } + virtual Qt::Orientations expandingDirections() const { return 0; } + virtual bool hasHeightForWidth() const { return false; } + virtual int count() const { return ilist.size(); } + void clear(); + + void wadd(int idx, QWidget* w); + virtual QSize sizeHint() const; + virtual QSize minimumSize() const; + virtual QSize maximumSize() const; + //QSize sizeHint() const; + //QSize minimumSize() const; + //QSize maximumSize() const; + ///QLayoutIterator iterator(); + virtual void setGeometry(const QRect &rect); + + //virtual QLayoutItem* itemAt(int) const { return 0;} // ddskrjo, is pure virtual, overridden + virtual QLayoutItem* itemAt(int i) const { return ilist.value(i);} + virtual QLayoutItem* takeAt(int); // { return 0;} // ddskrjo, is pure virtual, overridden + ///virtual int count() const { return ilist.count(); } // ddskrjo, is pure virtual, overridden + }; +#endif diff --git a/attic/muse2-oom/muse2/muse/arranger/arranger.cpp b/attic/muse2-oom/muse2/muse/arranger/arranger.cpp new file mode 100644 index 00000000..134465b9 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/arranger.cpp @@ -0,0 +1,1104 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: arranger.cpp,v 1.33.2.21 2009/11/17 22:08:22 terminator356 Exp $ +// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +//========================================================= + +#include "config.h" + +#include <stdio.h> +#include <values.h> + +#include <QComboBox> +#include <QGridLayout> +#include <QKeyEvent> +#include <QLabel> +#include <QList> +#include <QMainWindow> +#include <QScrollBar> +#include <QToolBar> +#include <QToolButton> +#include <QVBoxLayout> +#include <QWheelEvent> +#include <QPainter> +//#include <QStackedWidget> + +#include "arranger.h" +#include "song.h" +#include "app.h" +#include "mtscale.h" +#include "scrollscale.h" +#include "pcanvas.h" +#include "poslabel.h" +#include "xml.h" +#include "splitter.h" +#include "lcombo.h" +#include "mtrackinfo.h" +#include "midiport.h" +#include "mididev.h" +#include "utils.h" +#include "globals.h" +#include "tlist.h" +#include "icons.h" +#include "header.h" +#include "utils.h" +#include "alayout.h" +#include "audio.h" +#include "event.h" +#include "midiseq.h" +#include "midictrl.h" +#include "mpevent.h" +#include "gconfig.h" +#include "mixer/astrip.h" +#include "spinbox.h" +#include "tvieweditor.h" + +//--------------------------------------------------------- +// Arranger::setHeaderToolTips +//--------------------------------------------------------- + +void Arranger::setHeaderToolTips() + { + header->setToolTip(COL_RECORD, tr("Enable Recording")); + header->setToolTip(COL_MUTE, tr("Mute/Off Indicator")); + header->setToolTip(COL_SOLO, tr("Solo Indicator")); + header->setToolTip(COL_CLASS, tr("Track Type")); + header->setToolTip(COL_NAME, tr("Track Name")); + header->setToolTip(COL_OCHANNEL, tr("Midi output channel number or audio channels")); + header->setToolTip(COL_OPORT, tr("Midi output port or synth midi port")); + header->setToolTip(COL_TIMELOCK, tr("Time Lock")); + header->setToolTip(COL_AUTOMATION, tr("Automation parameter selection")); + } + + + +//--------------------------------------------------------- +// Arranger::setHeaderWhatsThis +//--------------------------------------------------------- + +void Arranger::setHeaderWhatsThis() + { + header->setWhatsThis(COL_RECORD, tr("Enable recording. Click to toggle.")); + header->setWhatsThis(COL_MUTE, tr("Mute indicator. Click to toggle.\nRight-click to toggle track on/off.\nMute is designed for rapid, repeated action.\nOn/Off is not!")); + header->setWhatsThis(COL_SOLO, tr("Solo indicator. Click to toggle.\nConnected tracks are also 'phantom' soloed,\n indicated by a dark square.")); + header->setWhatsThis(COL_CLASS, tr("Track type. Right-click to change\n midi and drum track types.")); + header->setWhatsThis(COL_NAME, tr("Track name. Double-click to edit.\nRight-click for more options.")); + header->setWhatsThis(COL_OCHANNEL, tr("Midi/drum track: Output channel number.\nAudio track: Channels.\nMid/right-click to change.")); + header->setWhatsThis(COL_OPORT, tr("Midi/drum track: Output port.\nSynth track: Assigned midi port.\nLeft-click to change.\nRight-click to show GUI.")); + header->setWhatsThis(COL_TIMELOCK, tr("Time lock")); + } + +//--------------------------------------------------------- +// Arranger +// is the central widget in app +//--------------------------------------------------------- + +Arranger::Arranger(QMainWindow* parent, const char* name) + : QWidget(parent) + { + setObjectName(name); + _raster = 0; // measure + selected = 0; + // Since program covers 3 controls at once, it is in 'midi controller' units rather than 'gui control' units. + //program = -1; + ///program = CTRL_VAL_UNKNOWN; + ///pan = -65; + ///volume = -1; + setMinimumSize(600, 50); + showTrackinfoFlag = true; + + cursVal = MAXINT; + + //setFocusPolicy(Qt::StrongFocus); + + //--------------------------------------------------- + // ToolBar + // create toolbar in toplevel widget + //--------------------------------------------------- + + parent->addToolBarBreak(); + QToolBar* toolbar = parent->addToolBar(tr("Arranger")); + + QLabel* label = new QLabel(tr("Cursor")); + label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + label->setIndent(3); + //toolbar->addWidget(label); + cursorPos = new PosLabel(0); + cursorPos->setEnabled(false); + cursorPos->setFixedHeight(22); + cursorPos->setObjectName("arrangerCursor"); + toolbar->addWidget(cursorPos); + + /*QToolButton* testView = new QToolButton(); + testView->setText(QString("TG")); + toolbar->addWidget(testView); + connect(testView, SIGNAL(clicked()), SLOT(showTrackViews())); + */ + + const char* rastval[] = { + QT_TRANSLATE_NOOP("@default", "Off"), QT_TRANSLATE_NOOP("@default", "Bar"), "1/2", "1/4", "1/8", "1/16" + }; + label = new QLabel(tr("Snap")); + label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + label->setIndent(3); + toolbar->addWidget(label); + QComboBox* raster = new QComboBox(); + for (int i = 0; i < 6; i++) + raster->insertItem(i, tr(rastval[i])); + raster->setCurrentIndex(1); + // Set the audio record part snapping. Set to 0 (bar), the same as this combo box intial raster. + song->setArrangerRaster(0); + toolbar->addWidget(raster); + connect(raster, SIGNAL(activated(int)), SLOT(_setRaster(int))); + ///raster->setFocusPolicy(Qt::NoFocus); + raster->setFocusPolicy(Qt::TabFocus); + + // Song len + label = new QLabel(tr("Len")); + label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + label->setIndent(3); + toolbar->addWidget(label); + + // song length is limited to 10000 bars; the real song len is limited + // by overflows in tick computations + // + lenEntry = new SpinBox(1, 10000, 1); + lenEntry->setValue(song->len()); + lenEntry->setToolTip(tr("song length - bars")); + lenEntry->setWhatsThis(tr("song length - bars")); + toolbar->addWidget(lenEntry); + connect(lenEntry, SIGNAL(valueChanged(int)), SLOT(songlenChanged(int))); + + typeBox = new LabelCombo(tr("Type"), 0); + typeBox->insertItem(0, tr("NO")); + typeBox->insertItem(1, tr("GM")); + typeBox->insertItem(2, tr("GS")); + typeBox->insertItem(3, tr("XG")); + typeBox->setCurrentIndex(0); + typeBox->setToolTip(tr("midi song type")); + typeBox->setWhatsThis(tr("midi song type")); + ///typeBox->setFocusPolicy(Qt::NoFocus); + typeBox->setFocusPolicy(Qt::TabFocus); + toolbar->addWidget(typeBox); + connect(typeBox, SIGNAL(activated(int)), SLOT(modeChange(int))); + + label = new QLabel(tr("Pitch")); + label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + label->setIndent(3); + toolbar->addWidget(label); + + globalPitchSpinBox = new SpinBox(-127, 127, 1); + globalPitchSpinBox->setValue(song->globalPitchShift()); + globalPitchSpinBox->setToolTip(tr("midi pitch")); + globalPitchSpinBox->setWhatsThis(tr("global midi pitch shift")); + toolbar->addWidget(globalPitchSpinBox); + connect(globalPitchSpinBox, SIGNAL(valueChanged(int)), SLOT(globalPitchChanged(int))); + + label = new QLabel(tr("Tempo")); + label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + label->setIndent(3); + toolbar->addWidget(label); + + globalTempoSpinBox = new SpinBox(50, 200, 1, toolbar); + globalTempoSpinBox->setSuffix(QString("%")); + globalTempoSpinBox->setValue(tempomap.globalTempo()); + globalTempoSpinBox->setToolTip(tr("midi tempo")); + globalTempoSpinBox->setWhatsThis(tr("midi tempo")); + toolbar->addWidget(globalTempoSpinBox); + connect(globalTempoSpinBox, SIGNAL(valueChanged(int)), SLOT(globalTempoChanged(int))); + + QToolButton* tempo50 = new QToolButton(); + tempo50->setText(QString("50%")); + toolbar->addWidget(tempo50); + connect(tempo50, SIGNAL(clicked()), SLOT(setTempo50())); + + QToolButton* tempo100 = new QToolButton(); + tempo100->setText(tr("N")); + toolbar->addWidget(tempo100); + connect(tempo100, SIGNAL(clicked()), SLOT(setTempo100())); + + QToolButton* tempo200 = new QToolButton(); + tempo200->setText(QString("200%")); + toolbar->addWidget(tempo200); + connect(tempo200, SIGNAL(clicked()), SLOT(setTempo200())); + + QVBoxLayout* box = new QVBoxLayout(this); + box->setContentsMargins(0, 0, 0, 0); + box->setSpacing(0); + box->addWidget(hLine(this), Qt::AlignTop); + //QFrame* hline = hLine(this); + //hline->setLineWidth(0); + //box->addWidget(hline, Qt::AlignTop); + + //--------------------------------------------------- + // Tracklist + //--------------------------------------------------- + + int xscale = -100; + int yscale = 1; + + split = new Splitter(Qt::Horizontal, this, "split"); + split->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + box->addWidget(split, 1000); + //split->setHandleWidth(10); + + QWidget* tracklist = new QWidget(split); + + split->setStretchFactor(split->indexOf(tracklist), 0); + //tracklist->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding, 0, 100)); + QSizePolicy tpolicy = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + tpolicy.setHorizontalStretch(0); + tpolicy.setVerticalStretch(100); + tracklist->setSizePolicy(tpolicy); + + QWidget* editor = new QWidget(split); + split->setStretchFactor(split->indexOf(editor), 1); + //editor->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, + // Changed by T356. Was causing "large int implicitly truncated" warning. These are UCHAR values... + //1000, 100)); + //232, 100)); // 232 is what it was being truncated to, but what is the right value?... + //255, 100)); + QSizePolicy epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + epolicy.setHorizontalStretch(255); + epolicy.setVerticalStretch(100); + editor->setSizePolicy(epolicy); + + //--------------------------------------------------- + // Track Info + //--------------------------------------------------- + + infoScroll = new QScrollBar(Qt::Vertical, tracklist); + infoScroll->setObjectName("infoScrollBar"); + //genTrackInfo(tracklist); // Moved below + + // Track-Info Button + ib = new QToolButton(tracklist); + ib->setText(tr("TrackInfo")); + ib->setCheckable(true); + ib->setChecked(showTrackinfoFlag); + ib->setFocusPolicy(Qt::NoFocus); + connect(ib, SIGNAL(toggled(bool)), SLOT(showTrackInfo(bool))); + + header = new Header(tracklist, "header"); + + header->setFixedHeight(30); + + QFontMetrics fm1(header->font()); + int fw = 8; + + header->setColumnLabel(tr("R"), COL_RECORD, fm1.width('R')+fw); + header->setColumnLabel(tr("M"), COL_MUTE, fm1.width('M')+fw); + header->setColumnLabel(tr("S"), COL_SOLO, fm1.width('S')+fw); + header->setColumnLabel(tr("C"), COL_CLASS, fm1.width('C')+fw); + header->setColumnLabel(tr("Track"), COL_NAME, 100); + header->setColumnLabel(tr("Port"), COL_OPORT, 60); + header->setColumnLabel(tr("Ch"), COL_OCHANNEL, 30); + header->setColumnLabel(tr("T"), COL_TIMELOCK, fm1.width('T')+fw); + header->setColumnLabel(tr("Automation"), COL_AUTOMATION, 75); + header->setResizeMode(COL_RECORD, QHeaderView::Fixed); + header->setResizeMode(COL_MUTE, QHeaderView::Fixed); + header->setResizeMode(COL_SOLO, QHeaderView::Fixed); + header->setResizeMode(COL_CLASS, QHeaderView::Fixed); + header->setResizeMode(COL_NAME, QHeaderView::Interactive); + header->setResizeMode(COL_OPORT, QHeaderView::Interactive); + header->setResizeMode(COL_OCHANNEL, QHeaderView::Fixed); + header->setResizeMode(COL_TIMELOCK, QHeaderView::Fixed); + header->setResizeMode(COL_AUTOMATION, QHeaderView::Interactive); + + setHeaderToolTips(); + setHeaderWhatsThis(); + header->setMovable (true ); + list = new TList(header, tracklist, "tracklist"); + + // Do this now that the list is available. + genTrackInfo(tracklist); + + ///connect(list, SIGNAL(selectionChanged()), SLOT(trackSelectionChanged())); + connect(list, SIGNAL(selectionChanged(Track*)), SLOT(trackSelectionChanged())); + connect(list, SIGNAL(selectionChanged(Track*)), midiTrackInfo, SLOT(setTrack(Track*))); + connect(header, SIGNAL(sectionResized(int,int,int)), list, SLOT(redraw())); + connect(header, SIGNAL(sectionMoved(int,int,int)), list, SLOT(redraw())); + connect(header, SIGNAL(sectionMoved(int,int,int)), this, SLOT(headerMoved())); + + // tracklist: + // + // 0 1 2 + // +-----------+--------+---------+ + // | Trackinfo | scroll | Header | 0 + // | | bar +---------+ + // | | | TList | 1 + // +-----------+--------+---------+ + // | hline | 2 + // +-----+------------------------+ + // | ib | | 3 + // +-----+------------------------+ + + connect(infoScroll, SIGNAL(valueChanged(int)), SLOT(trackInfoScroll(int))); + tgrid = new TLLayout(tracklist); // layout manager for this + tgrid->wadd(0, trackInfo); + tgrid->wadd(1, infoScroll); + tgrid->wadd(2, header); + tgrid->wadd(3, list); + tgrid->wadd(4, hLine(tracklist)); + tgrid->wadd(5, ib); + + //--------------------------------------------------- + // Editor + //--------------------------------------------------- + + int offset = AL::sigmap.ticksMeasure(0); + hscroll = new ScrollScale(-1000, -10, xscale, song->len(), Qt::Horizontal, editor, -offset); + hscroll->setFocusPolicy(Qt::NoFocus); + ib->setFixedHeight(hscroll->sizeHint().height()); + + // Changed p3.3.43 Too small steps for me... + //vscroll = new QScrollBar(1, 20*20, 1, 5, 0, Vertical, editor); + //vscroll = new QScrollBar(1, 20*20, 5, 25, 0, Qt::Vertical, editor); + vscroll = new QScrollBar(editor); + ///vscroll->setMinimum(1); + vscroll->setMinimum(0); // Tim. + vscroll->setMaximum(20*20); + vscroll->setSingleStep(5); + vscroll->setPageStep(25); + vscroll->setValue(0); + vscroll->setOrientation(Qt::Vertical); + + list->setScroll(vscroll); + + QList<int> vallist; + vallist.append(tgrid->maximumSize().width()); + split->setSizes(vallist); + + QGridLayout* egrid = new QGridLayout(editor); + egrid->setColumnStretch(0, 50); + egrid->setRowStretch(2, 50); + egrid->setContentsMargins(0, 0, 0, 0); + egrid->setSpacing(0); + + time = new MTScale(&_raster, editor, xscale); + time->setOrigin(-offset, 0); + canvas = new PartCanvas(&_raster, editor, xscale, yscale); + canvas->setBg(config.partCanvasBg); + canvas->setCanvasTools(arrangerTools); + canvas->setOrigin(-offset, 0); + canvas->setFocus(); + //parent->setFocusProxy(canvas); // Tim. + + connect(canvas, SIGNAL(setUsedTool(int)), this, SIGNAL(setUsedTool(int))); + connect(canvas, SIGNAL(trackChanged(Track*)), list, SLOT(selectTrack(Track*))); + connect(list, SIGNAL(keyPressExt(QKeyEvent*)), canvas, SLOT(redirKeypress(QKeyEvent*))); + connect(canvas, SIGNAL(selectTrackAbove()), list, SLOT(selectTrackAbove())); + connect(canvas, SIGNAL(selectTrackBelow()), list, SLOT(selectTrackBelow())); + + connect(this, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); + connect(list, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); + + //egrid->addMultiCellWidget(time, 0, 0, 0, 1); + //egrid->addMultiCellWidget(hLine(editor), 1, 1, 0, 1); + egrid->addWidget(time, 0, 0, 1, 2); + egrid->addWidget(hLine(editor), 1, 0, 1, 2); + + egrid->addWidget(canvas, 2, 0); + egrid->addWidget(vscroll, 2, 1); + egrid->addWidget(hscroll, 3, 0, Qt::AlignBottom); + + connect(vscroll, SIGNAL(valueChanged(int)), canvas, SLOT(setYPos(int))); + connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); + connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); + connect(vscroll, SIGNAL(valueChanged(int)), list, SLOT(setYPos(int))); + connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); // + connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); + connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); + connect(canvas, SIGNAL(verticalScroll(unsigned)),SLOT(verticalScrollSetYpos(unsigned))); + connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); + connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); + connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); + + connect(canvas, SIGNAL(tracklistChanged()), list, SLOT(tracklistChanged())); + connect(canvas, SIGNAL(dclickPart(Track*)), SIGNAL(editPart(Track*))); + connect(canvas, SIGNAL(startEditor(PartList*,int)), SIGNAL(startEditor(PartList*, int))); + + connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int))); + //connect(song, SIGNAL(mTypeChanged(MType)), SLOT(setMode((int)MType))); // p4.0.7 Tim. + connect(canvas, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int))); + connect(canvas, SIGNAL(selectionChanged()), SIGNAL(selectionChanged())); + connect(canvas, SIGNAL(dropSongFile(const QString&)), SIGNAL(dropSongFile(const QString&))); + connect(canvas, SIGNAL(dropMidiFile(const QString&)), SIGNAL(dropMidiFile(const QString&))); + + connect(canvas, SIGNAL(toolChanged(int)), SIGNAL(toolChanged(int))); +// connect(song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(seek())); + + // Removed p3.3.43 + // Song::addMarker() already emits a 'markerChanged'. + //connect(time, SIGNAL(addMarker(int)), SIGNAL(addMarker(int))); + + configChanged(); // set configuration values + if(canvas->part()) + midiTrackInfo->setTrack(canvas->part()->track()); // Tim. + showTrackInfo(showTrackinfoFlag); + + // Take care of some tabbies! + setTabOrder(tempo200, trackInfo); + setTabOrder(trackInfo, infoScroll); + setTabOrder(infoScroll, list); + setTabOrder(list, canvas); + //setTabOrder(canvas, ib); + //setTabOrder(ib, hscroll); + } + +//--------------------------------------------------------- +// updateHScrollRange +//--------------------------------------------------------- + +//void Arranger::updateHScrollRange() +//{ +// int s = 0, e = song->len(); + // Show one more measure. +// e += AL::sigmap.ticksMeasure(e); + // Show another quarter measure due to imprecise drawing at canvas end point. +// e += AL::sigmap.ticksMeasure(e) / 4; + // Compensate for the fixed vscroll width. +// e += canvas->rmapxDev(-vscroll->width()); +// int s1, e1; +// hscroll->range(&s1, &e1); +// if(s != s1 || e != e1) +// hscroll->setRange(s, e); +//} + +//--------------------------------------------------------- +// headerMoved +//--------------------------------------------------------- + +void Arranger::headerMoved() + { + //header->setResizeMode(COL_NAME, QHeaderView::Stretch); + } + +//--------------------------------------------------------- +// setTime +//--------------------------------------------------------- + +void Arranger::setTime(unsigned tick) + { + if (tick == MAXINT) + cursorPos->setEnabled(false); + else { + cursVal = tick; + cursorPos->setEnabled(true); + cursorPos->setValue(tick); + time->setPos(3, tick, false); + } + } + +//--------------------------------------------------------- +// toolChange +//--------------------------------------------------------- + +void Arranger::setTool(int t) + { + canvas->setTool(t); + } + +//--------------------------------------------------------- +// dclickPart +//--------------------------------------------------------- + +void Arranger::dclickPart(Track* t) + { + emit editPart(t); + } + +//--------------------------------------------------------- +// configChanged +//--------------------------------------------------------- + +void Arranger::configChanged() + { + //printf("Arranger::configChanged\n"); + + if (config.canvasBgPixmap.isEmpty()) { + canvas->setBg(config.partCanvasBg); + canvas->setBg(QPixmap()); + //printf("Arranger::configChanged - no bitmap!\n"); + } + else { + + //printf("Arranger::configChanged - bitmap %s!\n", config.canvasBgPixmap.ascii()); + canvas->setBg(QPixmap(config.canvasBgPixmap)); + } + ///midiTrackInfo->setFont(config.fonts[2]); + //updateTrackInfo(type); + } + +//--------------------------------------------------------- +// songlenChanged +//--------------------------------------------------------- + +void Arranger::songlenChanged(int n) + { + int newLen = AL::sigmap.bar2tick(n, 0, 0); + song->setLen(newLen); + } +//--------------------------------------------------------- +// songChanged +//--------------------------------------------------------- + +void Arranger::songChanged(int type) + { + // Is it simply a midi controller value adjustment? Forget it. + if(type != SC_MIDI_CONTROLLER) + { + unsigned endTick = song->len(); + int offset = AL::sigmap.ticksMeasure(endTick); + hscroll->setRange(-offset, endTick + offset); //DEBUG + canvas->setOrigin(-offset, 0); + time->setOrigin(-offset, 0); + + int bar, beat; + unsigned tick; + AL::sigmap.tickValues(endTick, &bar, &beat, &tick); + if (tick || beat) + ++bar; + lenEntry->blockSignals(true); + lenEntry->setValue(bar); + lenEntry->blockSignals(false); + + if(type & SC_SONG_TYPE) // p4.0.7 Tim. + setMode(song->mtype()); + + trackSelectionChanged(); + canvas->partsChanged(); + typeBox->setCurrentIndex(int(song->mtype())); + if (type & SC_SIG) + time->redraw(); + if (type & SC_TEMPO) + setGlobalTempo(tempomap.globalTempo()); + + if(type & SC_TRACK_REMOVED) + { + AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2)); + //AudioStrip* w = (AudioStrip*)(trackInfo->widget(2)); + if(w) + { + Track* t = w->getTrack(); + if(t) + { + TrackList* tl = song->tracks(); + iTrack it = tl->find(t); + if(it == tl->end()) + { + delete w; + trackInfo->addWidget(0, 2); + //trackInfo->insertWidget(2, 0); + selected = 0; + } + } + } + } + } + + updateTrackInfo(type); + } + +//--------------------------------------------------------- +// trackSelectionChanged +//--------------------------------------------------------- + +void Arranger::trackSelectionChanged() + { + TrackList* tracks = song->tracks(); + Track* track = 0; + for (iTrack t = tracks->begin(); t != tracks->end(); ++t) { + if ((*t)->selected()) { + track = *t; + break; + } + } + if (track == selected) + return; + selected = track; + updateTrackInfo(-1); + } + +//--------------------------------------------------------- +// modeChange +//--------------------------------------------------------- + +void Arranger::modeChange(int mode) + { + song->setMType(MType(mode)); + updateTrackInfo(-1); + } + +//--------------------------------------------------------- +// setMode +//--------------------------------------------------------- + +void Arranger::setMode(int mode) + { + typeBox->blockSignals(true); // + // This will only set if different. + typeBox->setCurrentIndex(mode); + typeBox->blockSignals(false); // + } + +void Arranger::showTrackViews() +{ + TrackViewEditor* ted = new TrackViewEditor(this); + ted->show(); +} +//--------------------------------------------------------- +// writeStatus +//--------------------------------------------------------- + +void Arranger::writeStatus(int level, Xml& xml) + { + xml.tag(level++, "arranger"); + xml.intTag(level, "info", ib->isChecked()); + split->writeStatus(level, xml); + list->writeStatus(level, xml, "list"); + + xml.intTag(level, "xpos", hscroll->pos()); + xml.intTag(level, "xmag", hscroll->mag()); + xml.intTag(level, "ypos", vscroll->value()); + xml.etag(level, "arranger"); + } + +//--------------------------------------------------------- +// readStatus +//--------------------------------------------------------- + +void Arranger::readStatus(Xml& xml) + { + for (;;) { + Xml::Token token(xml.parse()); + const QString& tag(xml.s1()); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::TagStart: + if (tag == "info") + showTrackinfoFlag = xml.parseInt(); + else if (tag == split->objectName()) + split->readStatus(xml); + else if (tag == "list") + list->readStatus(xml, "list"); + else if (tag == "xmag") + hscroll->setMag(xml.parseInt()); + else if (tag == "xpos") { + int hpos = xml.parseInt(); + hscroll->setPos(hpos); + } + else if (tag == "ypos") + vscroll->setValue(xml.parseInt()); + else + xml.unknown("Arranger"); + break; + case Xml::TagEnd: + if (tag == "arranger") { + ib->setChecked(showTrackinfoFlag); + return; + } + default: + break; + } + } + } + +//--------------------------------------------------------- +// setRaster +//--------------------------------------------------------- + +void Arranger::_setRaster(int index) + { + static int rasterTable[] = { + 1, 0, 768, 384, 192, 96 + }; + _raster = rasterTable[index]; + // Set the audio record part snapping. + song->setArrangerRaster(_raster); + canvas->redraw(); + } + +//--------------------------------------------------------- +// reset +//--------------------------------------------------------- + +void Arranger::reset() + { + canvas->setXPos(0); + canvas->setYPos(0); + hscroll->setPos(0); + vscroll->setValue(0); + time->setXPos(0); + time->setYPos(0); + } + +//--------------------------------------------------------- +// cmd +//--------------------------------------------------------- + +void Arranger::cmd(int cmd) + { + int ncmd; + switch (cmd) { + case CMD_CUT_PART: + ncmd = PartCanvas::CMD_CUT_PART; + break; + case CMD_COPY_PART: + ncmd = PartCanvas::CMD_COPY_PART; + break; + case CMD_PASTE_PART: + ncmd = PartCanvas::CMD_PASTE_PART; + break; + case CMD_PASTE_CLONE_PART: + ncmd = PartCanvas::CMD_PASTE_CLONE_PART; + break; + case CMD_PASTE_PART_TO_TRACK: + ncmd = PartCanvas::CMD_PASTE_PART_TO_TRACK; + break; + case CMD_PASTE_CLONE_PART_TO_TRACK: + ncmd = PartCanvas::CMD_PASTE_CLONE_PART_TO_TRACK; + break; + case CMD_INSERT_PART: + ncmd = PartCanvas::CMD_INSERT_PART; + break; + case CMD_INSERT_EMPTYMEAS: + ncmd = PartCanvas::CMD_INSERT_EMPTYMEAS; + break; + default: + return; + } + canvas->cmd(ncmd); + } + +//--------------------------------------------------------- +// globalPitchChanged +//--------------------------------------------------------- + +void Arranger::globalPitchChanged(int val) + { + song->setGlobalPitchShift(val); + } + +//--------------------------------------------------------- +// globalTempoChanged +//--------------------------------------------------------- + +void Arranger::globalTempoChanged(int val) + { + audio->msgSetGlobalTempo(val); + song->tempoChanged(); + } + +//--------------------------------------------------------- +// setTempo50 +//--------------------------------------------------------- + +void Arranger::setTempo50() + { + setGlobalTempo(50); + } + +//--------------------------------------------------------- +// setTempo100 +//--------------------------------------------------------- + +void Arranger::setTempo100() + { + setGlobalTempo(100); + } + +//--------------------------------------------------------- +// setTempo200 +//--------------------------------------------------------- + +void Arranger::setTempo200() + { + setGlobalTempo(200); + } + +//--------------------------------------------------------- +// setGlobalTempo +//--------------------------------------------------------- + +void Arranger::setGlobalTempo(int val) + { + if(val != globalTempoSpinBox->value()) + globalTempoSpinBox->setValue(val); + } + +//--------------------------------------------------------- +// verticalScrollSetYpos +//--------------------------------------------------------- +void Arranger::verticalScrollSetYpos(unsigned ypos) + { + vscroll->setValue(ypos); + } + +//--------------------------------------------------------- +// trackInfoScroll +//--------------------------------------------------------- + +void Arranger::trackInfoScroll(int y) + { + if (trackInfo->visibleWidget()) + trackInfo->visibleWidget()->move(0, -y); + } + +//--------------------------------------------------------- +// WidgetStack +//--------------------------------------------------------- + +WidgetStack::WidgetStack(QWidget* parent, const char* name) + : QWidget(parent) + { + setObjectName(name); + top = -1; + } + +//--------------------------------------------------------- +// raiseWidget +//--------------------------------------------------------- + +void WidgetStack::raiseWidget(int idx) + { + if (top != -1) { + if (stack[top]) + stack[top]->hide(); + } + top = idx; + if (idx == -1) + return; + int n = stack.size(); + if (idx >= n) + return; + if (stack[idx]) + stack[idx]->show(); + } + +//--------------------------------------------------------- +// addWidget +//--------------------------------------------------------- + +void WidgetStack::addWidget(QWidget* w, unsigned int n) + { + if (w) + w->hide(); + if (stack.size() <= n ) + stack.push_back(w); + else + stack[n] = w; + } + +QWidget* WidgetStack::getWidget(unsigned int n) + { + if (stack.size() <= n ) + return 0; + return stack[n]; + } + +//--------------------------------------------------------- +// visibleWidget +//--------------------------------------------------------- + +QWidget* WidgetStack::visibleWidget() const + { + if (top != -1) + return stack[top]; + return 0; + } + +//--------------------------------------------------------- +// minimumSizeHint +//--------------------------------------------------------- + +QSize WidgetStack::minimumSizeHint() const + { + if (top == -1) + { + //printf("WidgetStack::minimumSizeHint top is -1\n"); + return (QSize(0, 0)); + } + QSize s(0,0); + for (unsigned int i = 0; i < stack.size(); ++i) { + if (stack[i]) { + QSize ss = stack[i]->minimumSizeHint(); + if (!ss.isValid()) + ss = stack[i]->minimumSize(); + s = s.expandedTo(ss); + } + } + //printf("WidgetStack::minimumSizeHint width:%d height:%d\n", s.width(), s.height()); // REMOVE Tim. + return s; + } + +//--------------------------------------------------------- +// clear +//--------------------------------------------------------- + +void Arranger::clear() + { + AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2)); + if (w) + delete w; + trackInfo->addWidget(0, 2); + selected = 0; + } + +void Arranger::wheelEvent(QWheelEvent* ev) + { + emit redirectWheelEvent(ev); + } + +void Arranger::controllerChanged(Track *t) +{ + canvas->controllerChanged(t); +} + +//--------------------------------------------------------- +// showTrackInfo +//--------------------------------------------------------- + +void Arranger::showTrackInfo(bool flag) + { + showTrackinfoFlag = flag; + trackInfo->setVisible(flag); + infoScroll->setVisible(flag); + updateTrackInfo(-1); + } + +//--------------------------------------------------------- +// genTrackInfo +//--------------------------------------------------------- + +void Arranger::genTrackInfo(QWidget* parent) + { + trackInfo = new WidgetStack(parent, "trackInfoStack"); + //trackInfo->setFocusPolicy(Qt::TabFocus); // p4.0.9 + //trackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + + noTrackInfo = new QWidget(trackInfo); + noTrackInfo->setAutoFillBackground(true); + QPixmap *noInfoPix = new QPixmap(160, 1000); //muse_leftside_logo_xpm); + const QPixmap *logo = new QPixmap(*museLeftSideLogo); + noInfoPix->fill(noTrackInfo->palette().color(QPalette::Window) ); + QPainter p(noInfoPix); + p.drawPixmap(10, 0, *logo, 0,0, logo->width(), logo->height()); + + QPalette palette; + palette.setBrush(noTrackInfo->backgroundRole(), QBrush(*noInfoPix)); + noTrackInfo->setPalette(palette); + noTrackInfo->setGeometry(0, 0, 65, 200); + noTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding)); + + midiTrackInfo = new MidiTrackInfo(trackInfo); + //midiTrackInfo->setFocusPolicy(Qt::TabFocus); // p4.0.9 + //midiTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum)); + trackInfo->addWidget(noTrackInfo, 0); + trackInfo->addWidget(midiTrackInfo, 1); + trackInfo->addWidget(0, 2); + +/// genMidiTrackInfo(); + } + +//--------------------------------------------------------- +// updateTrackInfo +//--------------------------------------------------------- + +void Arranger::updateTrackInfo(int flags) + { + if (!showTrackinfoFlag) { + switchInfo(-1); + return; + } + if (selected == 0) { + switchInfo(0); + return; + } + if (selected->isMidiTrack()) { + switchInfo(1); + // If a new part was selected, and only if it's different. + if((flags & SC_SELECTION) && midiTrackInfo->track() != selected) + // Set a new track and do a complete update. + midiTrackInfo->setTrack(selected); + else + // Otherwise just regular update with specific flags. + midiTrackInfo->updateTrackInfo(flags); + } + else { + switchInfo(2); + } + } + +//--------------------------------------------------------- +// switchInfo +//--------------------------------------------------------- + +void Arranger::switchInfo(int n) + { + if (n == 2) { + AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2)); + if (w == 0 || selected != w->getTrack()) { + if (w) + delete w; + w = new AudioStrip(trackInfo, (AudioTrack*)selected); + switch(selected->type()) {/*{{{*/ + case Track::AUDIO_OUTPUT: + w->setObjectName("MixerAudioOutStrip"); + break; + case Track::AUDIO_GROUP: + w->setObjectName("MixerAudioGroupStrip"); + break; + case Track::AUDIO_AUX: + w->setObjectName("MixerAuxStrip"); + break; + case Track::WAVE: + w->setObjectName("MixerWaveStrip"); + break; + case Track::AUDIO_INPUT: + w->setObjectName("MixerAudioInStrip"); + break; + case Track::AUDIO_SOFTSYNTH: + w->setObjectName("MixerSynthStrip"); + break; + case Track::MIDI: + case Track::DRUM: + { + w->setObjectName("MidiTrackStrip"); + } + break; + }/*}}}*/ + //w->setFocusPolicy(Qt::TabFocus); // p4.0.9 + connect(song, SIGNAL(songChanged(int)), w, SLOT(songChanged(int))); + connect(muse, SIGNAL(configChanged()), w, SLOT(configChanged())); + w->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + trackInfo->addWidget(w, 2); + w->show(); + //setTabOrder(midiTrackInfo, w); // p4.0.9 + tgrid->activate(); + tgrid->update(); // muse-2 Qt4 + } + } + if (trackInfo->curIdx() == n) + return; + trackInfo->raiseWidget(n); + tgrid->activate(); + tgrid->update(); // muse-2 Qt4 + } + +/* +QSize WidgetStack::minimumSize() const +{ + printf("WidgetStack::minimumSize\n"); // REMOVE Tim. + return minimumSizeHint(); +} + +int WidgetStack::minimumHeight() const +{ + printf("WidgetStack::minimumHeight\n"); // REMOVE Tim. + return minimumSizeHint().height(); +} +*/ diff --git a/attic/muse2-oom/muse2/muse/arranger/arranger.h b/attic/muse2-oom/muse2/muse/arranger/arranger.h new file mode 100644 index 00000000..dde7c48a --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/arranger.h @@ -0,0 +1,173 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: arranger.h,v 1.17.2.15 2009/11/14 03:37:48 terminator356 Exp $ +// (C) Copyright 1999 Werner Schweer (ws@seh.de) +//========================================================= + +#ifndef __ARRANGER_H__ +#define __ARRANGER_H__ + +#include <vector> + +#include "midieditor.h" +#include "pcanvas.h" +#include "trackautomationview.h" + +class QAction; +class QCheckBox; +class QMainWindow; +class QMenu; +class QScrollBar; +class QToolButton; +class QWheelEvent; +class QKeyEvent; +//class QStackedWidget; + +class Header; +class TList; +class ScrollScale; +class MTScale; +class Track; +class Xml; +class Splitter; +class LabelCombo; +class PosLabel; +class MidiTrackInfo; +class TLLayout; +class WidgetStack; +class AudioStrip; +class SpinBox; + +//--------------------------------------------------------- +// WidgetStack +//--------------------------------------------------------- + +class WidgetStack : public QWidget { + Q_OBJECT + std::vector<QWidget*> stack; + int top; + + public: + WidgetStack(QWidget* parent, const char* name = 0); + void raiseWidget(int idx); + void addWidget(QWidget* w, unsigned int idx); + QWidget* getWidget(unsigned int idx); + QWidget* visibleWidget() const; + int curIdx() const { return top; } + virtual QSize minimumSizeHint() const; + //QSize minimumSize() const; + //int minimumHeight() const; + }; + +//--------------------------------------------------------- +// Arranger +//--------------------------------------------------------- + +class Arranger : public QWidget { + Q_OBJECT + + int _quant, _raster; + PartCanvas* canvas; + ScrollScale* hscroll; + QScrollBar* vscroll; + TList* list; + Header* header; + MTScale* time; + SpinBox* lenEntry; + bool showTrackinfoFlag; + WidgetStack* trackInfo; + //QStackedWidget* trackInfo; + QScrollBar* infoScroll; + //MidiTrackInfoBase* midiTrackInfo; + MidiTrackInfo* midiTrackInfo; + AudioStrip* waveTrackInfo; + QWidget* noTrackInfo; + TLLayout* tgrid; + + Track* selected; + + LabelCombo* typeBox; + QToolButton* ib; + int trackInfoType; + Splitter* split; + ///QMenu* pop; + int songType; + PosLabel* cursorPos; + SpinBox* globalTempoSpinBox; + SpinBox* globalPitchSpinBox; + + unsigned cursVal; + void genTrackInfo(QWidget* parent); + void genMidiTrackInfo(); + void genWaveTrackInfo(); + void switchInfo(int); + void setHeaderToolTips(); + void setHeaderWhatsThis(); + + private slots: + void _setRaster(int); + void songlenChanged(int); + void showTrackInfo(bool); + void trackSelectionChanged(); + void trackInfoScroll(int); + void songChanged(int); + void modeChange(int); + void setTime(unsigned); + void headerMoved(); + void globalPitchChanged(int); + void globalTempoChanged(int); + void setTempo50(); + void setTempo100(); + void setTempo200(); + //void seek(); + void verticalScrollSetYpos(unsigned); + void showTrackViews(); + + signals: + void redirectWheelEvent(QWheelEvent*); + void editPart(Track*); + void selectionChanged(); + void dropSongFile(const QString&); + void dropMidiFile(const QString&); + void startEditor(PartList*, int); + void toolChanged(int); + //void addMarker(int); + void setUsedTool(int); + + + protected: + virtual void wheelEvent(QWheelEvent* e); + + public slots: + void dclickPart(Track*); + void setTool(int); + void updateTrackInfo(int flags); + void configChanged(); + void controllerChanged(Track *t); + + public: + enum { CMD_CUT_PART, CMD_COPY_PART, CMD_PASTE_PART, CMD_PASTE_CLONE_PART, CMD_PASTE_PART_TO_TRACK, CMD_PASTE_CLONE_PART_TO_TRACK, + CMD_INSERT_PART, CMD_INSERT_EMPTYMEAS }; + + Arranger(QMainWindow* parent, const char* name = 0); + + PartCanvas* getCanvas() { return canvas; } + void setMode(int); + void reset(); + + void writeStatus(int level, Xml&); + void readStatus(Xml&); + + Track* curTrack() const { return selected; } + void cmd(int); + bool isSingleSelection() { return canvas->isSingleSelection(); } + int selectionSize() { return canvas->selectionSize(); } + void setGlobalTempo(int); + void clear(); + + unsigned cursorValue() { return cursVal; } + }; + +#endif + diff --git a/attic/muse2-oom/muse2/muse/arranger/pcanvas.cpp b/attic/muse2-oom/muse2/muse/arranger/pcanvas.cpp new file mode 100644 index 00000000..3e6919a7 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/pcanvas.cpp @@ -0,0 +1,2977 @@ +//========================================================= +// MusE +// 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) +//========================================================= + +#include <stdio.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <errno.h> +#include <values.h> +#include <uuid/uuid.h> +#include <math.h> + +#include <QClipboard> +#include <QLineEdit> +#include <QMenu> +#include <QMessageBox> +#include <QPainter> +#include <QUrl> + +#include "widgets/tools.h" +#include "pcanvas.h" +#include "midieditor.h" +#include "globals.h" +#include "icons.h" +#include "event.h" +#include "xml.h" +#include "wave.h" +#include "audio.h" +#include "shortcuts.h" +#include "gconfig.h" +#include "app.h" +#include "filedialog.h" +#include "marker/marker.h" + +// Moved into global config by Tim. +/* +const char* partColorNames[] = { + "Default", + "Refrain", + "Bridge", + "Intro", + "Coda", + "Chorus", + "Solo", + "Brass", + "Percussion", + "Drums", + "Guitar", + "Bass", + "Flute", + "Strings", + "Keyboard", + "Piano", + "Saxophon", + }; +*/ + +/* +//--------------------------------------------------------- +// ColorListItem +//--------------------------------------------------------- + +class ColorListItem { //: public QCustomMenuItem { ddskrjo + QColor color; + int h; + int fontheight; + QString label; + virtual QSize sizeHint() { return QSize(80, h); } + virtual void paint(QPainter* p, const QColorGroup&, bool act, bool enabled, int x, int y, int w, int h) + { + p->fillRect(x+5, y+2, h-4, h-4, QBrush(color)); + p->drawText(x+5 + h - 4 + 3, y+(fontheight * 3) / 4, label); + } + + public: + ColorListItem(const QColor& c, int _h, int _fh, const char* txt) + : color(c), h(_h), fontheight(_fh), label(txt) { + } + QString text() const { return QString("PartColor"); } + }; +*/ +// ORCAN : colorRect does the same job as the above class. +// Shall we get rid of the class? + +//--------------------------------------------------------- +// colorRect +// paints a rectangular icon with a given color +//--------------------------------------------------------- + +QIcon colorRect(const QColor& color, int width, int height) { + QPainter painter; + QPixmap image(width, height); + painter.begin(&image); + painter.setBrush(color); + QRect rectangle(0, 0, width, height); + painter.drawRect(rectangle); + painter.end(); + QIcon icon(image); + return icon; +} + +//--------------------------------------------------------- +// NPart +//--------------------------------------------------------- + +NPart::NPart(Part* e) : CItem(Event(), e) + { + int th = track()->height(); + int y = track()->y(); + //printf("NPart::NPart track name:%s, y:%d h:%d\n", track()->name().toLatin1().constData(), y, th); + + ///setPos(QPoint(e->tick(), y + 1)); + setPos(QPoint(e->tick(), y)); + + ///setBBox(QRect(e->tick(), y + 1, e->lenTick(), th)); + // NOTE: For adjustable border size: If using a two-pixel border width while drawing, use second line. + // If one-pixel width, use first line. Tim. + //setBBox(QRect(e->tick(), y, e->lenTick(), th)); + setBBox(QRect(e->tick(), y + 1, e->lenTick(), th)); + } + +//--------------------------------------------------------- +// PartCanvas +//--------------------------------------------------------- + +PartCanvas::PartCanvas(int* r, QWidget* parent, int sx, int sy) + : Canvas(parent, sx, sy) + { + setAcceptDrops(true); + _raster = r; + + setFocusPolicy(Qt::StrongFocus); + // Defaults: + lineEditor = 0; + editMode = false; + + tracks = song->tracks(); + setMouseTracking(true); + drag = DRAG_OFF; + curColorIndex = 0; + partsChanged(); + } + +//--------------------------------------------------------- +// y2pitch +//--------------------------------------------------------- + +int PartCanvas::y2pitch(int y) const + { + TrackList* tl = song->tracks(); + int yy = 0; + int idx = 0; + for (iTrack it = tl->begin(); it != tl->end(); ++it, ++idx) { + int h = (*it)->height(); + // if ((y >= yy) && (y < yy+h)) + if (y < yy+h) + break; + yy += h; + } + return idx; + } + +//--------------------------------------------------------- +// pitch2y +//--------------------------------------------------------- + +int PartCanvas::pitch2y(int p) const + { + TrackList* tl = song->tracks(); + int yy = 0; + int idx = 0; + for (iTrack it = tl->begin(); it != tl->end(); ++it, ++idx) { + if (idx == p) + break; + yy += (*it)->height(); + } + return yy; + } + +//--------------------------------------------------------- +// leaveEvent +//--------------------------------------------------------- + +void PartCanvas::leaveEvent(QEvent*) + { + emit timeChanged(MAXINT); + } + +//--------------------------------------------------------- +// returnPressed +//--------------------------------------------------------- + +void PartCanvas::returnPressed() + { + lineEditor->hide(); + Part* oldPart = editPart->part(); + Part* newPart = oldPart->clone(); + //printf("PartCanvas::returnPressed before msgChangePart oldPart refs:%d Arefs:%d newPart refs:%d Arefs:%d\n", oldPart->events()->refCount(), oldPart->events()->arefCount(), newPart->events()->refCount(), newPart->events()->arefCount()); + + newPart->setName(lineEditor->text()); + // Indicate do undo, and do port controller values but not clone parts. + //audio->msgChangePart(oldPart, newPart); + audio->msgChangePart(oldPart, newPart, true, true, false); + //printf("PartCanvas::returnPressed after msgChangePart oldPart refs:%d Arefs:%d newPart refs:%d Arefs:%d\n", oldPart->events()->refCount(), oldPart->events()->arefCount(), newPart->events()->refCount(), newPart->events()->arefCount()); + + editMode = false; + } + +//--------------------------------------------------------- +// viewMouseDoubleClick +//--------------------------------------------------------- + +void PartCanvas::viewMouseDoubleClickEvent(QMouseEvent* event) + { + if (_tool != PointerTool) { + viewMousePressEvent(event); + return; + } + QPoint cpos = event->pos(); + curItem = items.find(cpos); + bool shift = event->modifiers() & Qt::ShiftModifier; + if (curItem) { + if (event->button() == Qt::LeftButton && shift) { + editPart = (NPart*)curItem; + QRect r = map(curItem->bbox()); + if (lineEditor == 0) { + lineEditor = new QLineEdit(this); + lineEditor->setFrame(true); + } + editMode = true; + lineEditor->setGeometry(r); + lineEditor->setText(editPart->name()); + lineEditor->setFocus(); + lineEditor->show(); + } + else if (event->button() == Qt::LeftButton) { + deselectAll(); + selectItem(curItem, true); + emit dclickPart(((NPart*)(curItem))->track()); + } + } + // + // double click creates new part between left and + // right mark + + else { + TrackList* tl = song->tracks(); + iTrack it; + int yy = 0; + int y = event->y(); + for (it = tl->begin(); it != tl->end(); ++it) { + int h = (*it)->height(); + if (y >= yy && y < (yy + h)) + break; + yy += h; + } + if (pos[2] - pos[1] > 0 && it != tl->end()) { + Track* track = *it; + switch(track->type()) { + case Track::MIDI: + case Track::DRUM: + { + MidiPart* part = new MidiPart((MidiTrack*)track); + part->setTick(pos[1]); + part->setLenTick(pos[2]-pos[1]); + part->setName(track->name()); + NPart* np = new NPart(part); + items.add(np); + deselectAll(); + part->setSelected(true); + audio->msgAddPart(part); + } + break; + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + break; + } + } + } + } + +//--------------------------------------------------------- +// startUndo +//--------------------------------------------------------- + +void PartCanvas::startUndo(DragType) + { + song->startUndo(); + } + +//--------------------------------------------------------- +// endUndo +//--------------------------------------------------------- + +void PartCanvas::endUndo(DragType t, int flags) + { + song->endUndo(flags | ((t == MOVE_COPY || t == MOVE_CLONE) + ? SC_PART_INSERTED : SC_PART_MODIFIED)); + } + +//--------------------------------------------------------- +// moveCanvasItems +//--------------------------------------------------------- + +void PartCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtype, int*) +{ + /* + if(editor->parts()->empty()) + return; + + //struct p2c + //{ + // Part* newp; + // int xdiff; + //} + + //std::set<Part*> parts2change; + //typedef std::set<Part*>::iterator iptc; + std::map<Part*, Part*> parts2change; + typedef std::map<Part*, Part*>::iterator iP2C; + + int modified = 0; + for(iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) + { + Part* part = ip->second; + if(!part) + continue; + + int npartoffset = 0; + for(iCItem ici = items.begin(); ici != items.end(); ++ici) + { + CItem* ci = ici->second; + //Part* pt = ci->part(); + //if(!pt) + if(ci->part() != part) + continue; + + int x = ci->pos().x() + dx; + int y = pitch2y(y2pitch(ci->pos().y()) + dp); + QPoint newpos = raster(QPoint(x, y)); + + // Test moving the item... + + //int offset = testMoveItem(ci, newpos, dragtype); + NEvent* nevent = (NEvent*) ci; + Event event = nevent->event(); + //int npitch = y2pitch(newpos.y()); + x = newpos.x(); + if (x < 0) + x = 0; + + int ntick = editor->rasterVal(x) - part->tick(); + if (ntick < 0) + ntick = 0; + int diff = ntick + event.lenTick() - part->lenTick(); + + // If moving the item would require a new part size... + if(diff > npartoffset) + npartoffset = diff; + } + + if(npartoffset > 0) + { + // Create new part... + // if there are several events that are moved outside the part, it will be recreated for each + // so the part _in_ the event will not be valid, ask the authority. + Part* newPart = part->clone(); + //Part* newPart = Canvas::part()->clone(); + + newPart->setLenTick(newPart->lenTick() + npartoffset); + audio->msgChangePart(part, newPart,false); + + modified = SC_PART_MODIFIED; + + // BUG FIX: #1650953 + // Added by T356. + // Fixes posted "select and drag past end of part - crashing" bug + for(iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) + { + if(ip->second == part) + { + editor->parts()->erase(ip); + break; + } + } + + editor->parts()->add(newPart); + if(parts2change.find(part) == parts2change.end()) + parts2change.insert(std::pair<Part*, Part*> (part, newPart)); + +// part = newPart; // reassign +// item->setPart(part); +// item->setEvent(newEvent); +// curPart = part; +// curPartId = curPart->sn(); + + } + } +*/ + +// int modified = 0; + for(iCItem ici = items.begin(); ici != items.end(); ++ici) + { + CItem* ci = ici->second; + + // If this item's part is in the parts2change list, change the item's part to the new part. + //Part* pt = ci->part(); + //iP2C ip2c = parts2change.find(pt); + //if(ip2c != parts2change.end()) + // ci->setPart(ip2c->second); + + int x = ci->pos().x(); + int y = ci->pos().y(); + int nx = x + dx; + int ny = pitch2y(y2pitch(y) + dp); + QPoint newpos = raster(QPoint(nx, ny)); + selectItem(ci, true); + + if(moveItem(ci, newpos, dtype)) + ci->move(newpos); + if(moving.size() == 1) { + itemReleased(curItem, newpos); + } + if(dtype == MOVE_COPY || dtype == MOVE_CLONE) + selectItem(ci, false); + } + + + //if(pflags) + // *pflags = modified; +} + +//--------------------------------------------------------- +// moveItem +// return false, if copy/move not allowed +//--------------------------------------------------------- + +// Changed by T356. +//bool PartCanvas::moveItem(CItem* item, const QPoint& newpos, DragType t, int*) +bool PartCanvas::moveItem(CItem* item, const QPoint& newpos, DragType t) + { + NPart* npart = (NPart*) item; + Part* spart = npart->part(); + Track* track = npart->track(); + unsigned dtick = newpos.x(); + unsigned ntrack = y2pitch(item->mp().y()); + Track::TrackType type = track->type(); + if (tracks->index(track) == ntrack && (dtick == spart->tick())) { + return false; + } + if (ntrack >= tracks->size()) { + ntrack = tracks->size(); + Track* newTrack = song->addTrack(int(type)); + if (type == Track::WAVE) { + WaveTrack* st = (WaveTrack*) track; + WaveTrack* dt = (WaveTrack*) newTrack; + dt->setChannels(st->channels()); + } + emit tracklistChanged(); + } + Track* dtrack = tracks->index(ntrack); + + if (dtrack->type() != type) { + QMessageBox::critical(this, QString("MusE"), + tr("Cannot copy/move/clone to different Track-Type")); + return false; + } + + Part* dpart; + //bool clone = (t == MOVE_CLONE) || (spart->events()->arefCount() > 1); + //bool clone = (t == MOVE_CLONE); + bool clone = (t == MOVE_CLONE || (t == MOVE_COPY && spart->events()->arefCount() > 1)); + + if(t == MOVE_MOVE) + { + // This doesn't increment aref count, and doesn't chain clones. + // It also gives the new part a new serial number, but it is + // overwritten with the old one by Song::changePart(), from Audio::msgChangePart() below. + dpart = spart->clone(); + dpart->setTrack(dtrack); + } + else + // This increments aref count if cloned, and chains clones. + // It also gives the new part a new serial number. + dpart = dtrack->newPart(spart, clone); + + dpart->setTick(dtick); + + //printf("PartCanvas::moveItem before add/changePart clone:%d spart:%p events:%p refs:%d Arefs:%d sn:%d dpart:%p events:%p refs:%d Arefs:%d sn:%d\n", clone, spart, spart->events(), spart->events()->refCount(), spart->events()->arefCount(), spart->sn(), dpart, dpart->events(), dpart->events()->refCount(), dpart->events()->arefCount(), dpart->sn()); + + if(t == MOVE_MOVE) + item->setPart(dpart); + //if (!clone) { + if (t == MOVE_COPY && !clone) { + // + // Copy Events + // + EventList* se = spart->events(); + EventList* de = dpart->events(); + for (iEvent i = se->begin(); i != se->end(); ++i) { + Event oldEvent = i->second; + Event ev = oldEvent.clone(); + de->add(ev); + } + } + if (t == MOVE_COPY || t == MOVE_CLONE) { + // These will not increment ref count, and will not chain clones... + if (dtrack->type() == Track::WAVE) + audio->msgAddPart((WavePart*)dpart,false); + else + audio->msgAddPart(dpart,false); + } + else if (t == MOVE_MOVE) { + dpart->setSelected(spart->selected()); + // These will increment ref count if not a clone, and will chain clones... + if (dtrack->type() == Track::WAVE) + // Indicate no undo, and do not do port controller values and clone parts. + //audio->msgChangePart((WavePart*)spart, (WavePart*)dpart,false); + audio->msgChangePart((WavePart*)spart, (WavePart*)dpart, false, false, false); + else + // Indicate no undo, and do port controller values but not clone parts. + //audio->msgChangePart(spart, dpart, false); + audio->msgChangePart(spart, dpart, false, true, false); + + spart->setSelected(false); + } + //printf("PartCanvas::moveItem after add/changePart spart:%p events:%p refs:%d Arefs:%d dpart:%p events:%p refs:%d Arefs:%d\n", spart, spart->events(), spart->events()->refCount(), spart->events()->arefCount(), dpart, dpart->events(), dpart->events()->refCount(), dpart->events()->arefCount()); + + if (song->len() < (dpart->lenTick() + dpart->tick())) + song->setLen(dpart->lenTick() + dpart->tick()); + //endUndo(t); + return true; + } + +//--------------------------------------------------------- +// raster +//--------------------------------------------------------- + +QPoint PartCanvas::raster(const QPoint& p) const + { + int y = pitch2y(y2pitch(p.y())); + int x = p.x(); + if (x < 0) + x = 0; + x = AL::sigmap.raster(x, *_raster); + if (x < 0) + x = 0; + return QPoint(x, y); + } + +//--------------------------------------------------------- +// partsChanged +//--------------------------------------------------------- + +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); + items.add(np); + if (i->second->selected()) { + selectItem(np, true); + } + } + ++idx; + } + redraw(); + } + +//--------------------------------------------------------- +// updateSelection +//--------------------------------------------------------- + +void PartCanvas::updateSelection() + { + for (iCItem i = items.begin(); i != items.end(); ++i) { + NPart* part = (NPart*)(i->second); + part->part()->setSelected(i->second->isSelected()); + } + emit selectionChanged(); + redraw(); + } + +//--------------------------------------------------------- +// resizeItem +//--------------------------------------------------------- + +void PartCanvas::resizeItem(CItem* i, bool noSnap) + { + Track* t = ((NPart*)(i))->track(); + Part* p = ((NPart*)(i))->part(); + + int pos = p->tick() + i->width(); + int snappedpos = p->tick(); + if (!noSnap) { + snappedpos = AL::sigmap.raster(pos, *_raster); + } + unsigned int newwidth = snappedpos - p->tick(); + if (newwidth == 0) + newwidth = AL::sigmap.rasterStep(p->tick(), *_raster); + + song->cmdResizePart(t, p, newwidth); + } + +//--------------------------------------------------------- +// newItem +// first create local Item +//--------------------------------------------------------- + +CItem* PartCanvas::newItem(const QPoint& pos, int) + { + int x = pos.x(); + if (x < 0) + x = 0; + x = AL::sigmap.raster(x, *_raster); + unsigned trackIndex = y2pitch(pos.y()); + if (trackIndex >= tracks->size()) + return 0; + Track* track = tracks->index(trackIndex); + if(!track) + return 0; + + Part* pa = 0; + NPart* np = 0; + switch(track->type()) { + case Track::MIDI: + case Track::DRUM: + pa = new MidiPart((MidiTrack*)track); + pa->setTick(x); + pa->setLenTick(0); + break; + case Track::WAVE: + pa = new WavePart((WaveTrack*)track); + pa->setTick(x); + pa->setLenTick(0); + break; + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + return 0; + } + pa->setName(track->name()); + pa->setColorIndex(curColorIndex); + np = new NPart(pa); + return np; + } + +//--------------------------------------------------------- +// newItem +//--------------------------------------------------------- + +void PartCanvas::newItem(CItem* i, bool noSnap) + { + Part* p = ((NPart*)(i))->part(); + + int len = i->width(); + if (!noSnap) + len = AL::sigmap.raster(len, *_raster); + if (len == 0) + len = AL::sigmap.rasterStep(p->tick(), *_raster); + p->setLenTick(len); + p->setSelected(true); + audio->msgAddPart(p); + } + +//--------------------------------------------------------- +// deleteItem +//--------------------------------------------------------- + +bool PartCanvas::deleteItem(CItem* i) + { + Part* p = ((NPart*)(i))->part(); + audio->msgRemovePart(p); //Invokes songChanged which calls partsChanged which makes it difficult to delete them there + return true; + } + +//--------------------------------------------------------- +// splitItem +//--------------------------------------------------------- + +void PartCanvas::splitItem(CItem* item, const QPoint& pt) + { + NPart* np = (NPart*) item; + Track* t = np->track(); + Part* p = np->part(); + int x = pt.x(); + if (x < 0) + x = 0; + song->cmdSplitPart(t, p, AL::sigmap.raster(x, *_raster)); + } + +//--------------------------------------------------------- +// glueItem +//--------------------------------------------------------- + +void PartCanvas::glueItem(CItem* item) + { + NPart* np = (NPart*) item; + Track* t = np->track(); + Part* p = np->part(); + song->cmdGluePart(t, p); + } + +//--------------------------------------------------------- +// genItemPopup +//--------------------------------------------------------- + +QMenu* PartCanvas::genItemPopup(CItem* item) + { + NPart* npart = (NPart*) item; + Track::TrackType trackType = npart->track()->type(); + + QMenu* partPopup = new QMenu(this); + + QAction *act_cut = partPopup->addAction(*editcutIconSet, tr("C&ut")); + act_cut->setData(4); + act_cut->setShortcut(Qt::CTRL+Qt::Key_X); + + QAction *act_copy = partPopup->addAction(*editcopyIconSet, tr("&Copy")); + act_copy->setData(5); + act_copy->setShortcut(Qt::CTRL+Qt::Key_C); + + partPopup->addSeparator(); + int rc = npart->part()->events()->arefCount(); + QString st = QString(tr("s&elect ")); + if(rc > 1) + st += (QString().setNum(rc) + QString(" ")); + st += QString(tr("clones")); + QAction *act_select = partPopup->addAction(st); + act_select->setData(18); + + partPopup->addSeparator(); + QAction *act_rename = partPopup->addAction(tr("rename")); + act_rename->setData(0); + + QMenu* colorPopup = partPopup->addMenu(tr("color")); + + // part color selection + //const QFontMetrics& fm = colorPopup->fontMetrics(); + //int h = fm.lineSpacing(); + + for (int i = 0; i < NUM_PARTCOLORS; ++i) { + //ColorListItem* item = new ColorListItem(config.partColors[i], h, fontMetrics().height(), partColorNames[i]); //ddskrjo + QAction *act_color = colorPopup->addAction(colorRect(config.partColors[i], 80, 80), config.partColorNames[i]); + act_color->setData(20+i); + } + + QAction *act_delete = partPopup->addAction(QIcon(*deleteIcon), tr("delete")); // ddskrjo added QIcon to all + act_delete->setData(1); + QAction *act_split = partPopup->addAction(QIcon(*cutIcon), tr("split")); + act_split->setData(2); + QAction *act_glue = partPopup->addAction(QIcon(*glueIcon), tr("glue")); + act_glue->setData(3); + QAction *act_declone = partPopup->addAction(tr("de-clone")); + act_declone->setData(15); + + partPopup->addSeparator(); + switch(trackType) { + case Track::MIDI: { + QAction *act_pianoroll = partPopup->addAction(QIcon(*pianoIconSet), tr("pianoroll")); + act_pianoroll->setData(10); + QAction *act_mlist = partPopup->addAction(QIcon(*edit_listIcon), tr("list")); + act_mlist->setData(12); + QAction *act_mexport = partPopup->addAction(tr("export")); + act_mexport->setData(16); + } + break; + case Track::DRUM: { + QAction *act_dlist = partPopup->addAction(QIcon(*edit_listIcon), tr("list")); + act_dlist->setData(12); + QAction *act_drums = partPopup->addAction(QIcon(*edit_drummsIcon), tr("drums")); + act_drums->setData(13); + QAction *act_dexport = partPopup->addAction(tr("export")); + act_dexport->setData(16); + } + break; + case Track::WAVE: { + QAction *act_wedit = partPopup->addAction(QIcon(*edit_waveIcon), tr("wave edit")); + act_wedit->setData(14); + QAction *act_wexport = partPopup->addAction(tr("export")); + act_wexport->setData(16); + QAction *act_wfinfo = partPopup->addAction(tr("file info")); + act_wfinfo->setData(17); + } + break; + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + break; + } + + act_select->setEnabled( rc > 1); + act_delete->setEnabled( true); + act_cut->setEnabled( true); + act_declone->setEnabled( rc > 1); + + return partPopup; + } + +//--------------------------------------------------------- +// itemPopup +//--------------------------------------------------------- + +void PartCanvas::itemPopup(CItem* item, int n, const QPoint& pt) + { + PartList* pl = new PartList; + NPart* npart = (NPart*)(item); + pl->add(npart->part()); + switch(n) { + case 0: // rename + { + editPart = npart; + QRect r = map(curItem->bbox()); + if (lineEditor == 0) { + lineEditor = new QLineEdit(this); + lineEditor->setFrame(true); + } + lineEditor->setText(editPart->name()); + lineEditor->setFocus(); + lineEditor->show(); + lineEditor->setGeometry(r); + editMode = true; + } + break; + case 1: // delete + deleteItem(item); + break; + case 2: // split + splitItem(item, pt); + break; + case 3: // glue + glueItem(item); + break; + case 4: + copy(pl); + audio->msgRemovePart(npart->part()); + break; + case 5: + copy(pl); + break; + case 10: // pianoroll edit + emit startEditor(pl, 0); + return; + case 12: // list edit + emit startEditor(pl, 1); + return; + case 13: // drum edit + emit startEditor(pl, 3); + return; + case 14: // wave edit + { + // Changed to allow multiple selected parts to be shown. By T356 + // Slightly inefficient to add (above), then clear here. + // Should really only add npart->part() to pl only if NOT here. + // Removed. Added wave editor menu item instead. + //pl->clear(); + //PartList* ptl = npart->track()->parts(); + //for(ciPart pi = ptl->begin(); pi != ptl->end(); pi++) + //{ + // if(pi->second->selected()) + // pl->add(pi->second); + //} + emit startEditor(pl, 4); + } + return; + case 15: // declone + { + Part* spart = npart->part(); + Track* track = npart->track(); + Part* dpart = track->newPart(spart, false); + //printf("PartCanvas::itemPopup: #1 spart %s %p next:%s %p prev:%s %p\n", spart->name().toLatin1().constData(), spart, spart->nextClone()->name().toLatin1().constData(), spart->nextClone(), spart->prevClone()->name().toLatin1().constData(), spart->prevClone()); + //printf("PartCanvas::itemPopup: #1 dpart %s %p next:%s %p prev:%s %p\n", dpart->name().toLatin1().constData(), dpart, dpart->nextClone()->name().toLatin1().constData(), dpart->nextClone(), dpart->prevClone()->name().toLatin1().constData(), dpart->prevClone()); + + EventList* se = spart->events(); + EventList* de = dpart->events(); + for (iEvent i = se->begin(); i != se->end(); ++i) { + Event oldEvent = i->second; + Event ev = oldEvent.clone(); + de->add(ev); + } + song->startUndo(); + // Indicate no undo, and do port controller values but not clone parts. + //audio->msgChangePart(spart, dpart, false); + audio->msgChangePart(spart, dpart, false, true, false); + //printf("PartCanvas::itemPopup: #2 spart %s %p next:%s %p prev:%s %p\n", spart->name().toLatin1().constData(), spart, spart->nextClone()->name().toLatin1().constData(), spart->nextClone(), spart->prevClone()->name().toLatin1().constData(), spart->prevClone()); + //printf("PartCanvas::itemPopup: #2 dpart %s %p next:%s %p prev:%s %p\n", dpart->name().toLatin1().constData(), dpart, dpart->nextClone()->name().toLatin1().constData(), dpart->nextClone(), dpart->prevClone()->name().toLatin1().constData(), dpart->prevClone()); + + song->endUndo(SC_PART_MODIFIED); + break; // Has to be break here, right? + } + case 16: // Export to file + { + const Part* part = item->part(); + bool popenFlag = false; + //QString fn = getSaveFileName(QString(""), part_file_pattern, this, tr("MusE: save part")); + QString fn = getSaveFileName(QString(""), part_file_save_pattern, this, tr("MusE: save part")); + if (!fn.isEmpty()) { + FILE* fp = fileOpen(this, fn, ".mpt", "w", popenFlag, false, false); + if (fp) { + Xml tmpXml = Xml(fp); + //part->write(0, tmpXml); + // Write the part. Indicate that it's a copy operation - to add special markers, + // and force full wave paths. + part->write(0, tmpXml, true, true); + fclose(fp); + } + } + break; + } + + case 17: // File info + { + Part* p = item->part(); + EventList* el = p->events(); + QString str = tr("Part name") + ": " + p->name() + "\n" + tr("Files") + ":"; + for (iEvent e = el->begin(); e != el->end(); ++e) + { + Event event = e->second; + SndFileR f = event.sndFile(); + if (f.isNull()) + continue; + //str.append("\n" + f.path()); + str.append(QString("\n@") + QString().setNum(event.tick()) + QString(" len:") + + QString().setNum(event.lenTick()) + QString(" ") + f.path()); + } + QMessageBox::information(this, "File info", str, "Ok", 0); + break; + } + case 18: // Select clones + { + Part* part = item->part(); + + // Traverse and process the clone chain ring until we arrive at the same part again. + // The loop is a safety net. + Part* p = part; + int j = part->cevents()->arefCount(); + if(j > 0) + { + for(int i = 0; i < j; ++i) + { + //printf("PartCanvas::itemPopup i:%d %s %p events %p refs:%d arefs:%d\n", i, p->name().toLatin1().constData(), p, part->cevents(), part->cevents()->refCount(), j); + + p->setSelected(true); + p = p->nextClone(); + if(p == part) + break; + } + //song->update(); + song->update(SC_SELECTION); + } + + break; + } + case 20 ... NUM_PARTCOLORS+20: + { + curColorIndex = n - 20; + bool selfound = false; + //Loop through all parts and set color on selected: + for (iCItem i = items.begin(); i != items.end(); i++) { + if (i->second->isSelected()) { + selfound = true; + i->second->part()->setColorIndex(curColorIndex); + } + } + + // If no items selected, use the one clicked on. + if(!selfound) + item->part()->setColorIndex(curColorIndex); + + redraw(); + break; + } + default: + printf("unknown action %d\n", n); + break; + } + delete pl; + } + +//--------------------------------------------------------- +// viewMousePressEvent +//--------------------------------------------------------- + +void PartCanvas::mousePress(QMouseEvent* event) + { + if (event->modifiers() & Qt::ShiftModifier) { + return; + } + QPoint pt = event->pos(); + CItem* item = items.find(pt); + if (item == 0) + return; + switch (_tool) { + default: + emit trackChanged(item->part()->track()); + break; + case CutTool: + splitItem(item, pt); + break; + case GlueTool: + glueItem(item); + break; + case MuteTool: + { + NPart* np = (NPart*) item; + Part* p = np->part(); + p->setMute(!p->mute()); + redraw(); + break; + } + } + } + +//--------------------------------------------------------- +// viewMouseReleaseEvent +//--------------------------------------------------------- + +void PartCanvas::mouseRelease(const QPoint&) + { + } + +//--------------------------------------------------------- +// viewMouseMoveEvent +//--------------------------------------------------------- + +void PartCanvas::mouseMove(const QPoint& pos) + { + int x = pos.x(); + if (x < 0) + x = 0; + emit timeChanged(AL::sigmap.raster(x, *_raster)); + } + +//--------------------------------------------------------- +// y2Track +//--------------------------------------------------------- + +Track* PartCanvas::y2Track(int y) const + { + TrackList* l = song->tracks(); + int ty = 0; + for (iTrack it = l->begin(); it != l->end(); ++it) { + int h = (*it)->height(); + if (y >= ty && y < ty + h) + return *it; + ty += h; + } + return 0; + } + +//--------------------------------------------------------- +// keyPress +//--------------------------------------------------------- + +void PartCanvas::keyPress(QKeyEvent* event) + { + int key = event->key(); + if (editMode) + { + if ( key == Qt::Key_Return || key == Qt::Key_Enter ) + { + returnPressed(); + return; + } + else if ( key == Qt::Key_Escape ) + { + lineEditor->hide(); + editMode = false; + return; + } + } + + if (event->modifiers() & Qt::ShiftModifier) + key += Qt::SHIFT; + if (event->modifiers() & Qt::AltModifier) + key += Qt::ALT; + if (event->modifiers() & Qt::ControlModifier) + key += Qt::CTRL; + + if (key == shortcuts[SHRT_DELETE].key) { + if (getCurrentDrag()) { + //printf("dragging!!\n"); + return; + } + + song->startUndo(); + song->msgRemoveParts(); + song->endUndo(SC_PART_REMOVED); + return; + } + else if (key == shortcuts[SHRT_POS_DEC].key) { + int spos = pos[0]; + if(spos > 0) + { + spos -= 1; // Nudge by -1, then snap down with raster1. + spos = AL::sigmap.raster1(spos, *_raster); + } + if(spos < 0) + spos = 0; + Pos p(spos,true); + song->setPos(0, p, true, true, true); + return; + } + else if (key == shortcuts[SHRT_POS_INC].key) { + int spos = AL::sigmap.raster2(pos[0] + 1, *_raster); // Nudge by +1, then snap up with raster2. + Pos p(spos,true); + song->setPos(0, p, true, true, true); + return; + } + else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { + int spos = pos[0] - AL::sigmap.rasterStep(pos[0], *_raster); + if(spos < 0) + spos = 0; + Pos p(spos,true); + song->setPos(0, p, true, true, true); + return; + } + else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { + Pos p(pos[0] + AL::sigmap.rasterStep(pos[0], *_raster), true); + song->setPos(0, p, true, true, true); + return; + } + else if (key == shortcuts[SHRT_TOOL_POINTER].key) { + emit setUsedTool(PointerTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { + emit setUsedTool(PencilTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { + emit setUsedTool(RubberTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_SCISSORS].key) { + emit setUsedTool(CutTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_GLUE].key) { + emit setUsedTool(GlueTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_MUTE].key) { + emit setUsedTool(MuteTool); + return; + } + else if (key == shortcuts[SHRT_SEL_TRACK_ABOVE].key) { + emit selectTrackAbove(); + return; + } + else if (key == shortcuts[SHRT_SEL_TRACK_BELOW].key) { + emit selectTrackBelow(); + return; + } + + // + // Shortcuts that require selected parts from here + // + if (!curItem) { + if (items.size()==0) { + event->ignore(); // give global accelerators a chance + return; + } + for (iCItem i = items.begin(); i != items.end(); ++i) { + NPart* part = (NPart*)(i->second); + if (part->isSelected()) { + curItem=part; + break; + } + } + if (!curItem) + curItem = (NPart*)items.begin()->second; // just grab the first part + } + + CItem* newItem = 0; + bool singleSelection = isSingleSelection(); + bool add = false; + //Locators to selection + if (key == shortcuts[SHRT_LOCATORS_TO_SELECTION].key) { + CItem *leftmost = 0, *rightmost = 0; + for (iCItem i = items.begin(); i != items.end(); i++) { + if (i->second->isSelected()) { + // Check leftmost: + if (!leftmost) + leftmost = i->second; + else + if (leftmost->x() > i->second->x()) + leftmost = i->second; + + // Check rightmost: + if (!rightmost) + rightmost = i->second; + else + if (rightmost->x() < i->second->x()) + rightmost = i->second; + } + } + + int left_tick = leftmost->part()->tick(); + int right_tick = rightmost->part()->tick() + rightmost->part()->lenTick(); + Pos p1(left_tick, true); + Pos p2(right_tick, true); + song->setPos(1, p1); + song->setPos(2, p2); + return; + } + + // Select part to the right + else if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) { + if (key == shortcuts[SHRT_SEL_RIGHT_ADD].key) + add = true; + + Part* part = curItem->part(); + Track* track = part->track(); + unsigned int tick = part->tick(); + bool afterthis = false; + for (iCItem i = items.begin(); i != items.end(); ++i) { + NPart* npart = (NPart*)(i->second); + Part* ipart = npart->part(); + if (ipart->track() != track) + continue; + if (ipart->tick() < tick) + continue; + if (ipart == part) + { + afterthis = true; + continue; + } + if(afterthis) + { + newItem = i->second; + break; + } + } + } + // Select part to the left + else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { + if (key == shortcuts[SHRT_SEL_LEFT_ADD].key) + add = true; + + Part* part = curItem->part(); + Track* track = part->track(); + unsigned int tick = part->tick(); + + for (iCItem i = items.begin(); i != items.end(); ++i) { + NPart* npart = (NPart*)(i->second); + Part* ipart = npart->part(); + + if (ipart->track() != track) + continue; + if (ipart->tick() > tick) + continue; + if (ipart == part) + break; + newItem = i->second; + } + } + + // Select nearest part on track above + else if (key == shortcuts[SHRT_SEL_ABOVE].key || key == shortcuts[SHRT_SEL_ABOVE_ADD].key) { + if (key == shortcuts[SHRT_SEL_ABOVE_ADD].key) + add = true; + //To get an idea of which track is above us: + int stepsize = rmapxDev(1); + Track* track = curItem->part()->track();//top->part()->track(); + track = y2Track(track->y() - 1); + + //If we're at topmost, leave + if (!track) { + printf("no track above!\n"); + return; + } + int middle = curItem->x() + curItem->part()->lenTick()/2; + CItem *aboveL = 0, *aboveR = 0; + //Upper limit: song end, lower limit: song start + int ulimit = song->len(); + int llimit = 0; + + while (newItem == 0) { + int y = track->y() + 2; + int xoffset = 0; + int xleft = middle - xoffset; + int xright = middle + xoffset; + while ((xleft > llimit || xright < ulimit) && (aboveL == 0) && (aboveR == 0)) { + xoffset += stepsize; + xleft = middle - xoffset; + xright = middle + xoffset; + if (xleft >= 0) + aboveL = items.find(QPoint(xleft,y)); + if (xright <= ulimit) + aboveR = items.find(QPoint(xright,y)); + } + + if ((aboveL || aboveR) != 0) { //We've hit something + CItem* above = 0; + above = (aboveL !=0) ? aboveL : aboveR; + newItem = above; + } + else { //We didn't hit anything. Move to track above, if there is one + track = y2Track(track->y() - 1); + if (track == 0) + return; + } + } + emit trackChanged(track); + } + // Select nearest part on track below + else if (key == shortcuts[SHRT_SEL_BELOW].key || key == shortcuts[SHRT_SEL_BELOW_ADD].key) { + if (key == shortcuts[SHRT_SEL_BELOW_ADD].key) + add = true; + + //To get an idea of which track is below us: + int stepsize = rmapxDev(1); + Track* track = curItem->part()->track();//bottom->part()->track(); + track = y2Track(track->y() + track->height() + 1 ); + int middle = curItem->x() + curItem->part()->lenTick()/2; + //If we're at bottommost, leave + if (!track) + return; + + CItem *belowL = 0, *belowR = 0; + //Upper limit: song end , lower limit: song start + int ulimit = song->len(); + int llimit = 0; + while (newItem == 0) { + int y = track->y() + 1; + int xoffset = 0; + int xleft = middle - xoffset; + int xright = middle + xoffset; + while ((xleft > llimit || xright < ulimit) && (belowL == 0) && (belowR == 0)) { + xoffset += stepsize; + xleft = middle - xoffset; + xright = middle + xoffset; + if (xleft >= 0) + belowL = items.find(QPoint(xleft,y)); + if (xright <= ulimit) + belowR = items.find(QPoint(xright,y)); + } + + if ((belowL || belowR) != 0) { //We've hit something + CItem* below = 0; + below = (belowL !=0) ? belowL : belowR; + newItem = below; + } + else { + //Get next track below, or abort if this is the lowest + track = y2Track(track->y() + track->height() + 1 ); + if (track == 0) + return; + } + } + emit trackChanged(track); + } + else if (key == shortcuts[SHRT_EDIT_PART].key && curItem) { //This should be the other way around - singleSelection first. + if (!singleSelection) { + event->ignore(); + return; + } + PartList* pl = new PartList; + NPart* npart = (NPart*)(curItem); + Track* track = npart->part()->track(); + pl->add(npart->part()); + int type = 0; + + // Check if track is wave or drum, + // else track is midi + + switch (track->type()) { + case Track::DRUM: + type = 3; + break; + + case Track::WAVE: + type = 4; + break; + + case Track::MIDI: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: //TODO + break; + } + emit startEditor(pl, type); + } + else { + event->ignore(); // give global accelerators a chance + return; + } + + + // Check if anything happened to the selected parts + if (newItem) { + //If this is a single selection, toggle previous item + if (singleSelection && !add) + selectItem(curItem, false); + else if(!add) + deselectAll(); + + curItem = newItem; + selectItem(newItem, true); + + //Check if we've hit the upper or lower boundaries of the window. If so, set a new position + if (newItem->x() < mapxDev(0)) { + int curpos = pos[0]; + setPos(0,newItem->x(),true); + setPos(0,curpos,false); //Dummy to put the current position back once we've scrolled + } + else if (newItem->x() > mapxDev(width())) { + int curpos = pos[0]; + setPos(0,newItem->x(),true); + setPos(0,curpos,false); //Dummy to put the current position back once we've scrolled + } + redraw(); + } + } + +//--------------------------------------------------------- +// drawPart +// draws a part +//--------------------------------------------------------- + +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(); + + //printf("PartCanvas::drawItem %s evRefs:%d pTick:%d pLen:%d\nbb.x:%d bb.y:%d bb.w:%d bb.h:%d\n" + // "rect.x:%d rect.y:%d rect.w:%d rect.h:%d\nr.x:%d r.y:%d r.w:%d r.h:%d\n", + // part->name().toLatin1().constData(), part->events()->arefCount(), pTick, part->lenTick(), + // bb.x(), bb.y(), bb.width(), bb.height(), + // rect.x(), rect.y(), rect.width(), rect.height(), + // r.x(), r.y(), r.width(), r.height()); + + int i = part->colorIndex(); + p.setPen(Qt::black); + if (part->mute()) { + QColor c(Qt::white); + c.setAlpha(config.globalAlphaBlend); + p.setBrush(c); + + // 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())); + //p.drawRect(r); + + return; + } + if (item->isMoving()) { + QColor c(Qt::gray); + c.setAlpha(config.globalAlphaBlend); + p.setBrush(c); + + // 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())); + //p.drawRect(r); + + } + //else if (part->mute()) + // return; + 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); + p.setBrush(c); + 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]); + c.setAlpha(config.globalAlphaBlend); + p.setBrush(c); + + p.drawRect(r); + } + + MidiPart* mp = 0; + WavePart* wp = 0; + Track::TrackType type = part->track()->type(); + if (type == Track::WAVE) { + wp =(WavePart*)part; + } + else { + mp = (MidiPart*)part; + } + + if (config.canvasShowPartType & 2) { // show events + if (mp) + { + // Do not allow this, causes segfault. + if(from <= to) + { + p.setPen(Qt::darkGray); + EventList* events = mp->events(); + iEvent ito(events->lower_bound(to)); + + for (iEvent i = events->lower_bound(from); i != ito; ++i) { + EventType type = i->second.type(); + if ( + ((config.canvasShowPartEvent & 1) && (type == Note)) + || ((config.canvasShowPartEvent & 2) && (type == PAfter)) + || ((config.canvasShowPartEvent & 4) && (type == Controller)) + || ((config.canvasShowPartEvent &16) && (type == CAfter)) + || ((config.canvasShowPartEvent &64) && (type == Sysex || type == Meta)) + ) { + int t = i->first + pTick; + int th = part->track()->height(); + if(t >= r.left() && t <= r.right()) + p.drawLine(t, r.y()+2, t, r.y()+th-4); + } + } + } + } + else if (wp) + drawWavePart(p, rect, wp, r); + } + + else { // show Cakewalk Style + if (mp) { + p.setPen(Qt::darkGray); + EventList* events = mp->events(); + iEvent ito(events->lower_bound(to)); + //printf("PartCanvas::drawItem pTick:%d from:%d to:%d part len:%d\n", pTick, from, to, part->lenTick()); + + for (iEvent i = events->begin(); i != ito; ++i) { + int t = i->first + pTick; + int te = t + i->second.lenTick(); + + if (t > (to + pTick)) + { + printf("PartCanvas::drawItem t:%d > to:%d + pTick:%d i->first:%d\n", t, to, pTick, i->first); + + break; + } + + if (te < (from + pTick)) + continue; + + if (te > (to + pTick)) + te = to + pTick; + + EventType type = i->second.type(); + if (type == Note) { + int pitch = i->second.pitch(); + int th = int(part->track()->height() * 0.75); // only draw on three quarters + int hoffset = (part->track()->height() - th ) / 2; // offset from bottom + int y = hoffset + (r.y() + th - (pitch * (th) / 127)); + p.drawLine(t, y, te, y); + } + } + } + else if (wp) + drawWavePart(p, rect, wp, r); + } + 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); + 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 */ + QRect rr = map(r); + rr.setX(rr.x() + 3); + p.save(); + p.setFont(config.fonts[1]); + p.setWorldMatrixEnabled(false); + p.drawText(rr, Qt::AlignVCenter|Qt::AlignLeft, part->name()); + p.restore(); + } + } + +//--------------------------------------------------------- +// drawMoving +// draws moving items +//--------------------------------------------------------- + +void PartCanvas::drawMoving(QPainter& p, const CItem* item, const QRect&) + { + //if(!item->isMoving()) + // return; + 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); + 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()); + } + +//--------------------------------------------------------- +// drawWavePart +// bb - bounding box of paint area +// pr - part rectangle +//--------------------------------------------------------- + +void PartCanvas::drawWavePart(QPainter& p, + const QRect& bb, WavePart* wp, const QRect& _pr) + { + //printf("PartCanvas::drawWavePart bb.x:%d bb.y:%d bb.w:%d bb.h:%d pr.x:%d pr.y:%d pr.w:%d pr.h:%d\n", + // bb.x(), bb.y(), bb.width(), bb.height(), _pr.x(), _pr.y(), _pr.width(), _pr.height()); + + QRect rr = p.worldMatrix().mapRect(bb); + QRect pr = p.worldMatrix().mapRect(_pr); + + p.save(); + p.resetTransform(); + + int x2 = 1; + int x1 = rr.x() > pr.x() ? rr.x() : pr.x(); + x2 += rr.right() < pr.right() ? rr.right() : pr.right(); + + if (x1 < 0) + x1 = 0; + if (x2 > width()) + x2 = width(); + int hh = pr.height(); + int h = hh/2; + int y = pr.y() + h; + + EventList* el = wp->events(); + for (iEvent e = el->begin(); e != el->end(); ++e) { + int cc = hh % 2 ? 0 : 1; + Event event = e->second; + SndFileR f = event.sndFile(); + if (f.isNull()) + continue; + unsigned channels = f.channels(); + if (channels == 0) { + printf("drawWavePart: channels==0! %s\n", f.name().toLatin1().constData()); + continue; + } + + int xScale; + int pos; + int tickstep = rmapxDev(1); + int postick = tempomap.frame2tick(wp->frame() + event.frame()); + int eventx = mapx(postick); + int drawoffset; + if((x1 - eventx) < 0) + drawoffset = 0; + else + drawoffset = rmapxDev(x1 - eventx); + postick += drawoffset; + pos = event.spos() + tempomap.tick2frame(postick) - wp->frame() - event.frame(); + + int i; + if(x1 < eventx) + i = eventx; + else + i = x1; + int ex = mapx(tempomap.frame2tick(wp->frame() + event.frame() + event.lenFrame())); + if(ex > x2) + ex = x2; + if (h < 20) { + // + // combine multi channels into one waveform + // + //printf("PartCanvas::drawWavePart i:%d ex:%d\n", i, ex); // REMOVE Tim. + + for (; i < ex; i++) { + SampleV sa[channels]; + xScale = tempomap.deltaTick2frame(postick, postick + tickstep); + f.read(sa, xScale, pos); + postick += tickstep; + pos += xScale; + int peak = 0; + int rms = 0; + for (unsigned k = 0; k < channels; ++k) { + if (sa[k].peak > peak) + peak = sa[k].peak; + rms += sa[k].rms; + } + rms /= channels; + peak = (peak * (hh-2)) >> 9; + rms = (rms * (hh-2)) >> 9; + p.setPen(QColor(Qt::darkGray)); + p.drawLine(i, y - peak - cc, i, y + peak); + p.setPen(QColor(Qt::black)); + p.drawLine(i, y - rms - cc, i, y + rms); + } + } + else { + // + // multi channel display + // + int hm = hh / (channels * 2); + int cc = hh % (channels * 2) ? 0 : 1; + for (; i < ex; i++) { + y = pr.y() + hm; + SampleV sa[channels]; + xScale = tempomap.deltaTick2frame(postick, postick + tickstep); + f.read(sa, xScale, pos); + postick += tickstep; + pos += xScale; + for (unsigned k = 0; k < channels; ++k) { + int peak = (sa[k].peak * (hm - 1)) >> 8; + int rms = (sa[k].rms * (hm - 1)) >> 8; + p.setPen(QColor(Qt::darkGray)); + p.drawLine(i, y - peak - cc, i, y + peak); + p.setPen(QColor(Qt::black)); + p.drawLine(i, y - rms - cc, i, y + rms); + + y += 2 * hm; + } + } + } + } + p.restore(); + } +//--------------------------------------------------------- +// cmd +//--------------------------------------------------------- + +void PartCanvas::cmd(int cmd) + { + PartList pl; + for (iCItem i = items.begin(); i != items.end(); ++i) { + if (!i->second->isSelected()) + continue; + NPart* npart = (NPart*)(i->second); + pl.add(npart->part()); + } + switch (cmd) { + case CMD_CUT_PART: + copy(&pl); + song->startUndo(); + + bool loop; + do + { + loop = false; + for (iCItem i = items.begin(); i != items.end(); ++i) { + if (!i->second->isSelected()) + continue; + NPart* p = (NPart*)(i->second); + Part* part = p->part(); + audio->msgRemovePart(part); + + loop = true; + break; + } + } while (loop); + song->endUndo(SC_PART_REMOVED); + break; + case CMD_COPY_PART: + copy(&pl); + break; + case CMD_PASTE_PART: + paste(false, false); + break; + case CMD_PASTE_CLONE_PART: + paste(true, false); + break; + case CMD_PASTE_PART_TO_TRACK: + paste(); + break; + case CMD_PASTE_CLONE_PART_TO_TRACK: + paste(true); + break; + case CMD_INSERT_PART: + paste(false, false, true); + break; + case CMD_INSERT_EMPTYMEAS: + song->startUndo(); + int startPos=song->vcpos(); + int oneMeas=AL::sigmap.ticksMeasure(startPos); + movePartsTotheRight(startPos,oneMeas); + song->endUndo(SC_PART_INSERTED); + break; + } + } + +//--------------------------------------------------------- +// copy +// cut copy paste +//--------------------------------------------------------- + +void PartCanvas::copy(PartList* pl) + { + //printf("void PartCanvas::copy(PartList* pl)\n"); + if (pl->empty()) + return; + // Changed by T356. Support mixed .mpt files. + //bool isWave = pl->begin()->second->track()->type() == Track::WAVE; + bool wave = false; + bool midi = false; + for(ciPart p = pl->begin(); p != pl->end(); ++p) + { + if(p->second->track()->isMidiTrack()) + midi = true; + else + if(p->second->track()->type() == Track::WAVE) + wave = true; + if(midi && wave) + break; + } + if(!(midi || wave)) + return; + + //--------------------------------------------------- + // write parts as XML into tmp file + //--------------------------------------------------- + + FILE* tmp = tmpfile(); + if (tmp == 0) { + fprintf(stderr, "PartCanvas::copy() fopen failed: %s\n", + strerror(errno)); + return; + } + Xml xml(tmp); + + // Clear the copy clone list. + cloneList.clear(); + //copyCloneList.clear(); + + int level = 0; + int tick = 0; + for (ciPart p = pl->begin(); p != pl->end(); ++p) { + // Indicate this is a copy operation. Also force full wave paths. + //p->second->write(level, xml); + p->second->write(level, xml, true, true); + + int endTick = p->second->endTick(); + if (endTick > tick) + tick = endTick; + } + Pos p(tick, true); + song->setPos(0, p); + + //--------------------------------------------------- + // read tmp file into QTextDrag Object + //--------------------------------------------------- + + fflush(tmp); + struct stat f_stat; + if (fstat(fileno(tmp), &f_stat) == -1) { + fprintf(stderr, "PartCanvas::copy() fstat failed:<%s>\n", + strerror(errno)); + fclose(tmp); + return; + } + int n = f_stat.st_size; + char* fbuf = (char*)mmap(0, n+1, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fileno(tmp), 0); + fbuf[n] = 0; + + QByteArray data(fbuf); + QMimeData* md = new QMimeData(); + + + if(midi && wave) + md->setData("text/x-muse-mixedpartlist", data); // By T356. Support mixed .mpt files. + else + if(midi) + md->setData("text/x-muse-midipartlist", data); + else + if(wave) + md->setData("text/x-muse-wavepartlist", data); + + QApplication::clipboard()->setMimeData(md, QClipboard::Clipboard); + + munmap(fbuf, n); + fclose(tmp); + } + +//--------------------------------------------------------- +// pasteAt +//--------------------------------------------------------- + +int PartCanvas::pasteAt(const QString& pt, Track* track, unsigned int pos, bool clone, bool toTrack) + { + //printf("int PartCanvas::pasteAt(const QString& pt, Track* track, int pos)\n"); + QByteArray ba = pt.toLatin1(); + const char* ptxt = ba.constData(); + Xml xml(ptxt); + bool firstPart=true; + int posOffset=0; + //int finalPos=0; + unsigned int finalPos = pos; + int notDone = 0; + int done = 0; + bool end = false; + + //song->startUndo(); + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + end = true; + break; + case Xml::TagStart: + if (tag == "part") { + /* + Part* p = 0; + if(clone) + { + if(!(p = readClone(xml, track, toTrack))) + break; + } + else + { + if (track->type() == Track::MIDI || track->type() == Track::DRUM) + p = new MidiPart((MidiTrack*)track); + else if (track->type() == Track::WAVE) + p = new WavePart((WaveTrack*)track); + else + break; + p->read(xml, 0, toTrack); + } + */ + + // Read the part. + Part* p = 0; + p = readXmlPart(xml, track, clone, toTrack); + // If it could not be created... + if(!p) + { + // Increment the number of parts not done and break. + ++notDone; + break; + } + + // Increment the number of parts done. + ++done; + + if (firstPart) { + firstPart=false; + posOffset=pos-p->tick(); + } + p->setTick(p->tick()+posOffset); + if (p->tick()+p->lenTick()>finalPos) { + finalPos=p->tick()+p->lenTick(); + } + //pos += p->lenTick(); + audio->msgAddPart(p,false); + } + else + xml.unknown("PartCanvas::pasteAt"); + break; + case Xml::TagEnd: + break; + default: + end = true; + break; + } + if(end) + break; + } + + //song->endUndo(SC_PART_INSERTED); + //return pos; + + if(notDone) + { + int tot = notDone + done; + QMessageBox::critical(this, QString("MusE"), + QString().setNum(notDone) + (tot > 1 ? (tr(" out of ") + QString().setNum(tot)) : QString("")) + + (tot > 1 ? tr(" parts") : tr(" part")) + + tr(" could not be pasted.\nLikely the selected track is the wrong type.")); + } + + return finalPos; + } + +/* +//--------------------------------------------------------- +// PartCanvas::readPart +//--------------------------------------------------------- + +Part* PartCanvas::readPart(Xml& xml, Track* track, bool doClone, bool toTrack) + { + int id = -1; + Part* npart = 0; + uuid_t uuid; + uuid_clear(uuid); + bool uuidvalid = false; + bool clone = true; + + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return npart; + case Xml::TagStart: + // If the part has not been created yet... + if(!npart) + { + // Attribute section did not create a clone from any matching part. Create a non-clone part now. + if(!track) + { + xml.skip("part"); + return 0; + } + if (track->type() == Track::MIDI || track->type() == Track::DRUM) + npart = new MidiPart((MidiTrack*)track); + else if (track->type() == Track::WAVE) + npart = new WavePart((WaveTrack*)track); + else + { + xml.skip("part"); + return 0; + } + + // Signify a new non-clone part was created. + // Even if the original part was itself a clone, clear this because the + // attribute section did not create a clone from any matching part. + clone = false; + + // If an id or uuid was found, add the part to the clone list + // so that subsequent parts can look it up and clone from it... + if(id != -1) + { + ClonePart ncp(npart, id); + cloneList.push_back(ncp); + } + else + if(uuidvalid) + { + ClonePart ncp(npart); + // New ClonePart creates its own uuid, but we need to replace it. + uuid_copy(ncp.uuid, uuid); + cloneList.push_back(ncp); + } + } + + if (tag == "name") + npart->setName(xml.parse1()); + else if (tag == "poslen") { + ((PosLen*)npart)->read(xml, "poslen"); + } + else if (tag == "pos") { + Pos pos; + pos.read(xml, "pos"); // obsolete + npart->setTick(pos.tick()); + } + else if (tag == "len") { + Pos len; + len.read(xml, "len"); // obsolete + npart->setLenTick(len.tick()); + } + else if (tag == "selected") + npart->setSelected(xml.parseInt()); + else if (tag == "color") + npart->setColorIndex(xml.parseInt()); + else if (tag == "mute") + npart->setMute(xml.parseInt()); + else if (tag == "event") + { + // If a new non-clone part was created, accept the events... + if(!clone) + { + EventType type = Wave; + if(track->isMidiTrack()) + type = Note; + Event e(type); + e.read(xml); + // stored tickpos for event has absolute value. However internally + // tickpos is relative to start of part, we substract tick(). + // TODO: better handling for wave event + e.move( -npart->tick() ); + int tick = e.tick(); + + // Do not discard events belonging to clone parts, + // at least not yet. A later clone might have a longer, + // fully accommodating part length! + //if ((tick < 0) || (tick >= (int) lenTick())) { + //if ((tick < 0) || ( id == -1 && !clone && (tick >= (int)lenTick()) )) + // No way to tell at the moment whether there will be clones referencing this... + // No choice but to accept all events past 0. + if(tick < 0) + { + //printf("readClone: warning: event not in part: %d - %d -%d, discarded\n", + printf("readClone: warning: event at tick:%d not in part:%s, discarded\n", + tick, npart->name().toLatin1().constData()); + } + else + { + npart->events()->add(e); + } + } + else + // ...Otherwise a clone was created, so we don't need the events. + xml.skip(tag); + } + else + xml.unknown("PartCanvas::readClone"); + break; + case Xml::Attribut: + if (tag == "cloneId") + { + id = xml.s2().toInt(); + if(id != -1) + { + for(iClone i = cloneList.begin(); i != cloneList.end(); ++i) + { + // Is a matching part found in the clone list? + if(i->id == id) + { + // If it's a regular paste (not paste clone), and the original part is + // not a clone, defer so that a new copy is created in TagStart above. + if(!doClone && i->cp->cevents()->arefCount() <= 1) + break; + + // This makes a clone, chains the part, and increases ref counts. + npart = track->newPart((Part*)i->cp, true); + break; + } + } + } + } + else if (tag == "uuid") + { + uuid_parse(xml.s2().toLatin1().constData(), uuid); + if(!uuid_is_null(uuid)) + { + uuidvalid = true; + for(iClone i = cloneList.begin(); i != cloneList.end(); ++i) + { + // Is a matching part found in the clone list? + if(uuid_compare(uuid, i->uuid) == 0) + { + Track* cpt = i->cp->track(); + // If we want to paste to the given track... + if(toTrack) + { + // If the given track type is not the same as the part's + // original track type, we can't continue. Just return. + if(!track || cpt->type() != track->type()) + { + xml.skip("part"); + return 0; + } + } + else + // ...else we want to paste to the part's original track. + { + // Make sure the track exists (has not been deleted). + if((cpt->isMidiTrack() && song->midis()->find(cpt) != song->midis()->end()) || + (cpt->type() == Track::WAVE && song->waves()->find(cpt) != song->waves()->end())) + track = cpt; + else + // Track was not found. Try pasting to the given track, as above... + { + if(!track || cpt->type() != track->type()) + { + // No luck. Just return. + xml.skip("part"); + return 0; + } + } + } + + // If it's a regular paste (not paste clone), and the original part is + // not a clone, defer so that a new copy is created in TagStart above. + if(!doClone && i->cp->cevents()->arefCount() <= 1) + break; + + // This makes a clone, chains the part, and increases ref counts. + npart = track->newPart((Part*)i->cp, true); + break; + } + } + } + } + //else if(tag == "isclone") // Ignore + // clone = xml.s2().toInt(); + break; + case Xml::TagEnd: + if (tag == "part") + return npart; + default: + break; + } + } + return npart; +} +*/ + +/* +//--------------------------------------------------------- +// PartCanvas::readClone +//--------------------------------------------------------- + +Part* PartCanvas::readClone(Xml& xml, Track* track, bool toTrack) + { + int id = -1; + Part* npart = 0; + uuid_t uuid; + uuid_clear(uuid); + bool uuidvalid = false; + bool clone = true; + + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return npart; + case Xml::TagStart: + // If the part has not been created yet... + if(!npart) + { + // Attribute section did not create a clone from any matching part. Create a non-clone part now. + if (track->type() == Track::MIDI || track->type() == Track::DRUM) + npart = new MidiPart((MidiTrack*)track); + else if (track->type() == Track::WAVE) + npart = new WavePart((WaveTrack*)track); + else + return 0; + + // Signify a new non-clone part was created. + // Even if the original part was itself a clone, clear this because the + // attribute section did not create a clone from any matching part. + clone = false; + + // If an id or uuid was found, add the part to the clone list + // so that subsequent parts can look it up and clone from it... + if(id != -1) + { + ClonePart ncp(npart, id); + cloneList.push_back(ncp); + } + else + if(uuidvalid) + { + ClonePart ncp(npart); + // New ClonePart creates its own uuid, but we need to replace it. + uuid_copy(ncp.uuid, uuid); + cloneList.push_back(ncp); + } + } + + if (tag == "name") + npart->setName(xml.parse1()); + else if (tag == "poslen") { + ((PosLen*)npart)->read(xml, "poslen"); + } + else if (tag == "pos") { + Pos pos; + pos.read(xml, "pos"); // obsolete + npart->setTick(pos.tick()); + } + else if (tag == "len") { + Pos len; + len.read(xml, "len"); // obsolete + npart->setLenTick(len.tick()); + } + else if (tag == "selected") + npart->setSelected(xml.parseInt()); + else if (tag == "color") + npart->setColorIndex(xml.parseInt()); + else if (tag == "mute") + npart->setMute(xml.parseInt()); + else if (tag == "event") + { + // If a new non-clone part was created, accept the events... + if(!clone) + { + EventType type = Wave; + if(track->isMidiTrack()) + type = Note; + Event e(type); + e.read(xml); + // stored tickpos for event has absolute value. However internally + // tickpos is relative to start of part, we substract tick(). + // TODO: better handling for wave event + e.move( -npart->tick() ); + int tick = e.tick(); + + // Do not discard events belonging to clone parts, + // at least not yet. A later clone might have a longer, + // fully accommodating part length! + //if ((tick < 0) || (tick >= (int) lenTick())) { + //if ((tick < 0) || ( id == -1 && !clone && (tick >= (int)lenTick()) )) + // No way to tell at the moment whether there will be clones referencing this... + // No choice but to accept all events past 0. + if(tick < 0) + { + //printf("readClone: warning: event not in part: %d - %d -%d, discarded\n", + printf("readClone: warning: event at tick:%d not in part:%s, discarded\n", + tick, npart->name().toLatin1().constData()); + } + else + { + npart->events()->add(e); + } + } + else + // ...Otherwise a clone was created, so we don't need the events. + xml.skip(tag); + } + else + xml.unknown("PartCanvas::readClone"); + break; + case Xml::Attribut: + if (tag == "cloneId") + { + id = xml.s2().toInt(); + if(id != -1) + { + for(iClone i = cloneList.begin(); i != cloneList.end(); ++i) + { + // Is a matching part found in the clone list? + if(i->id == id) + { + // This makes a clone, chains the part, and increases ref counts. + npart = track->newPart((Part*)i->cp, true); + break; + } + } + } + } + else if (tag == "uuid") + { + uuid_parse(xml.s2().toLatin1().constData(), uuid); + if(!uuid_is_null(uuid)) + { + uuidvalid = true; + for(iClone i = cloneList.begin(); i != cloneList.end(); ++i) + { + // Is a matching part found in the clone list? + if(uuid_compare(uuid, i->uuid) == 0) + { + // If we want to paste to the part's original track... + if(!toTrack) + { + // Make sure the track exists (has not been deleted). + if((i->cp->track()->isMidiTrack() && song->midis()->find(i->cp->track()) != song->midis()->end()) || + (i->cp->track()->type() == Track::WAVE && song->waves()->find(i->cp->track()) != song->waves()->end())) + track = i->cp->track(); + } + // This makes a clone, chains the part, and increases ref counts. + npart = track->newPart((Part*)i->cp, true); + break; + } + } + } + } + //else if(tag == "isclone") // Ignore + // clone = xml.s2().toInt(); + break; + case Xml::TagEnd: + if (tag == "part") + return npart; + default: + break; + } + } + return npart; +} +*/ + +//--------------------------------------------------------- +// paste +// paste part to current selected track at cpos +//--------------------------------------------------------- + +//void PartCanvas::paste() +void PartCanvas::paste(bool clone, bool toTrack, bool doInsert) +{ + Track* track = 0; + + if (doInsert) // logic depends on keeping track of newly selected tracks + deselectAll(); + + + // If we want to paste to a selected track... + if(toTrack) + { + TrackList* tl = song->tracks(); + for (iTrack i = tl->begin(); i != tl->end(); ++i) { + if ((*i)->selected()) { + if (track) { + QMessageBox::critical(this, QString("MusE"), + tr("Cannot paste: multiple tracks selected")); + return; + } + else + track = *i; + } + } + if (track == 0) { + QMessageBox::critical(this, QString("MusE"), + tr("Cannot paste: no track selected")); + return; + } + } + + QClipboard* cb = QApplication::clipboard(); + const QMimeData* md = cb->mimeData(QClipboard::Clipboard); + + QString pfx("text/"); + QString mdpl("x-muse-midipartlist"); + QString wvpl("x-muse-wavepartlist"); + QString mxpl("x-muse-mixedpartlist"); + QString txt; + + if(md->hasFormat(pfx + mdpl)) + { + // If we want to paste to a selected track... + if(toTrack && !track->isMidiTrack()) + { + QMessageBox::critical(this, QString("MusE"), + tr("Can only paste to midi/drum track")); + return; + } + txt = cb->text(mdpl, QClipboard::Clipboard); + } + else + if(md->hasFormat(pfx + wvpl)) + { + // If we want to paste to a selected track... + if(toTrack && track->type() != Track::WAVE) + { + QMessageBox::critical(this, QString("MusE"), + tr("Can only paste to wave track")); + return; + } + txt = cb->text(wvpl, QClipboard::Clipboard); + } + else + if(md->hasFormat(pfx + mxpl)) + { + // If we want to paste to a selected track... + if(toTrack && !track->isMidiTrack() && track->type() != Track::WAVE) + { + QMessageBox::critical(this, QString("MusE"), + tr("Can only paste to midi or wave track")); + return; + } + txt = cb->text(mxpl, QClipboard::Clipboard); + } + else + { + QMessageBox::critical(this, QString("MusE"), + tr("Cannot paste: wrong data type")); + return; + } + + int endPos=0; + unsigned int startPos=song->vcpos(); + if (!txt.isEmpty()) + { + song->startUndo(); + endPos=pasteAt(txt, track, startPos, clone, toTrack); + Pos p(endPos, true); + song->setPos(0, p); + if (!doInsert) + song->endUndo(SC_PART_INSERTED); + } + + if (doInsert) { + int offset = endPos-startPos; + movePartsTotheRight(startPos, offset); + song->endUndo(SC_PART_INSERTED); + } + } + +//--------------------------------------------------------- +// movePartsToTheRight +//--------------------------------------------------------- +void PartCanvas::movePartsTotheRight(unsigned int startTicks, int length) +{ + // all parts that start after the pasted parts will be moved the entire length of the pasted parts + for (iCItem i = items.begin(); i != items.end(); ++i) { + if (!i->second->isSelected()) { + Part* part = i->second->part(); + if (part->tick() >= startTicks) { + //void Audio::msgChangePart(Part* oldPart, Part* newPart, bool doUndoFlag, bool doCtrls, bool doClones) + Part *newPart = part->clone(); + newPart->setTick(newPart->tick()+length); + if (part->track()->type() == Track::WAVE) { + audio->msgChangePart((WavePart*)part,(WavePart*)newPart,false,false,false); + } else { + audio->msgChangePart(part,newPart,false,false,false); + } + + } + } + } + // perhaps ask if markers should be moved? + MarkerList *markerlist = song->marker(); + for(iMarker i = markerlist->begin(); i != markerlist->end(); ++i) + { + Marker* m = &i->second; + if (m->tick() >= startTicks) { + Marker *oldMarker = new Marker(); + *oldMarker = *m; + m->setTick(m->tick()+length); + song->undoOp(UndoOp::ModifyMarker,oldMarker, m); + } + } +} +//--------------------------------------------------------- +// startDrag +//--------------------------------------------------------- + +void PartCanvas::startDrag(CItem* item, DragType t) + { + //printf("PartCanvas::startDrag(CItem* item, DragType t)\n"); + NPart* p = (NPart*)(item); + Part* part = p->part(); + + //--------------------------------------------------- + // write part as XML into tmp file + //--------------------------------------------------- + + FILE* tmp = tmpfile(); + if (tmp == 0) { + fprintf(stderr, "PartCanvas::startDrag() fopen failed: %s\n", + strerror(errno)); + return; + } + Xml xml(tmp); + int level = 0; + part->write(level, xml); + + //--------------------------------------------------- + // read tmp file into QTextDrag Object + //--------------------------------------------------- + + fflush(tmp); + struct stat f_stat; + if (fstat(fileno(tmp), &f_stat) == -1) { + fprintf(stderr, "PartCanvas::startDrag fstat failed:<%s>\n", + strerror(errno)); + fclose(tmp); + return; + } + int n = f_stat.st_size + 1; + char* fbuf = (char*)mmap(0, n, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fileno(tmp), 0); + fbuf[n] = 0; + + QByteArray data(fbuf); + QMimeData* md = new QMimeData(); + + md->setData("text/x-muse-partlist", data); + + // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. + // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can + // clean up after the drag and drop operation has been completed. " + QDrag* drag = new QDrag(this); + drag->setMimeData(md); + + if (t == MOVE_COPY || t == MOVE_CLONE) + drag->exec(Qt::CopyAction); + else + drag->exec(Qt::MoveAction); + + munmap(fbuf, n); + fclose(tmp); + } + +//--------------------------------------------------------- +// dragEnterEvent +//--------------------------------------------------------- + +void PartCanvas::dragEnterEvent(QDragEnterEvent* event) + { + ///event->accept(Q3TextDrag::canDecode(event)); + event->acceptProposedAction(); // TODO CHECK Tim. + } + +//--------------------------------------------------------- +// dragMoveEvent +//--------------------------------------------------------- + +void PartCanvas::dragMoveEvent(QDragMoveEvent*) + { +// printf("drag move %x\n", this); + //event->acceptProposedAction(); + } + +//--------------------------------------------------------- +// dragLeaveEvent +//--------------------------------------------------------- + +void PartCanvas::dragLeaveEvent(QDragLeaveEvent*) + { +// printf("drag leave\n"); + //event->acceptProposedAction(); + } + +//--------------------------------------------------------- +// dropEvent +//--------------------------------------------------------- + +void PartCanvas::viewDropEvent(QDropEvent* event) + { + //printf("void PartCanvas::viewDropEvent(QDropEvent* event)\n"); + if (event->source() == this) { + printf("local DROP\n"); + //event->ignore(); // TODO CHECK Tim. + return; + } + int type = 0; // 0 = unknown, 1 = partlist, 2 = uri-list + QString text; + + if(event->mimeData()->hasFormat("text/partlist")) + type = 1; + else + //if(event->mimeData()->hasFormat("text/uri-list")) + if(event->mimeData()->hasUrls()) + type = 2; + else + { + if(debugMsg && event->mimeData()->formats().size() != 0) + printf("Drop with unknown format. First format:<%s>\n", event->mimeData()->formats()[0].toLatin1().constData()); + //event->ignore(); // TODO CHECK Tim. + return; + } + + // Make a backup of the current clone list, to retain any 'copy' items, + // so that pasting works properly after. + CloneList copyCloneList = cloneList; + // Clear the clone list to prevent any dangerous associations with + // current non-original parts. + cloneList.clear(); + + if (type == 1) + { + text = QString(event->mimeData()->data("text/partlist")); + + int x = AL::sigmap.raster(event->pos().x(), *_raster); + if (x < 0) + x = 0; + unsigned trackNo = y2pitch(event->pos().y()); + Track* track = 0; + if (trackNo < tracks->size()) + track = tracks->index(trackNo); + if (track) { + song->startUndo(); + pasteAt(text, track, x); + song->endUndo(SC_PART_INSERTED); + } + } + else if (type == 2) + { + // Multiple urls not supported here. Grab the first one. + text = event->mimeData()->urls()[0].path(); + + if (text.endsWith(".wav",Qt::CaseInsensitive) || + text.endsWith(".ogg",Qt::CaseInsensitive) || + text.endsWith(".mpt", Qt::CaseInsensitive) ) + { + int x = AL::sigmap.raster(event->pos().x(), *_raster); + if (x < 0) + x = 0; + unsigned trackNo = y2pitch(event->pos().y()); + Track* track = 0; + if (trackNo < tracks->size()) + track = tracks->index(trackNo); + if (track) + { + if (track->type() == Track::WAVE && + (text.endsWith(".wav", Qt::CaseInsensitive) || + (text.endsWith(".ogg", Qt::CaseInsensitive)))) + { + unsigned tick = x; + muse->importWaveToTrack(text, tick, track); + } + // Changed by T356. Support mixed .mpt files. + else if ((track->isMidiTrack() || track->type() == Track::WAVE) && text.endsWith(".mpt", Qt::CaseInsensitive)) + { + unsigned tick = x; + muse->importPartToTrack(text, tick, track); + } + } + } + else if(text.endsWith(".med",Qt::CaseInsensitive)) + { + emit dropSongFile(text); + } + else if(text.endsWith(".mid",Qt::CaseInsensitive)) + { + emit dropMidiFile(text); + } + else + { + printf("dropped... something... no hable...\n"); + } + } + + // Restore backup of the clone list, to retain any 'copy' items, + // so that pasting works properly after. + cloneList.clear(); + cloneList = copyCloneList; + } + +//--------------------------------------------------------- +// drawCanvas +//--------------------------------------------------------- + +void PartCanvas::drawCanvas(QPainter& p, const QRect& rect) +{ + int x = rect.x(); + int y = rect.y(); + int w = rect.width(); + int h = rect.height(); + + ////////// + // GRID // + ////////// + QColor baseColor(config.partCanvasBg.light(104)); + p.setPen(baseColor); + + //-------------------------------- + // vertical lines + //------------------------------- + //printf("raster=%d\n", *_raster); + if (config.canvasShowGrid) { + int bar, beat; + unsigned tick; + + AL::sigmap.tickValues(x, &bar, &beat, &tick); + for (;;) { + int xt = AL::sigmap.bar2tick(bar++, 0, 0); + if (xt >= x + w) + break; + if (!((bar-1) % 4)) + p.setPen(baseColor.dark(115)); + else + p.setPen(baseColor); + p.drawLine(xt, y, xt, y+h); + + // append + int noDivisors=0; + if (*_raster == config.division *2) // 1/2 + noDivisors=2; + else if (*_raster== config.division) // 1/4 + noDivisors=4; + else if (*_raster==config.division/2) // 1/8 + noDivisors=8; + else if (*_raster==config.division/4) // 1/16 + noDivisors=16; + else if (*_raster==config.division/8) // 1/16 + noDivisors=32; + else if (*_raster==config.division/16) // 1/16 + noDivisors=64; + + int r = *_raster; + int rr = rmapx(r); + if (*_raster > 1) { + while (rr < 4) { + r *= 2; + rr = rmapx(r); + noDivisors=noDivisors/2; + } + p.setPen(baseColor); + for (int t=1;t< noDivisors;t++) + p.drawLine(xt+r*t, y, xt+r*t, y+h); + } + } + } + //-------------------------------- + // horizontal lines + //-------------------------------- + + TrackList* tl = song->tracks(); + int yy = 0; + int th; + for (iTrack it = tl->begin(); it != tl->end(); ++it) { + if (yy > y + h) + break; + Track* track = *it; + th = track->height(); + ///if (/*config.canvasShowGrid ||*/ !track->isMidiTrack()) { + if (config.canvasShowGrid && (track->isMidiTrack() || track->type() == Track::WAVE)) // Tim. + { + //printf("PartCanvas::drawCanvas track name:%s, y:%d h:%d\n", track->name().toLatin1().constData(), yy, th); + p.setPen(baseColor.dark(130)); + ///p.drawLine(x, yy, x + w, yy); + p.drawLine(x, yy + th, x + w, yy + th); // Tim. + p.setPen(baseColor); + } + if (!track->isMidiTrack() && (track->type() != Track::WAVE)) { + QRect r = rect & QRect(x, yy, w, track->height()); + drawAudioTrack(p, r, (AudioTrack*)track); + p.setPen(baseColor); + } + if (!track->isMidiTrack()) { // draw automation + QRect r = rect & QRect(x, yy, w, track->height()); + drawAutomation(p, r, (AudioTrack*)track); + p.setPen(baseColor); + + } + yy += track->height(); + } +} + +//--------------------------------------------------------- +// drawAudioTrack +//--------------------------------------------------------- + +void PartCanvas::drawAudioTrack(QPainter& p, const QRect& r, AudioTrack* /* t */) +{ + // 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. + QPen pen(Qt::black, 0, Qt::SolidLine); + //p.setPen(QPen(Qt::black, 2, Qt::SolidLine)); + //pen.setCosmetic(true); + p.setPen(pen); + //p.setBrush(Qt::gray); + QColor c(Qt::gray); + c.setAlpha(config.globalAlphaBlend); + p.setBrush(c); + + // Factor in pen stroking size: + //QRect rr(r); + //rr.setHeight(rr.height() -1); + + p.drawRect(r); +} + +//--------------------------------------------------------- +// drawAutomation +//--------------------------------------------------------- + +void PartCanvas::drawAutomation(QPainter& p, const QRect& r, AudioTrack *t) +{ +// printf("drawAudioTrack %d x %d y %d w %d h %d\n",t, r.x(), r.y(), r.width(), r.height()); + //int v2=r.x()+r.width(); + //printf("v2=%d mapx=%d rmapx=%d mapxdev=%d rmapxdev=%d\n",v2, mapx(v2),rmapx(v2),mapxDev(v2),rmapxDev(v2)); + //return; + +// p.setPen(QPen(Qt::black, 2, Qt::SolidLine)); + int height=r.bottom()-r.top()-4; // limit height + + CtrlListList* cll = t->controller(); +// QColor cols[10]; +// cols[0]=Qt::white; +// cols[1]=Qt::red; +// cols[2]=Qt::yellow; +// cols[3]=Qt::black; +// cols[4]=Qt::blue; + //int colIndex=0; + bool firstRun=true; + for(CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) + { + //iCtrlList *icl = icll->second; + CtrlList *cl = icll->second; + if (cl->dontShow()) + continue; + double prevVal; + iCtrl ic=cl->begin(); + if (!cl->isVisible()) + continue; // skip this iteration if this controller isn't in the visible list + p.setPen(QPen(cl->color(),1,Qt::SolidLine)); + + // First check that there ARE automation, ic == cl->end means no automation + if (ic != cl->end()) { + CtrlVal cvFirst = ic->second; + ic++; + int prevPos=cvFirst.frame; + prevVal = cvFirst.val; + + // prepare prevVal + if (cl->id() == AC_VOLUME ) { // use db scale for volume + prevVal = (20.0*log10(cvFirst.val)+60) / 70.0; // represent volume between 0 and 1 + if (prevVal < 0) prevVal = 0.0; + } + else { + // we need to set curVal between 0 and 1 + double min, max; + cl->range(&min,&max); + prevVal = (prevVal- min)/(max-min); + } + + for (; ic !=cl->end(); ++ic) + { + CtrlVal cv = ic->second; + double nextVal = cv.val; // was curVal + if (cl->id() == AC_VOLUME ) { // use db scale for volume + nextVal = (20.0*log10(cv.val)+60) / 70.0; // represent volume between 0 and 1 + if (nextVal < 0) nextVal = 0.0; + } + else { + // we need to set curVal between 0 and 1 + double min, max; + cl->range(&min,&max); + nextVal = (nextVal- min)/(max-min); + } + int leftX=tempomap.frame2tick(prevPos); + if (firstRun && leftX>r.x()) { + leftX=r.x(); + } + + p.drawLine( leftX, + (r.bottom()-2)-prevVal*height, + tempomap.frame2tick(cv.frame), + (r.bottom()-2)-nextVal*height); + firstRun=false; + //printf("draw line: %d %f %d %f\n",tempomap.frame2tick(lastPos),r.bottom()-lastVal*height,tempomap.frame2tick(cv.frame),r.bottom()-curVal*height); + prevPos=cv.frame; + prevVal=nextVal; + } + //printf("outer draw %f\n", cvFirst.val ); + p.drawLine(tempomap.frame2tick(prevPos), + (r.bottom()-2)-prevVal*height, + r.x()+r.width(), + (r.bottom()-2)-prevVal*height); + //printf("draw last line: %d %f %d %f\n",tempomap.frame2tick(prevPos),(r.bottom()-2)-prevVal*height,tempomap.frame2tick(prevPos)+r.width(),(r.bottom()-2)-prevVal*height); + } + } +} + + +void PartCanvas::controllerChanged(Track* /* t */) +{ + redraw(); +} diff --git a/attic/muse2-oom/muse2/muse/arranger/pcanvas.h b/attic/muse2-oom/muse2/muse/arranger/pcanvas.h new file mode 100644 index 00000000..103b3d02 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/pcanvas.h @@ -0,0 +1,139 @@ +//========================================================= +// MusE +// 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) +//========================================================= + +#ifndef __PCANVAS_H__ +#define __PCANVAS_H__ + +#include "song.h" +#include "canvas.h" +#include "trackautomationview.h" + +class QDragMoveEvent; +class QDropEvent; +class QDragLeaveEvent; +class QMouseEvent; +class QKeyEvent; +class QEvent; +class QDragEnterEvent; + +#define beats 4 + +//--------------------------------------------------------- +// NPart +// ''visual'' Part +// wraps Parts with additional information needed +// for displaying +//--------------------------------------------------------- + +class NPart : public CItem { + public: + NPart(Part* e); + const QString name() const { return part()->name(); } + void setName(const QString& s) { part()->setName(s); } + Track* track() const { return part()->track(); } + }; + +class QLineEdit; +class MidiEditor; +class QMenu; +class Xml; + +//--------------------------------------------------------- +// PartCanvas +//--------------------------------------------------------- + +class PartCanvas : public Canvas { + int* _raster; + TrackList* tracks; + + Part* resizePart; + QLineEdit* lineEditor; + NPart* editPart; + int curColorIndex; + bool editMode; + + std::vector<TrackAutomationView*> automationViews; + Q_OBJECT + virtual void keyPress(QKeyEvent*); + virtual void mousePress(QMouseEvent*); + virtual void mouseMove(const QPoint&); + virtual void mouseRelease(const QPoint&); + virtual void viewMouseDoubleClickEvent(QMouseEvent*); + virtual void leaveEvent(QEvent*e); + virtual void drawItem(QPainter&, const CItem*, const QRect&); + virtual void drawMoving(QPainter&, const CItem*, const QRect&); + virtual void updateSelection(); + virtual QPoint raster(const QPoint&) const; + virtual int y2pitch(int y) const; + virtual int pitch2y(int p) const; + + virtual void moveCanvasItems(CItemList&, int, int, DragType, int*); + // Changed by T356. + //virtual bool moveItem(CItem*, const QPoint&, DragType, int*); + virtual bool moveItem(CItem*, const QPoint&, DragType); + virtual CItem* newItem(const QPoint&, int); + virtual void resizeItem(CItem*,bool); + virtual void newItem(CItem*,bool); + virtual bool deleteItem(CItem*); + virtual void startUndo(DragType); + + virtual void endUndo(DragType, int); + virtual void startDrag(CItem*, DragType); + virtual void dragEnterEvent(QDragEnterEvent*); + virtual void dragMoveEvent(QDragMoveEvent*); + virtual void dragLeaveEvent(QDragLeaveEvent*); + virtual void viewDropEvent(QDropEvent*); + + virtual QMenu* genItemPopup(CItem*); + virtual void itemPopup(CItem*, int, const QPoint&); + + void glueItem(CItem* item); + void splitItem(CItem* item, const QPoint&); + + void copy(PartList*); + void paste(bool clone = false, bool toTrack = true, bool doInsert=false); + int pasteAt(const QString&, Track*, unsigned int, bool clone = false, bool toTrack = true); + void movePartsTotheRight(unsigned int startTick, int length); + //Part* readClone(Xml&, Track*, bool toTrack = true); + void drawWavePart(QPainter&, const QRect&, WavePart*, const QRect&); + Track* y2Track(int) const; + void drawAudioTrack(QPainter& p, const QRect& r, AudioTrack* track); + void drawAutomation(QPainter& p, const QRect& r, AudioTrack* track); + + + protected: + virtual void drawCanvas(QPainter&, const QRect&); + + signals: + void timeChanged(unsigned); + void tracklistChanged(); + void dclickPart(Track*); + void selectionChanged(); + void dropSongFile(const QString&); + void dropMidiFile(const QString&); + void setUsedTool(int); + void trackChanged(Track*); + void selectTrackAbove(); + void selectTrackBelow(); + + void startEditor(PartList*, int); + + private slots: + void returnPressed(); + + public: + enum { CMD_CUT_PART, CMD_COPY_PART, CMD_PASTE_PART, CMD_PASTE_CLONE_PART, CMD_PASTE_PART_TO_TRACK, CMD_PASTE_CLONE_PART_TO_TRACK, + CMD_INSERT_PART, CMD_INSERT_EMPTYMEAS }; + + PartCanvas(int* raster, QWidget* parent, int, int); + void partsChanged(); + void cmd(int); + void controllerChanged(Track *t); + public slots: + void redirKeypress(QKeyEvent* e) { keyPress(e); } + }; +#endif diff --git a/attic/muse2-oom/muse2/muse/arranger/tlist.cpp b/attic/muse2-oom/muse2/muse/arranger/tlist.cpp new file mode 100644 index 00000000..02f742f7 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/tlist.cpp @@ -0,0 +1,1595 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: tlist.cpp,v 1.31.2.31 2009/12/15 03:39:58 terminator356 Exp $ +// (C) Copyright 1999 Werner Schweer (ws@seh.de) +//========================================================= + +//#include "config.h" + +#include <cmath> + +#include <QKeyEvent> +#include <QLineEdit> +#include <QMenu> +#include <QMessageBox> +#include <QMouseEvent> +#include <QPainter> +#include <QPaintEvent> +#include <QPixmap> +#include <QResizeEvent> +#include <QScrollBar> +#include <QWheelEvent> + +#include "popupmenu.h" +#include "globals.h" +#include "icons.h" +#include "scrollscale.h" +#include "tlist.h" +#include "xml.h" +#include "mididev.h" +#include "midiport.h" +#include "midiseq.h" +#include "comment.h" +#include "track.h" +#include "song.h" +#include "header.h" +#include "node.h" +#include "audio.h" +#include "instruments/minstrument.h" +#include "app.h" +#include "gconfig.h" +#include "event.h" +#include "midiedit/drummap.h" +#include "synth.h" +#include "config.h" + +#ifdef DSSI_SUPPORT +#include "dssihost.h" +#endif + +extern QMenu* populateAddSynth(QWidget* parent); + +static const int MIN_TRACKHEIGHT = 20; +static const int WHEEL_DELTA = 120; + +//--------------------------------------------------------- +// TList +//--------------------------------------------------------- + +TList::TList(Header* hdr, QWidget* parent, const char* name) + : QWidget(parent) // Qt::WNoAutoErase | Qt::WResizeNoErase are no longer needed according to Qt4 doc + { + setBackgroundRole(QPalette::NoRole); + setAttribute(Qt::WA_NoSystemBackground); + setAttribute(Qt::WA_StaticContents); + // This is absolutely required for speed! Otherwise painfully slow because we get + // full rect paint events even on small scrolls! See help on QPainter::scroll(). + setAttribute(Qt::WA_OpaquePaintEvent); + + setObjectName(name); + ypos = 0; + editMode = false; + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); + header = hdr; + + _scroll = 0; + editTrack = 0; + editor = 0; + mode = NORMAL; + + //setBackgroundMode(Qt::NoBackground); // ORCAN - FIXME + //setAttribute(Qt::WA_OpaquePaintEvent); + resizeFlag = false; + + connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int))); + connect(muse, SIGNAL(configChanged()), SLOT(redraw())); + } + +//--------------------------------------------------------- +// songChanged +//--------------------------------------------------------- + +void TList::songChanged(int flags) + { + if (flags & (SC_MUTE | SC_SOLO | SC_RECFLAG | SC_TRACK_INSERTED + | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_ROUTE | SC_CHANNELS | SC_MIDI_TRACK_PROP)) + redraw(); + if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED)) + adjustScrollbar(); + } + +//--------------------------------------------------------- +// drawCenteredPixmap +// small helper function for "draw()" below +//--------------------------------------------------------- + +static void drawCenteredPixmap(QPainter& p, const QPixmap* pm, const QRect& r) + { + p.drawPixmap(r.x() + (r.width() - pm->width())/2, r.y() + (r.height() - pm->height())/2, *pm); + } + +//--------------------------------------------------------- +// paintEvent +//--------------------------------------------------------- + +void TList::paintEvent(QPaintEvent* ev) + { + paint(ev->rect()); + } + +//--------------------------------------------------------- +// redraw +//--------------------------------------------------------- + +void TList::redraw() + { + update(); + } + +//--------------------------------------------------------- +// redraw +//--------------------------------------------------------- + +void TList::redraw(const QRect& r) + { + update(r); + } + +//--------------------------------------------------------- +// paint +//--------------------------------------------------------- + +void TList::paint(const QRect& r) + { + if (!isVisible()) + return; + QRect rect(r); + QPainter p(this); + + if (bgPixmap.isNull()) + p.fillRect(rect, config.trackBg); + else + p.drawTiledPixmap(rect, bgPixmap, QPoint(rect.x(), ypos + rect.y())); + p.setClipRegion(rect); + + //printf("TList::paint hasClipping:%d\n", p.hasClipping()); // Tested true. + + int y = rect.y(); + int w = rect.width(); + int h = rect.height(); + int x1 = rect.x(); + int x2 = rect.x() + w; + + //--------------------------------------------------- + // Tracks + //--------------------------------------------------- + + TrackList* l = song->tracks(); + int idx = 0; + int yy = -ypos; + for (iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i) { + Track* track = *i; + Track::TrackType type = track->type(); + int trackHeight = track->height(); + if (yy >= (y + h)) + break; + if ((yy + trackHeight) < y) + continue; + // + // clear one row + // + QColor bg; + if (track->selected()) { + bg = config.selectTrackBg; + //p.setPen(palette().active().text()); + p.setPen(config.selectTrackFg); + } + else { + switch(type) { + case Track::MIDI: + bg = config.midiTrackBg; + break; + case Track::DRUM: + bg = config.drumTrackBg; + break; + case Track::WAVE: + bg = config.waveTrackBg; + break; + case Track::AUDIO_OUTPUT: + bg = config.outputTrackBg; + break; + case Track::AUDIO_INPUT: + bg = config.inputTrackBg; + break; + case Track::AUDIO_GROUP: + bg = config.groupTrackBg; + break; + case Track::AUDIO_AUX: + bg = config.auxTrackBg; + break; + case Track::AUDIO_SOFTSYNTH: + bg = config.synthTrackBg; + break; + } + p.setPen(palette().color(QPalette::Active, QPalette::Text)); + } + p.fillRect(x1, yy, w, trackHeight, bg); + + int x = 0; + for (int index = 0; index < header->count(); ++index) { + int section = header->logicalIndex(index); + int w = header->sectionSize(section); + //QRect r = p.xForm(QRect(x+2, yy, w-4, trackHeight)); + QRect r = p.combinedTransform().mapRect(QRect(x+2, yy, w-4, trackHeight)); + + switch (section) { + case COL_RECORD: + if (track->canRecord()) { + drawCenteredPixmap(p, + track->recordFlag() ? record_on_Icon : record_off_Icon, r); + } + break; + case COL_CLASS: + { + const QPixmap* pm = 0; + switch(type) { + case Track::MIDI: + pm = addtrack_addmiditrackIcon; + break; + case Track::DRUM: + pm = addtrack_drumtrackIcon; + break; + case Track::WAVE: + pm = addtrack_wavetrackIcon; + break; + case Track::AUDIO_OUTPUT: + pm = addtrack_audiooutputIcon; + break; + case Track::AUDIO_INPUT: + pm = addtrack_audioinputIcon; + break; + case Track::AUDIO_GROUP: + pm = addtrack_audiogroupIcon; + break; + case Track::AUDIO_AUX: + pm = addtrack_auxsendIcon; + break; + case Track::AUDIO_SOFTSYNTH: + //pm = waveIcon; + pm = synthIcon; + break; + } + drawCenteredPixmap(p, pm, r); + } + break; + case COL_MUTE: + if (track->off()) + drawCenteredPixmap(p, offIcon, r); + else if (track->mute()) + drawCenteredPixmap(p, editmuteSIcon, r); + break; + case COL_SOLO: + if(track->solo() && track->internalSolo()) + drawCenteredPixmap(p, blacksqcheckIcon, r); + else + if(track->internalSolo()) + drawCenteredPixmap(p, blacksquareIcon, r); + else + if (track->solo()) + drawCenteredPixmap(p, bluedotIcon, r); + break; + case COL_TIMELOCK: + if (track->isMidiTrack() + && track->locked()) { + drawCenteredPixmap(p, lockIcon, r); + } + break; + case COL_NAME: + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, track->name()); + break; + case COL_OCHANNEL: + { + QString s; + int n; + if (track->isMidiTrack()) { + n = ((MidiTrack*)track)->outChannel() + 1; + } + else { + // show number of ports + n = ((WaveTrack*)track)->channels(); + } + s.setNum(n); + p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, s); + } + break; + case COL_OPORT: + { + QString s; + if (track->isMidiTrack()) { + int outport = ((MidiTrack*)track)->outPort(); + s.sprintf("%d:%s", outport+1, midiPorts[outport].portname().toLatin1().constData()); + } + // Added by Tim. p3.3.9 + + else + if(track->type() == Track::AUDIO_SOFTSYNTH) + { + MidiDevice* md = dynamic_cast<MidiDevice*>(track); + if(md) + { + int outport = md->midiPort(); + if((outport >= 0) && (outport < MIDI_PORTS)) + s.sprintf("%d:%s", outport+1, midiPorts[outport].portname().toLatin1().constData()); + else + s = tr("<none>"); + } + } + + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); + } + break; + case COL_AUTOMATION: + { + QString s="-"; + + if (!track->isMidiTrack()) { + int count = ((AudioTrack*)track)->controller()->size(); + s.sprintf("%d viewed", count); + } + + + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); + } + break; + default: + break; + } + x += header->sectionSize(section); + } + p.setPen(Qt::gray); + p.drawLine(x1, yy, x2, yy); + } + p.drawLine(x1, yy, x2, yy); + + if (mode == DRAG) { + int yy = curY - dragYoff; + p.setPen(Qt::green); + p.drawLine(x1, yy, x2, yy); + p.drawLine(x1, yy + dragHeight, x2, yy+dragHeight); + } + + //--------------------------------------------------- + // draw vertical lines + //--------------------------------------------------- + + int n = header->count(); + int xpos = 0; + p.setPen(Qt::gray); + for (int index = 0; index < n; index++) { + int section = header->logicalIndex(index); + xpos += header->sectionSize(section); + p.drawLine(xpos, 0, xpos, height()); + } + } + +//--------------------------------------------------------- +// returnPressed +//--------------------------------------------------------- + +void TList::returnPressed() + { + editor->hide(); + if (editor->text() != editTrack->name()) { + TrackList* tl = song->tracks(); + for (iTrack i = tl->begin(); i != tl->end(); ++i) { + if ((*i)->name() == editor->text()) { + QMessageBox::critical(this, + tr("MusE: bad trackname"), + tr("please choose a unique track name"), + QMessageBox::Ok, + Qt::NoButton, + Qt::NoButton); + editTrack = 0; + setFocus(); + return; + } + } + //Track* track = editTrack->clone(); + Track* track = editTrack->clone(false); + editTrack->setName(editor->text()); + audio->msgChangeTrack(track, editTrack); + } + editTrack = 0; + editMode = false; + setFocus(); + } + +//--------------------------------------------------------- +// adjustScrollbar +//--------------------------------------------------------- + +void TList::adjustScrollbar() + { + int h = 0; + TrackList* l = song->tracks(); + for (iTrack it = l->begin(); it != l->end(); ++it) + h += (*it)->height(); + _scroll->setMaximum(h +30); + redraw(); + } + +//--------------------------------------------------------- +// y2Track +//--------------------------------------------------------- + +Track* TList::y2Track(int y) const + { + TrackList* l = song->tracks(); + int ty = 0; + for (iTrack it = l->begin(); it != l->end(); ++it) { + int h = (*it)->height(); + if (y >= ty && y < ty + h) + return *it; + ty += h; + } + return 0; + } + +//--------------------------------------------------------- +// viewMouseDoubleClickEvent +//--------------------------------------------------------- + +void TList::mouseDoubleClickEvent(QMouseEvent* ev) + { + int x = ev->x(); + int section = header->logicalIndexAt(x); + if (section == -1) + return; + + Track* t = y2Track(ev->y() + ypos); + + if (t) { + int colx = header->sectionPosition(section); + int colw = header->sectionSize(section); + int coly = t->y() - ypos; + int colh = t->height(); + + if (section == COL_NAME) { + editTrack = t; + if (editor == 0) { + editor = new QLineEdit(this); + /*connect(editor, SIGNAL(returnPressed()), + SLOT(returnPressed()));*/ + editor->setFrame(true); + } + editor->setText(editTrack->name()); + editor->end(false); + editor->setGeometry(colx, coly, colw, colh); + editMode = true; + editor->show(); + } + else + mousePressEvent(ev); + } + } + +//--------------------------------------------------------- +// portsPopupMenu +//--------------------------------------------------------- + +void TList::portsPopupMenu(Track* t, int x, int y) + { + switch(t->type()) { + case Track::MIDI: + case Track::DRUM: + case Track::AUDIO_SOFTSYNTH: + { + MidiTrack* track = (MidiTrack*)t; + + //QPopupMenu* p = midiPortsPopup(0); + MidiDevice* md = 0; + int port = -1; + if(t->type() == Track::AUDIO_SOFTSYNTH) + { + //MidiDevice* md = dynamic_cast<MidiDevice*>((SynthI*)t); + md = dynamic_cast<MidiDevice*>(t); + if(md) + port = md->midiPort(); + } + else + port = track->outPort(); + + QMenu* p = midiPortsPopup(0, port); + QAction* act = p->exec(mapToGlobal(QPoint(x, y)), 0); + if (act) { + int n = act->data().toInt(); + // Changed by T356. + //track->setOutPort(n); + //audio->msgSetTrackOutPort(track, n); + + //song->update(); + if (t->type() == Track::DRUM) { + bool change = QMessageBox::question(this, tr("Update drummap?"), + tr("Do you want to use same port for all instruments in the drummap?"), + tr("&Yes"), tr("&No"), QString::null, 0, 1); + audio->msgIdle(true); + if (!change) + { + // Delete all port controller events. + //audio->msgChangeAllPortDrumCtrlEvents(false); + song->changeAllPortDrumCtrlEvents(false); + track->setOutPort(n); + + for (int i=0; i<DRUM_MAPSIZE; i++) //Remap all drum instruments to this port + drumMap[i].port = track->outPort(); + // Add all port controller events. + //audio->msgChangeAllPortDrumCtrlEvents(true); + song->changeAllPortDrumCtrlEvents(true); + } + else + { + //audio->msgSetTrackOutPort(track, n); + track->setOutPortAndUpdate(n); + } + audio->msgIdle(false); + song->update(); + } + else + if (t->type() == Track::AUDIO_SOFTSYNTH) + { + if(md != 0) + { + // Idling is already handled in msgSetMidiDevice. + //audio->msgIdle(true); + + // Compiler complains if simple cast from Track to SynthI... + midiSeq->msgSetMidiDevice(&midiPorts[n], (midiPorts[n].device() == md) ? 0 : md); + muse->changeConfig(true); // save configuration file + + //audio->msgIdle(false); + song->update(); + } + } + else + { + audio->msgIdle(true); + //audio->msgSetTrackOutPort(track, n); + track->setOutPortAndUpdate(n); + audio->msgIdle(false); + song->update(); + } + } + delete p; + } + break; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: //TODO + break; + } + } + +//--------------------------------------------------------- +// oportPropertyPopupMenu +//--------------------------------------------------------- + +void TList::oportPropertyPopupMenu(Track* t, int x, int y) + { + // Added by Tim. p3.3.9 + if(t->type() == Track::AUDIO_SOFTSYNTH) + { + SynthI* synth = (SynthI*)t; + + QMenu* p = new QMenu; + QAction* act = p->addAction(tr("Show Gui")); + act->setCheckable(true); + //printf("synth hasgui %d, gui visible %d\n",synth->hasGui(), synth->guiVisible()); + act->setEnabled(synth->hasGui()); + act->setChecked(synth->guiVisible()); + + // If it has a gui but we don't have OSC, disable the action. + #ifndef OSC_SUPPORT + #ifdef DSSI_SUPPORT + if(dynamic_cast<DssiSynthIF*>(synth->sif())) + { + act->setChecked(false); + act->setEnabled(false); + } + #endif + #endif + + QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), 0); + if (ract == act) { + bool show = !synth->guiVisible(); + audio->msgShowInstrumentGui(synth, show); + } + delete p; + return; + } + + + if (t->type() != Track::MIDI && t->type() != Track::DRUM) + return; + int oPort = ((MidiTrack*)t)->outPort(); + MidiPort* port = &midiPorts[oPort]; + + QMenu* p = new QMenu; + QAction* act = p->addAction(tr("Show Gui")); + act->setCheckable(true); + //printf("synth hasgui %d, gui visible %d\n",port->hasGui(), port->guiVisible()); + act->setEnabled(port->hasGui()); + act->setChecked(port->guiVisible()); + + // If it has a gui but we don't have OSC, disable the action. + #ifndef OSC_SUPPORT + #ifdef DSSI_SUPPORT + MidiDevice* dev = port->device(); + if(dev && dev->isSynti() && (dynamic_cast<DssiSynthIF*>(((SynthI*)dev)->sif()))) + { + act->setChecked(false); + act->setEnabled(false); + } + #endif + #endif + + QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), 0); + if (ract == act) { + bool show = !port->guiVisible(); + audio->msgShowInstrumentGui(port->instrument(), show); + } + delete p; + + } + +//--------------------------------------------------------- +// tracklistChanged +//--------------------------------------------------------- + +void TList::tracklistChanged() + { + redraw(); + } + +//--------------------------------------------------------- +// keyPressEvent +//--------------------------------------------------------- + +void TList::keyPressEvent(QKeyEvent* e) + { + if (editMode) + { + // First time we get a keypress event when lineedit is open is on the return key: + // -- Not true for Qt4. Modifier keys also send key events - Orcan + if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) + { + returnPressed(); + return; + } + else if ( e->key() == Qt::Key_Escape ) + { + editor->hide(); + editTrack = 0; + editMode = false; + setFocus(); + return; + } + } + emit keyPressExt(e); //redirect keypress events to main app + + // p4.0.10 Removed by Tim. keyPressExt are sent to part canvas, where they are + // ignored *only* if necessary. + //e->ignore(); + + /* + int key = e->key(); + switch (key) { + case Key_Up: + moveSelection(-1); + break; + case Key_Down: + moveSelection(1); + break; + default: + + break; + } + */ + } + +//--------------------------------------------------------- +// moveSelection +//--------------------------------------------------------- + +void TList::moveSelection(int n) + { + TrackList* tracks = song->tracks(); + + // check for single selection + int nselect = 0; + for (iTrack t = tracks->begin(); t != tracks->end(); ++t) + if ((*t)->selected()) + ++nselect; + if (nselect != 1) + return; + Track* selTrack = 0; + for (iTrack t = tracks->begin(); t != tracks->end(); ++t) { + iTrack s = t; + if ((*t)->selected()) { + selTrack = *t; + if (n > 0) { + while (n--) { + ++t; + if (t == tracks->end()) { + --t; + break; + } + } + } + else { + while (n++ != 0) { + if (t == tracks->begin()) + break; + --t; + } + } + (*s)->setSelected(false); + (*t)->setSelected(true); + + // rec enable track if expected + TrackList recd = getRecEnabledTracks(); + if (recd.size() == 1 && config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection + song->setRecordFlag((Track*)recd.front(),false); + song->setRecordFlag((*t),true); + } + + if (editTrack && editTrack != *t) + returnPressed(); + redraw(); + break; + } + } + ///emit selectionChanged(); + emit selectionChanged(selTrack); + } + +TrackList TList::getRecEnabledTracks() +{ + //printf("getRecEnabledTracks\n"); + TrackList recEnabled; + TrackList* tracks = song->tracks(); + for (iTrack t = tracks->begin(); t != tracks->end(); ++t) { + if ((*t)->recordFlag()) { + //printf("rec enabled track\n"); + recEnabled.push_back(*t); + } + } + return recEnabled; +} + +//--------------------------------------------------------- +// mousePressEvent +//--------------------------------------------------------- + +void TList::changeAutomation(QAction* act) +{ + printf("changeAutomation!\n"); + if (editTrack->type() == Track::MIDI) { + printf("this is wrong, we can't edit automation for midi tracks from arranger yet!\n"); + return; + } + + CtrlListList* cll = ((AudioTrack*)editTrack)->controller(); + int index=0; + for(CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { + if (act->data() == index++) { // got it, change state + CtrlList *cl = icll->second; + cl->setVisible(!cl->isVisible()); + } + } + song->update(SC_TRACK_MODIFIED); +} + +void TList::mousePressEvent(QMouseEvent* ev) + { + int x = ev->x(); + int y = ev->y(); + int button = ev->button(); + bool shift = ((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier; + + Track* t = y2Track(y + ypos); + + TrackColumn col = TrackColumn(header->logicalIndexAt(x)); + if (t == 0) { + if (button == Qt::RightButton) { + QMenu* p = new QMenu; + //p->clear(); + QAction* midi = p->addAction(*addtrack_addmiditrackIcon, + tr("Add Midi Track")); + midi->setData(Track::MIDI); + QAction* drum = p->addAction(*addtrack_drumtrackIcon, + tr("Add Drum Track")); + drum->setData(Track::DRUM); + QAction* wave = p->addAction(*addtrack_wavetrackIcon, + tr("Add Wave Track")); + wave->setData(Track::WAVE); + QAction* aoutput = p->addAction(*addtrack_audiooutputIcon, + tr("Add Output")); + aoutput->setData(Track::AUDIO_OUTPUT); + QAction* agroup = p->addAction(*addtrack_audiogroupIcon, + tr("Add Group")); + agroup->setData(Track::AUDIO_GROUP); + QAction* ainput = p->addAction(*addtrack_audioinputIcon, + tr("Add Input")); + ainput->setData(Track::AUDIO_INPUT); + QAction* aaux = p->addAction(*addtrack_auxsendIcon, + tr("Add Aux Send")); + aaux->setData(Track::AUDIO_AUX); + + // Create a sub-menu and fill it with found synth types. Make p the owner. + QMenu* synp = populateAddSynth(p); + synp->setIcon(*synthIcon); + synp->setTitle(QT_TRANSLATE_NOOP("@default", "Add Synth")); + + // Add the 'Add Synth' sub-menu to the menu. + p->addMenu(synp); + + // Show the menu + QAction* act = p->exec(ev->globalPos(), 0); + + // Valid click? + if(act) + { + int n = act->data().toInt(); + // Valid item? + if((n >= 0) && ((Track::TrackType)n != Track::AUDIO_SOFTSYNTH)) + { + // Synth sub-menu id? + if(n >= MENU_ADD_SYNTH_ID_BASE) + { + n -= MENU_ADD_SYNTH_ID_BASE; + //if(n < synthis.size()) + // t = song->createSynthI(synthis[n]->baseName()); + //if((n - MENU_ADD_SYNTH_ID_BASE) < (int)synthis.size()) + if(n < (int)synthis.size()) + { + //t = song->createSynthI(synp->text(n)); + //t = song->createSynthI(synthis[n]->name()); + t = song->createSynthI(synthis[n]->baseName(), synthis[n]->name()); + + if(t) + { + // Add instance last in midi device list. + for (int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* port = &midiPorts[i]; + MidiDevice* dev = port->device(); + if (dev==0) + { + midiSeq->msgSetMidiDevice(port, (SynthI*)t); + muse->changeConfig(true); // save configuration file + song->update(); + break; + } + } + } + } + } + // Normal track. + else + t = song->addTrack((Track::TrackType)n); + + if(t) + { + song->deselectTracks(); + t->setSelected(true); + + ///emit selectionChanged(); + emit selectionChanged(t); + adjustScrollbar(); + } + } + } + + // Just delete p, and all its children will go too, right? + //delete synp; + delete p; + } + return; + } + + TrackList* tracks = song->tracks(); + dragYoff = y - (t->y() - ypos); + startY = y; + + if (resizeFlag) { + mode = RESIZE; + int y = ev->y(); + int ty = -ypos; + sTrack = 0; + for (iTrack it = tracks->begin(); it != tracks->end(); ++it, ++sTrack) { + int h = (*it)->height(); + ty += h; + if (y >= (ty-2)) { + + if ( (*it) == tracks->back() && y > ty ) { + //printf("tracks->back() && y > ty\n"); + } + else if ( y > (ty+2) ) { + //printf(" y > (ty+2) \n"); + } + else { + //printf("ogga ogga\n"); + + break; + } + + + //&& y < (ty)) + // break; + } + } + + return; + } + mode = START_DRAG; + + switch (col) { + case COL_AUTOMATION: + { + if (t->type() != Track::MIDI) { + editTrack = t; + PopupMenu* p = new PopupMenu(); + p->disconnect(); + p->clear(); + p->setTitle(tr("Viewable automation")); + CtrlListList* cll = ((AudioTrack*)t)->controller(); + QAction* act = 0; + int index=0; + for(CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { + CtrlList *cl = icll->second; + if (cl->dontShow()) + continue; + act = p->addAction(cl->name()); + act->setCheckable(true); + act->setChecked(cl->isVisible()); + act->setData(index++); + } + connect(p, SIGNAL(triggered(QAction*)), SLOT(changeAutomation(QAction*))); + //connect(p, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); + //p->popup(QCursor::pos()); + p->exec(QCursor::pos()); + + delete p; + } + break; + } + + case COL_RECORD: + { + bool val = !(t->recordFlag()); + if (button == Qt::LeftButton) { + if (!t->isMidiTrack()) { + if (t->type() == Track::AUDIO_OUTPUT) { + if (val && t->recordFlag() == false) { + muse->bounceToFile((AudioOutput*)t); + } + audio->msgSetRecord((AudioOutput*)t, val); + if (!((AudioOutput*)t)->recFile()) + val = false; + else + return; + } + song->setRecordFlag(t, val); + } + else + song->setRecordFlag(t, val); + } else if (button == Qt::RightButton) { + // enable or disable ALL tracks of this type + if (!t->isMidiTrack()) { + if (t->type() == Track::AUDIO_OUTPUT) { + return; + } + WaveTrackList* wtl = song->waves(); + foreach (WaveTrack *wt, *wtl) { + song->setRecordFlag(wt, val); + } + } + else { + MidiTrackList* mtl = song->midis(); + foreach (MidiTrack *mt, *mtl) { + song->setRecordFlag(mt, val); + } + } + } + } + break; + case COL_NONE: + break; + case COL_CLASS: + if (t->isMidiTrack()) + classesPopupMenu(t, x, t->y() - ypos); + break; + case COL_OPORT: + // Changed by Tim. p3.3.9 + // Reverted. + if (button == Qt::LeftButton) + portsPopupMenu(t, x, t->y() - ypos); + else if (button == Qt::RightButton) + oportPropertyPopupMenu(t, x, t->y() - ypos); + //if(((button == QMouseEvent::LeftButton) && (t->type() == Track::AUDIO_SOFTSYNTH)) || (button == QMouseEvent::RightButton)) + // oportPropertyPopupMenu(t, x, t->y() - ypos); + //else + //if(button == QMouseEvent::LeftButton) + // portsPopupMenu(t, x, t->y() - ypos); + + break; + case COL_MUTE: + // p3.3.29 + if ((button == Qt::RightButton) || (((QInputEvent*)ev)->modifiers() & Qt::ControlModifier)) + t->setOff(!t->off()); + else + { + if (t->off()) + t->setOff(false); + else + t->setMute(!t->mute()); + } + song->update(SC_MUTE); + break; + case COL_SOLO: + audio->msgSetSolo(t, !t->solo()); + song->update(SC_SOLO); + break; + + case COL_NAME: + if (button == Qt::LeftButton) { + if (!shift) { + song->deselectTracks(); + t->setSelected(true); + + // rec enable track if expected + TrackList recd = getRecEnabledTracks(); + if (recd.size() == 1 && config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection + song->setRecordFlag((Track*)recd.front(),false); + song->setRecordFlag(t,true); + } + } + else + t->setSelected(!t->selected()); + if (editTrack && editTrack != t) + returnPressed(); + ///emit selectionChanged(); + emit selectionChanged(t->selected() ? t : 0); + } + else if (button == Qt::RightButton) { + mode = NORMAL; + QMenu* p = new QMenu; + //p->clear(); + p->addAction(QIcon(*automation_clear_dataIcon), tr("Delete Track"))->setData(0); + p->addAction(QIcon(*track_commentIcon), tr("Track Comment"))->setData(1); + QAction* act = p->exec(ev->globalPos(), 0); + if (act) { + int n = act->data().toInt(); + switch (n) { + case 0: // delete track + song->removeTrack0(t); + audio->msgUpdateSoloStates(); + break; + + case 1: // show track comment + { + TrackComment* tc = new TrackComment(t, 0); + tc->show(); + //QToolTip::add( this, "FOOOOOOOOOOOOO" ); + } + break; + + default: + printf("action %d\n", n); + break; + } + + } + delete p; + } + break; + + case COL_TIMELOCK: + t->setLocked(!t->locked()); + break; + + case COL_OCHANNEL: + { + int delta = 0; + if (button == Qt::RightButton) + delta = 1; + else if (button == Qt::MidButton) + delta = -1; + if (t->isMidiTrack()) + { + MidiTrack* mt = dynamic_cast<MidiTrack*>(t); + if (mt == 0) + break; + + int channel = mt->outChannel(); + channel += delta; + if(channel >= MIDI_CHANNELS) + channel = MIDI_CHANNELS - 1; + if(channel < 0) + channel = 0; + //if (channel != ((MidiTrack*)t)->outChannel()) + if (channel != mt->outChannel()) + { + // Changed by T356. + //mt->setOutChannel(channel); + audio->msgIdle(true); + //audio->msgSetTrackOutChannel(mt, channel); + mt->setOutChanAndUpdate(channel); + audio->msgIdle(false); + + /* --- I really don't like this, you can mess up the whole map "as easy as dell" + if (mt->type() == MidiTrack::DRUM) {//Change channel on all drum instruments + for (int i=0; i<DRUM_MAPSIZE; i++) + drumMap[i].channel = channel; + }*/ + + // may result in adding/removing mixer strip: + //song->update(-1); + //song->update(SC_CHANNELS); + song->update(SC_MIDI_TRACK_PROP); + } + } + else + { + if(t->type() != Track::AUDIO_SOFTSYNTH) + { + AudioTrack* at = dynamic_cast<AudioTrack*>(t); + if (at == 0) + break; + + int n = t->channels() + delta; + if (n > MAX_CHANNELS) + n = MAX_CHANNELS; + else if (n < 1) + n = 1; + if (n != t->channels()) { + audio->msgSetChannels(at, n); + song->update(SC_CHANNELS); + } + } + } + } + break; + } + redraw(); + } + +//--------------------------------------------------------- +// selectTrack +//--------------------------------------------------------- +void TList::selectTrack(Track* tr) + { + song->deselectTracks(); + tr->setSelected(true); + + + // rec enable track if expected + TrackList recd = getRecEnabledTracks(); + if (recd.size() == 1 && config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection + song->setRecordFlag((Track*)recd.front(),false); + song->setRecordFlag(tr,true); + } + + // By T356. Force a redraw for wave tracks, since it does not seem to happen. + //if(!tr->isMidiTrack()) + redraw(); + ///emit selectionChanged(); + emit selectionChanged(tr); + } + +//--------------------------------------------------------- +// selectTrackAbove +//--------------------------------------------------------- +void TList::selectTrackAbove() +{ + moveSelection(-1); +} +//--------------------------------------------------------- +// selectTrackBelow +//--------------------------------------------------------- +void TList::selectTrackBelow() +{ + moveSelection(1); +} + +//--------------------------------------------------------- +// mouseMoveEvent +//--------------------------------------------------------- + +void TList::mouseMoveEvent(QMouseEvent* ev) + { + if ((((QInputEvent*)ev)->modifiers() | ev->buttons()) == 0) { + int y = ev->y(); + int ty = -ypos; + TrackList* tracks = song->tracks(); + iTrack it; + for (it = tracks->begin(); it != tracks->end(); ++it) { + int h = (*it)->height(); + ty += h; + if (y >= (ty-2)) { + if ( (*it) == tracks->back() && y >= ty ) { + // outside last track don't change to splitVCursor + } + else if ( y > (ty+2) ) { + //printf(" y > (ty+2) \n"); + } + else { + if (!resizeFlag) { + resizeFlag = true; + setCursor(QCursor(Qt::SplitVCursor)); + } + break; + } + } + } + if (it == tracks->end() && resizeFlag) { + setCursor(QCursor(Qt::ArrowCursor)); + resizeFlag = false; + } + return; + } + curY = ev->y(); + int delta = curY - startY; + switch (mode) { + case START_DRAG: + if (delta < 0) + delta = -delta; + if (delta <= 2) + break; + { + Track* t = y2Track(startY + ypos); + if (t == 0) + mode = NORMAL; + else { + mode = DRAG; + dragHeight = t->height(); + sTrack = song->tracks()->index(t); + setCursor(QCursor(Qt::SizeVerCursor)); + redraw(); + } + } + break; + case NORMAL: + break; + case DRAG: + redraw(); + break; + case RESIZE: + { + if(sTrack >= 0 && (unsigned) sTrack < song->tracks()->size()) + { + Track* t = song->tracks()->index(sTrack); + if(t) + { + int h = t->height() + delta; + startY = curY; + if (h < MIN_TRACKHEIGHT) + h = MIN_TRACKHEIGHT; + t->setHeight(h); + song->update(SC_TRACK_MODIFIED); + } + } + } + break; + } + } + +//--------------------------------------------------------- +// mouseReleaseEvent +//--------------------------------------------------------- + +void TList::mouseReleaseEvent(QMouseEvent* ev) + { + if (mode == DRAG) { + Track* t = y2Track(ev->y() + ypos); + if (t) { + int dTrack = song->tracks()->index(t); + audio->msgMoveTrack(sTrack, dTrack); + } + } + if (mode != NORMAL) { + mode = NORMAL; + setCursor(QCursor(Qt::ArrowCursor)); + redraw(); + } + if (editTrack && editor && editor->isVisible()) + editor->setFocus(); + adjustScrollbar(); + } + +//--------------------------------------------------------- +// wheelEvent +//--------------------------------------------------------- + +void TList::wheelEvent(QWheelEvent* ev) + { + int x = ev->x(); + int y = ev->y(); + Track* t = y2Track(y + ypos); + if (t == 0) { + emit redirectWheelEvent(ev); + return; + } + TrackColumn col = TrackColumn(header->logicalIndexAt(x)); + int delta = ev->delta() / WHEEL_DELTA; + ev->accept(); + + switch (col) { + case COL_RECORD: + case COL_NONE: + case COL_CLASS: + case COL_NAME: + case COL_AUTOMATION: + break; + case COL_MUTE: + // p3.3.29 + if (((QInputEvent*)ev)->modifiers() & Qt::ControlModifier) + t->setOff(!t->off()); + else + { + if (t->off()) + t->setOff(false); + else + t->setMute(!t->mute()); + } + song->update(SC_MUTE); + break; + + case COL_SOLO: + audio->msgSetSolo(t, !t->solo()); + song->update(SC_SOLO); + break; + + case COL_TIMELOCK: + t->setLocked(!t->locked()); + break; + + case COL_OPORT: + if (t->isMidiTrack()) { + MidiTrack* mt = (MidiTrack*)t; + int port = mt->outPort() + delta; + + if (port >= MIDI_PORTS) + port = MIDI_PORTS-1; + else if (port < 0) + port = 0; + if (port != ((MidiTrack*)t)->outPort()) { + // Changed by T356. + //mt->setOutPort(port); + audio->msgIdle(true); + //audio->msgSetTrackOutPort(mt, port); + mt->setOutPortAndUpdate(port); + audio->msgIdle(false); + + song->update(SC_ROUTE); + } + } + break; + + case COL_OCHANNEL: + if (t->isMidiTrack()) { + MidiTrack* mt = (MidiTrack*)t; + int channel = mt->outChannel() + delta; + + if (channel >= MIDI_CHANNELS) + channel = MIDI_CHANNELS-1; + else if (channel < 0) + channel = 0; + if (channel != ((MidiTrack*)t)->outChannel()) { + // Changed by T356. + //mt->setOutChannel(channel); + audio->msgIdle(true); + //audio->msgSetTrackOutChannel(mt, channel); + mt->setOutChanAndUpdate(channel); + audio->msgIdle(false); + + // may result in adding/removing mixer strip: + //song->update(-1); + song->update(SC_MIDI_TRACK_PROP); + } + } + else { + int n = t->channels() + delta; + if (n > MAX_CHANNELS) + n = MAX_CHANNELS; + else if (n < 1) + n = 1; + if (n != t->channels()) { + audio->msgSetChannels((AudioTrack*)t, n); + song->update(SC_CHANNELS); + } + } + break; + default: + break; + } + } + +//--------------------------------------------------------- +// writeStatus +//--------------------------------------------------------- + +void TList::writeStatus(int level, Xml& xml, const char* name) const + { + xml.tag(level++, name); + header->writeStatus(level, xml); + xml.etag(level, name); + } + +//--------------------------------------------------------- +// readStatus +//--------------------------------------------------------- + +void TList::readStatus(Xml& xml, const char* name) + { + for (;;) { + Xml::Token token(xml.parse()); + const QString& tag(xml.s1()); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::TagStart: + if (tag == header->objectName()) + header->readStatus(xml); + else + xml.unknown("Tlist"); + break; + case Xml::TagEnd: + if (tag == name) + return; + default: + break; + } + } + } + +//--------------------------------------------------------- +// setYPos +//--------------------------------------------------------- + +void TList::setYPos(int y) + { + int delta = ypos - y; // - -> shift up + ypos = y; + + scroll(0, delta); + } + +//--------------------------------------------------------- +// resizeEvent +//--------------------------------------------------------- + +void TList::resizeEvent(QResizeEvent* /*ev*/) + { + + } + +//--------------------------------------------------------- +// classesPopupMenu +//--------------------------------------------------------- + +void TList::classesPopupMenu(Track* t, int x, int y) + { + QMenu p; + p.clear(); + p.addAction(QIcon(*addtrack_addmiditrackIcon), tr("Midi"))->setData(Track::MIDI); + p.addAction(QIcon(*addtrack_drumtrackIcon), tr("Drum"))->setData(Track::DRUM); + QAction* act = p.exec(mapToGlobal(QPoint(x, y)), 0); + + if (!act) + return; + + int n = act->data().toInt(); + if (Track::TrackType(n) == Track::MIDI && t->type() == Track::DRUM) { + // + // Drum -> Midi + // + audio->msgIdle(true); + PartList* pl = t->parts(); + MidiTrack* m = (MidiTrack*) t; + for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { + EventList* el = ip->second->events(); + for (iEvent ie = el->begin(); ie != el->end(); ++ie) { + Event ev = ie->second; + if(ev.type() == Note) + { + int pitch = ev.pitch(); + // Changed by T356. + // Tested: Notes were being mixed up switching back and forth between midi and drum. + //pitch = drumMap[pitch].anote; + pitch = drumMap[pitch].enote; + + ev.setPitch(pitch); + } + else + if(ev.type() == Controller) + { + int ctl = ev.dataA(); + // Is it a drum controller event, according to the track port's instrument? + MidiController *mc = midiPorts[m->outPort()].drumController(ctl); + if(mc) + // Change the controller event's index into the drum map to an instrument note. + ev.setA((ctl & ~0xff) | drumMap[ctl & 0x7f].enote); + } + + } + } + t->setType(Track::MIDI); + audio->msgIdle(false); + } + else if (Track::TrackType(n) == Track::DRUM && t->type() == Track::MIDI) { + // + // Midi -> Drum + // + bool change = QMessageBox::question(this, tr("Update drummap?"), + tr("Do you want to use same port and channel for all instruments in the drummap?"), + tr("&Yes"), tr("&No"), QString::null, 0, 1); + + audio->msgIdle(true); + // Delete all port controller events. + //audio->msgChangeAllPortDrumCtrlEvents(false); + song->changeAllPortDrumCtrlEvents(false); + + if (!change) { + MidiTrack* m = (MidiTrack*) t; + for (int i=0; i<DRUM_MAPSIZE; i++) { + drumMap[i].channel = m->outChannel(); + drumMap[i].port = m->outPort(); + } + } + + //audio->msgIdle(true); + PartList* pl = t->parts(); + MidiTrack* m = (MidiTrack*) t; + for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { + EventList* el = ip->second->events(); + for (iEvent ie = el->begin(); ie != el->end(); ++ie) { + Event ev = ie->second; + if (ev.type() == Note) + { + int pitch = ev.pitch(); + pitch = drumInmap[pitch]; + ev.setPitch(pitch); + } + else + { + if(ev.type() == Controller) + { + int ctl = ev.dataA(); + // Is it a drum controller event, according to the track port's instrument? + MidiController *mc = midiPorts[m->outPort()].drumController(ctl); + if(mc) + // Change the controller event's instrument note to an index into the drum map. + ev.setA((ctl & ~0xff) | drumInmap[ctl & 0x7f]); + } + + } + + } + } + t->setType(Track::DRUM); + + // Add all port controller events. + //audio->msgChangeAllPortDrumCtrlEvents(true); + song->changeAllPortDrumCtrlEvents(true); + + audio->msgIdle(false); + } + } + diff --git a/attic/muse2-oom/muse2/muse/arranger/tlist.h b/attic/muse2-oom/muse2/muse/arranger/tlist.h new file mode 100644 index 00000000..188685bc --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/tlist.h @@ -0,0 +1,115 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: tlist.h,v 1.8.2.5 2008/01/19 13:33:46 wschweer Exp $ +// (C) Copyright 1999 Werner Schweer (ws@seh.de) +//========================================================= + +#ifndef __TLIST_H__ +#define __TLIST_H__ + +#include "track.h" + +#include <QWidget> + +class QKeyEvent; +class QLineEdit; +class QMouseEvent; +class QPaintEvent; +class QResizeEvent; +class QScrollBar; +class QWheelEvent; + +class ScrollScale; +class Track; +class Xml; +class Header; + +enum TrackColumn { + COL_RECORD = 0, + COL_MUTE, + COL_SOLO, + COL_CLASS, + COL_NAME, + COL_OPORT, + COL_OCHANNEL, + COL_TIMELOCK, + COL_AUTOMATION, + COL_NONE = -1 + }; + +//--------------------------------------------------------- +// TList +//--------------------------------------------------------- + +class TList : public QWidget { + Q_OBJECT + + int ypos; + bool editMode; + + QPixmap bgPixmap; // background Pixmap + bool resizeFlag; // true if resize cursor is shown + + Header* header; + QScrollBar* _scroll; + QLineEdit* editor; + Track* editTrack; + + int startY; + int curY; + int sTrack; + int dragHeight; + int dragYoff; + + enum { NORMAL, START_DRAG, DRAG, RESIZE} mode; + + virtual void paintEvent(QPaintEvent*); + virtual void mousePressEvent(QMouseEvent* event); + virtual void mouseDoubleClickEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void keyPressEvent(QKeyEvent* e); + virtual void wheelEvent(QWheelEvent* e); + + void portsPopupMenu(Track*, int, int); + void oportPropertyPopupMenu(Track*, int x, int y); + void moveSelection(int n); + void adjustScrollbar(); + void paint(const QRect& r); + virtual void resizeEvent(QResizeEvent*); + void redraw(const QRect& r); + Track* y2Track(int) const; + void classesPopupMenu(Track*, int x, int y); + TrackList getRecEnabledTracks(); + void setHeaderToolTips(); + + private slots: + void returnPressed(); + void songChanged(int flags); + void changeAutomation(QAction*); + + signals: + ///void selectionChanged(); + void selectionChanged(Track*); + void keyPressExt(QKeyEvent*); + void redirectWheelEvent(QWheelEvent*); + + public slots: + void tracklistChanged(); + void setYPos(int); + void redraw(); + void selectTrack(Track*); + void selectTrackAbove(); + void selectTrackBelow(); + + public: + TList(Header*, QWidget* parent, const char* name); + void setScroll(QScrollBar* s) { _scroll = s; } + Track* track() const { return editTrack; } + void writeStatus(int level, Xml&, const char* name) const; + void readStatus(Xml&, const char* name); + }; + +#endif + diff --git a/attic/muse2-oom/muse2/muse/arranger/trackautomationview.cpp b/attic/muse2-oom/muse2/muse/arranger/trackautomationview.cpp new file mode 100644 index 00000000..8f7cfb12 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/trackautomationview.cpp @@ -0,0 +1,52 @@ +#include "trackautomationview.h" +#include "math.h" + +#include <QPainter> +#include <QPaintEvent> + +#include "track.h" + +TrackAutomationView::TrackAutomationView(QWidget *parent, Track *t) : QWidget(parent) +{ + printf("created trackautomationview\n"); + _t = t; + //show(); +} + +void TrackAutomationView::paintEvent(QPaintEvent* e) +{ + QPainter p(this); + const QRect &r = e->rect(); + + // temporary solution, audio track drawing moved here. + // best would be to get transparency to work correctly + p.setPen(QPen(Qt::black, 2, Qt::SolidLine)); + p.setBrush(Qt::gray); + p.drawRect(r); + + int height=r.bottom()-r.top(); + if( _t->type()>1) { // audio type + double volume = ((AudioTrack*)_t)->volume(); + double dbvolume = (20.0*log10(volume)+60) /70.0; // represent volume between 0 and 1 + if (dbvolume < 0) dbvolume =0.0; + printf("height=%d volume=%f dbvolume=%f\n", height, volume, dbvolume); + p.setPen(QPen(Qt::yellow,1,Qt::SolidLine)); + p.drawLine(r.left(),r.bottom()-dbvolume*height,r.right(),r.bottom()-dbvolume*height); + + } + + + + printf("paintEvent\n"); +} + +void TrackAutomationView::collectAutomationData() +{ + // here we should collect all automation data that is currently selected for viewing and + // prepare an event list that is easy to draw in paintEvent + // the main reason being that the event list in it's entirety likely contains too much data to + // be processed in the paintEvent. Better to preprocess. + +// CtrlListList cll =((AudioTrack*)_t)->controller(); +// cll.count() +} diff --git a/attic/muse2-oom/muse2/muse/arranger/trackautomationview.h b/attic/muse2-oom/muse2/muse/arranger/trackautomationview.h new file mode 100644 index 00000000..2ef05125 --- /dev/null +++ b/attic/muse2-oom/muse2/muse/arranger/trackautomationview.h @@ -0,0 +1,20 @@ +#ifndef TRACKAUTOMATIONVIEW_H +#define TRACKAUTOMATIONVIEW_H + +class QPaintEvent; +class QWidget; + +#include "track.h" + +class TrackAutomationView : public QWidget +{ + Track *_t; + void paintEvent(QPaintEvent *e); + std::map<int,int> automationList; +public: + TrackAutomationView(QWidget *parent, Track *t); + Track *track() { return _t; } + void collectAutomationData(); +}; + +#endif // TRACKAUTOMATIONVIEW_H |