You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
12 KiB
364 lines
12 KiB
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
|
Bown to claim authorship of this work have been asserted.
|
|
|
|
Other copyrights also apply to some parts of this work. Please
|
|
see the AUTHORS file and individual file headers for details.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version. See the file
|
|
COPYING included with this distribution for more information.
|
|
*/
|
|
|
|
|
|
#include "TupletDialog.h"
|
|
#include <tqlayout.h>
|
|
|
|
#include <tdelocale.h>
|
|
#include "base/NotationTypes.h"
|
|
#include "gui/editors/notation/NotationStrings.h"
|
|
#include "gui/editors/notation/NotePixmapFactory.h"
|
|
#include <kcombobox.h>
|
|
#include <kdialogbase.h>
|
|
#include <tqcheckbox.h>
|
|
#include <tqframe.h>
|
|
#include <tqgrid.h>
|
|
#include <tqgroupbox.h>
|
|
#include <tqlabel.h>
|
|
#include <tqobject.h>
|
|
#include <tqstring.h>
|
|
#include <tqvbox.h>
|
|
#include <tqwidget.h>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
TupletDialog::TupletDialog(TQWidget *parent, Note::Type defaultUnitType,
|
|
timeT maxDuration) :
|
|
KDialogBase(parent, 0, true, i18n("Tuplet"), Ok | Cancel | Help),
|
|
m_maxDuration(maxDuration)
|
|
{
|
|
setHelp("nv-tuplets");
|
|
TQVBox *vbox = makeVBoxMainWidget();
|
|
|
|
TQGroupBox *timingBox = new TQGroupBox
|
|
(1, TQt::Horizontal, i18n("New timing for tuplet group"), vbox);
|
|
|
|
if (m_maxDuration > 0) {
|
|
|
|
// bit of a sanity check
|
|
if (maxDuration < Note(Note::Semiquaver).getDuration()) {
|
|
maxDuration = Note(Note::Semiquaver).getDuration();
|
|
}
|
|
|
|
Note::Type maxUnitType =
|
|
Note::getNearestNote(maxDuration / 2, 0).getNoteType();
|
|
if (defaultUnitType > maxUnitType)
|
|
defaultUnitType = maxUnitType;
|
|
}
|
|
|
|
TQFrame *timingFrame = new TQFrame(timingBox);
|
|
TQGridLayout *timingLayout = new TQGridLayout(timingFrame, 3, 3, 5, 5);
|
|
|
|
timingLayout->addWidget(new TQLabel(i18n("Play "), timingFrame), 0, 0);
|
|
|
|
m_untupledCombo = new KComboBox(timingFrame);
|
|
timingLayout->addWidget(m_untupledCombo, 0, 1);
|
|
|
|
m_unitCombo = new KComboBox(timingFrame);
|
|
timingLayout->addWidget(m_unitCombo, 0, 2);
|
|
|
|
for (Note::Type t = Note::Shortest; t <= Note::Longest; ++t) {
|
|
Note note(t);
|
|
timeT duration(note.getDuration());
|
|
if (maxDuration > 0 && (2 * duration > maxDuration))
|
|
break;
|
|
timeT e; // error factor, ignore
|
|
m_unitCombo->insertItem(NotePixmapFactory::toTQPixmap
|
|
(NotePixmapFactory::makeNoteMenuPixmap(duration, e)),
|
|
NotationStrings::makeNoteMenuLabel(duration, false, e, true));
|
|
if (defaultUnitType == t) {
|
|
m_unitCombo->setCurrentItem(m_unitCombo->count() - 1);
|
|
}
|
|
}
|
|
|
|
timingLayout->addWidget(new TQLabel(i18n("in the time of "), timingFrame), 1, 0);
|
|
|
|
m_tupledCombo = new KComboBox(timingFrame);
|
|
timingLayout->addWidget(m_tupledCombo, 1, 1);
|
|
|
|
m_hasTimingAlready = new TQCheckBox
|
|
(i18n("Timing is already correct: update display only"), timingFrame);
|
|
m_hasTimingAlready->setChecked(false);
|
|
timingLayout->addMultiCellWidget(m_hasTimingAlready, 2, 2, 0, 2);
|
|
|
|
connect(m_hasTimingAlready, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotHasTimingChanged()));
|
|
|
|
updateUntupledCombo();
|
|
updateTupledCombo();
|
|
|
|
m_timingDisplayBox = new TQGroupBox
|
|
(1, TQt::Horizontal, i18n("Timing calculations"), vbox);
|
|
|
|
TQGrid *timingDisplayGrid = new TQGrid(3, TQt::Horizontal, m_timingDisplayBox);
|
|
|
|
if (maxDuration > 0) {
|
|
|
|
new TQLabel(i18n("Selected region:"), timingDisplayGrid);
|
|
new TQLabel("", timingDisplayGrid);
|
|
m_selectionDurationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_selectionDurationDisplay->setAlignment(int(TQLabel::AlignVCenter |
|
|
TQLabel::AlignRight));
|
|
} else {
|
|
m_selectionDurationDisplay = 0;
|
|
}
|
|
|
|
new TQLabel(i18n("Group with current timing:"), timingDisplayGrid);
|
|
m_untupledDurationCalculationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_untupledDurationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_untupledDurationDisplay->setAlignment(int(TQLabel::AlignVCenter |
|
|
TQLabel::AlignRight));
|
|
|
|
new TQLabel(i18n("Group with new timing:"), timingDisplayGrid);
|
|
m_tupledDurationCalculationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_tupledDurationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_tupledDurationDisplay->setAlignment(int(TQLabel::AlignVCenter |
|
|
TQLabel::AlignRight));
|
|
|
|
new TQLabel(i18n("Gap created by timing change:"), timingDisplayGrid);
|
|
m_newGapDurationCalculationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_newGapDurationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_newGapDurationDisplay->setAlignment(int(TQLabel::AlignVCenter |
|
|
TQLabel::AlignRight));
|
|
|
|
if (maxDuration > 0) {
|
|
|
|
new TQLabel(i18n("Unchanged at end of selection:"), timingDisplayGrid);
|
|
m_unchangedDurationCalculationDisplay = new TQLabel
|
|
("x", timingDisplayGrid);
|
|
m_unchangedDurationDisplay = new TQLabel("x", timingDisplayGrid);
|
|
m_unchangedDurationDisplay->setAlignment(int(TQLabel::AlignVCenter |
|
|
TQLabel::AlignRight));
|
|
|
|
} else {
|
|
m_unchangedDurationDisplay = 0;
|
|
}
|
|
|
|
updateTimingDisplays();
|
|
|
|
TQObject::connect(m_unitCombo, TQ_SIGNAL(activated(const TQString &)),
|
|
this, TQ_SLOT(slotUnitChanged(const TQString &)));
|
|
|
|
TQObject::connect(m_untupledCombo, TQ_SIGNAL(activated(const TQString &)),
|
|
this, TQ_SLOT(slotUntupledChanged(const TQString &)));
|
|
TQObject::connect(m_untupledCombo, TQ_SIGNAL(textChanged(const TQString &)),
|
|
this, TQ_SLOT(slotUntupledChanged(const TQString &)));
|
|
|
|
TQObject::connect(m_tupledCombo, TQ_SIGNAL(activated(const TQString &)),
|
|
this, TQ_SLOT(slotTupledChanged(const TQString &)));
|
|
TQObject::connect(m_tupledCombo, TQ_SIGNAL(textChanged(const TQString &)),
|
|
this, TQ_SLOT(slotTupledChanged(const TQString &)));
|
|
}
|
|
|
|
void
|
|
TupletDialog::slotHasTimingChanged()
|
|
{
|
|
updateUntupledCombo();
|
|
updateTupledCombo();
|
|
m_timingDisplayBox->setEnabled(!m_hasTimingAlready->isChecked());
|
|
}
|
|
|
|
Note::Type
|
|
TupletDialog::getUnitType() const
|
|
{
|
|
return Note::Shortest + m_unitCombo->currentItem();
|
|
}
|
|
|
|
int
|
|
TupletDialog::getUntupledCount() const
|
|
{
|
|
bool isNumeric = true;
|
|
int count = m_untupledCombo->currentText().toInt(&isNumeric);
|
|
if (count == 0 || !isNumeric)
|
|
return 1;
|
|
else
|
|
return count;
|
|
}
|
|
|
|
int
|
|
TupletDialog::getTupledCount() const
|
|
{
|
|
bool isNumeric = true;
|
|
int count = m_tupledCombo->currentText().toInt(&isNumeric);
|
|
if (count == 0 || !isNumeric)
|
|
return 1;
|
|
else
|
|
return count;
|
|
}
|
|
|
|
bool
|
|
TupletDialog::hasTimingAlready() const
|
|
{
|
|
return m_hasTimingAlready->isChecked();
|
|
}
|
|
|
|
void
|
|
TupletDialog::updateUntupledCombo()
|
|
{
|
|
// Untupled combo can contain numbers up to the maximum
|
|
// duration divided by the unit duration. If there's no
|
|
// maximum, we'll have to put in some likely values and
|
|
// allow the user to edit it. Both the numerical combos
|
|
// should possibly be spinboxes, except I think I like
|
|
// being able to "suggest" a few values
|
|
|
|
int maxValue = 12;
|
|
|
|
if (m_maxDuration) {
|
|
if (m_hasTimingAlready->isChecked()) {
|
|
maxValue = (m_maxDuration * 2) / Note(getUnitType()).getDuration();
|
|
} else {
|
|
maxValue = m_maxDuration / Note(getUnitType()).getDuration();
|
|
}
|
|
}
|
|
|
|
TQString previousText = m_untupledCombo->currentText();
|
|
if (previousText.toInt() == 0) {
|
|
if (maxValue < 3)
|
|
previousText = TQString("%1").arg(maxValue);
|
|
else
|
|
previousText = "3";
|
|
}
|
|
|
|
m_untupledCombo->clear();
|
|
bool setText = false;
|
|
|
|
for (int i = 1; i <= maxValue; ++i) {
|
|
TQString text = TQString("%1").arg(i);
|
|
m_untupledCombo->insertItem(text);
|
|
if (m_hasTimingAlready->isChecked()) {
|
|
if (i == (m_maxDuration * 3) / (Note(getUnitType()).getDuration()*2)) {
|
|
m_untupledCombo->setCurrentItem(m_untupledCombo->count() - 1);
|
|
}
|
|
} else if (text == previousText) {
|
|
m_untupledCombo->setCurrentItem(m_untupledCombo->count() - 1);
|
|
setText = true;
|
|
}
|
|
}
|
|
|
|
if (!setText) {
|
|
m_untupledCombo->setEditText(previousText);
|
|
}
|
|
}
|
|
|
|
void
|
|
TupletDialog::updateTupledCombo()
|
|
{
|
|
// should contain all positive integers less than the
|
|
// largest value in the untupled combo. In principle
|
|
// we can support values larger, but we can't quite
|
|
// do the tupleting transformation yet
|
|
|
|
int untupled = getUntupledCount();
|
|
|
|
TQString previousText = m_tupledCombo->currentText();
|
|
if (previousText.toInt() == 0 ||
|
|
previousText.toInt() > untupled) {
|
|
if (untupled < 2)
|
|
previousText = TQString("%1").arg(untupled);
|
|
else
|
|
previousText = "2";
|
|
}
|
|
|
|
m_tupledCombo->clear();
|
|
|
|
for (int i = 1; i < untupled; ++i) {
|
|
TQString text = TQString("%1").arg(i);
|
|
m_tupledCombo->insertItem(text);
|
|
if (m_hasTimingAlready->isChecked()) {
|
|
if (i == m_maxDuration / Note(getUnitType()).getDuration()) {
|
|
m_tupledCombo->setCurrentItem(m_tupledCombo->count() - 1);
|
|
}
|
|
} else if (text == previousText) {
|
|
m_tupledCombo->setCurrentItem(m_tupledCombo->count() - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
TupletDialog::updateTimingDisplays()
|
|
{
|
|
timeT unitDuration = Note(getUnitType()).getDuration();
|
|
|
|
int untupledCount = getUntupledCount();
|
|
int tupledCount = getTupledCount();
|
|
|
|
timeT untupledDuration = unitDuration * untupledCount;
|
|
timeT tupledDuration = unitDuration * tupledCount;
|
|
|
|
if (m_selectionDurationDisplay) {
|
|
m_selectionDurationDisplay->setText(TQString("%1").arg(m_maxDuration));
|
|
}
|
|
|
|
m_untupledDurationCalculationDisplay->setText
|
|
(TQString(" %1 x %2 = ").arg(untupledCount).arg(unitDuration));
|
|
m_untupledDurationDisplay->setText
|
|
(TQString("%1").arg(untupledDuration));
|
|
|
|
m_tupledDurationCalculationDisplay->setText
|
|
(TQString(" %1 x %2 = ").arg(tupledCount).arg(unitDuration));
|
|
m_tupledDurationDisplay->setText
|
|
(TQString("%1").arg(tupledDuration));
|
|
|
|
m_newGapDurationCalculationDisplay->setText
|
|
(TQString(" %1 - %2 = ").arg(untupledDuration).arg(tupledDuration));
|
|
m_newGapDurationDisplay->setText
|
|
(TQString("%1").arg(untupledDuration - tupledDuration));
|
|
|
|
if (m_selectionDurationDisplay && m_unchangedDurationDisplay) {
|
|
if (m_maxDuration != untupledDuration) {
|
|
m_unchangedDurationCalculationDisplay->setText
|
|
(TQString(" %1 - %2 = ").arg(m_maxDuration).arg(untupledDuration));
|
|
} else {
|
|
m_unchangedDurationCalculationDisplay->setText("");
|
|
}
|
|
m_unchangedDurationDisplay->setText
|
|
(TQString("%1").arg(m_maxDuration - untupledDuration));
|
|
}
|
|
}
|
|
|
|
void
|
|
TupletDialog::slotUnitChanged(const TQString &)
|
|
{
|
|
updateUntupledCombo();
|
|
updateTupledCombo();
|
|
updateTimingDisplays();
|
|
}
|
|
|
|
void
|
|
TupletDialog::slotUntupledChanged(const TQString &)
|
|
{
|
|
updateTupledCombo();
|
|
updateTimingDisplays();
|
|
}
|
|
|
|
void
|
|
TupletDialog::slotTupledChanged(const TQString &)
|
|
{
|
|
updateTimingDisplays();
|
|
}
|
|
|
|
}
|
|
#include "TupletDialog.moc"
|