Files
edgetx-gx12/companion/src/modeledit/mixes.cpp
T

527 lines
16 KiB
C++

#include "mixes.h"
#include "helpers.h"
MixesPanel::MixesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
ModelPanel(parent, model, generalSettings, firmware),
mixInserted(false),
highlightedSource(0),
modelPrinter(firmware, generalSettings, model)
{
QGridLayout * mixesLayout = new QGridLayout(this);
MixerlistWidget = new MixersList(this, false); // TODO enum
QPushButton * qbUp = new QPushButton(this);
QPushButton * qbDown = new QPushButton(this);
QPushButton * qbClear = new QPushButton(this);
qbUp->setText(tr("Move Up"));
qbUp->setIcon(CompanionIcon("moveup.png"));
qbUp->setShortcut(QKeySequence(tr("Ctrl+Up")));
qbDown->setText(tr("Move Down"));
qbDown->setIcon(CompanionIcon("movedown.png"));
qbDown->setShortcut(QKeySequence(tr("Ctrl+Down")));
qbClear->setText(tr("Clear Mixes"));
qbClear->setIcon(CompanionIcon("clear.png"));
mixesLayout->addWidget(MixerlistWidget,1,1,1,3);
mixesLayout->addWidget(qbUp,2,1);
mixesLayout->addWidget(qbClear,2,2);
mixesLayout->addWidget(qbDown,2,3);
connect(MixerlistWidget,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(mixerlistWidget_customContextMenuRequested(QPoint)));
connect(MixerlistWidget,SIGNAL(doubleClicked(QModelIndex)),this,SLOT(mixerlistWidget_doubleClicked(QModelIndex)));
connect(MixerlistWidget,SIGNAL(mimeDropped(int,const QMimeData*,Qt::DropAction)),this,SLOT(mimeMixerDropped(int,const QMimeData*,Qt::DropAction)));
connect(qbUp,SIGNAL(pressed()),SLOT(moveMixUp()));
connect(qbDown,SIGNAL(pressed()),SLOT(moveMixDown()));
connect(qbClear,SIGNAL(pressed()),SLOT(clearMixes()));
connect(MixerlistWidget,SIGNAL(keyWasPressed(QKeyEvent*)), this, SLOT(mixerlistWidget_KeyPress(QKeyEvent*)));
}
MixesPanel::~MixesPanel()
{
}
void MixesPanel::update()
{
// curDest -> destination channel
// i -> mixer number
MixerlistWidget->clear();
firstLine = true;
unsigned int curDest = 0;
unsigned int outputs = firmware->getCapability(Outputs);
for (int i=0; i<firmware->getCapability(Mixes); i++) {
MixData & mix = model->mixData[i];
// qDebug() << "mix.destCh: " << mix.destCh;
if (mix.destCh == 0 || mix.destCh>outputs) continue;
QString str = "";
while (curDest < mix.destCh-1) {
curDest++;
AddMixerLine(-curDest);
}
if (AddMixerLine(i)) {
curDest++;
}
}
while (curDest < outputs) {
curDest++;
AddMixerLine(-curDest);
}
}
/**
@brief Creates new mixer line (list item) and adds it to the list widget
@note Mixer lines are now HTML formated in order to support bold text.
@param[in] dest defines which mixer line to create.
If dest < 0 then create empty channel slotr fo channel -dest ( dest=-2 -> CH2)
if dest >=0 then create used channel based on model mix data from slot dest (dest=4 -> model mix[4])
@retval true destination channel is different from the previous list item
false destination channle is the same as previous list item
*/
bool MixesPanel::AddMixerLine(int dest)
{
bool new_ch;
QString str = getMixerText(dest, &new_ch);
QListWidgetItem *itm = new QListWidgetItem(str);
QByteArray qba(1, (quint8)dest);
if (dest >= 0) {
//add mix data
MixData *md = &model->mixData[dest];
qba.append((const char*)md, sizeof(MixData));
}
itm->setData(Qt::UserRole, qba);
#if MIX_ROW_HEIGHT_INCREASE > 0
if (new_ch && !firstLine) {
//increase size of this row
itm->setData(GroupHeaderRole, 1);
}
#endif
MixerlistWidget->addItem(itm);
firstLine = false;
// qDebug() << "MixesPanel::AddMixerLine(): dest" << dest << "text" << str;
return new_ch;
}
/**
@brief Returns HTML formated mixer representation
@param[in] dest defines which mixer line to create.
If dest < 0 then create empty channel slot for channel -dest ( dest=-2 -> CH2)
if dest >=0 then create used channel based on model mix data from slot dest (dest=4 -> model mix[4])
@retval string mixer line in HTML
*/
QString MixesPanel::getMixerText(int dest, bool * new_ch)
{
QString str;
bool newChannel = false;
if (dest < 0) {
str = modelPrinter.printMixerName(-dest);
//highlight channel if needed
if (-dest == (int)highlightedSource) {
str = "<b>" + str + "</b>";
}
newChannel = true;
}
else {
MixData & mix = model->mixData[dest];
//mix->destCh from 1 to 32
str = modelPrinter.printMixerName(mix.destCh);
if ((dest == 0) || (model->mixData[dest-1].destCh != mix.destCh)) {
newChannel = true;
//highlight channel if needed
if (mix.destCh == highlightedSource) {
str = "<b>" + str + "</b>";
}
}
else {
str.fill(' ');
}
str += modelPrinter.printMixerLine(mix, !newChannel, highlightedSource);
}
if (new_ch) *new_ch = newChannel;
return str.replace(" ", "&nbsp;");
}
bool MixesPanel::gm_insertMix(int idx)
{
if (idx < 0 || idx >= firmware->getCapability(Mixes) || model->mixData[firmware->getCapability(Mixes)-1].destCh > 0) {
QMessageBox::information(this, "companion", tr("Not enough available mixers!"));
return false;
}
int i = model->mixData[idx].destCh;
memmove(&model->mixData[idx+1], &model->mixData[idx],
(firmware->getCapability(Mixes)-(idx+1)) * sizeof(MixData) );
memset(&model->mixData[idx], 0, sizeof(MixData));
model->mixData[idx].srcRaw = RawSource(SOURCE_TYPE_NONE);
model->mixData[idx].destCh = i;
model->mixData[idx].weight = 100;
return true;
}
void MixesPanel::gm_deleteMix(int index)
{
memmove(&model->mixData[index], &model->mixData[index+1],
(firmware->getCapability(Mixes)-(index+1)) * sizeof(MixData));
memset(&model->mixData[firmware->getCapability(Mixes)-1], 0, sizeof(MixData));
}
void MixesPanel::gm_openMix(int index)
{
if(index < 0 || index>=firmware->getCapability(Mixes)) return;
MixData mixd(model->mixData[index]);
emit modified();
update();
MixerDialog *g = new MixerDialog(this, *model, &mixd, generalSettings, firmware);
if(g->exec()) {
model->mixData[index] = mixd;
emit modified();
update();
}
else {
if (mixInserted) {
gm_deleteMix(index);
}
mixInserted = false;
emit modified();
update();
}
}
int MixesPanel::getMixerIndex(unsigned int dch)
{
int i = 0;
while ((model->mixData[i].destCh <= dch) && (model->mixData[i].destCh) && (i < firmware->getCapability(Mixes))) i++;
if (i == firmware->getCapability(Mixes)) return -1;
return i;
}
void MixesPanel::mixerlistWidget_doubleClicked(QModelIndex index)
{
int idx = MixerlistWidget->item(index.row())->data(Qt::UserRole).toByteArray().at(0);
if (idx<0) {
int i = -idx;
idx = getMixerIndex(i); //get mixer index to insert
if (!gm_insertMix(idx)) return;
model->mixData[idx].destCh = i;
mixInserted = true;
}
else {
mixInserted = false;
}
gm_openMix(idx);
}
void MixesPanel::mixersDeleteList(QList<int> list)
{
qSort(list.begin(), list.end());
int iDec = 0;
foreach(int idx, list) {
gm_deleteMix(idx - iDec);
iDec++;
}
}
QList<int> MixesPanel::createMixListFromSelected()
{
QList<int> list;
foreach(QListWidgetItem *item, MixerlistWidget->selectedItems()) {
int idx= item->data(Qt::UserRole).toByteArray().at(0);
if(idx >= 0 && idx<firmware->getCapability(Mixes)) list << idx;
}
return list;
}
// TODO duplicated code
void MixesPanel::setSelectedByMixList(QList<int> list)
{
for(int i=0; i<MixerlistWidget->count(); i++) {
int t = MixerlistWidget->item(i)->data(Qt::UserRole).toByteArray().at(0);
if(list.contains(t)) {
MixerlistWidget->item(i)->setSelected(true);
}
}
}
void MixesPanel::mixersDelete(bool ask)
{
QList<int> list = createMixListFromSelected();
if(list.isEmpty()) return;
QMessageBox::StandardButton ret = QMessageBox::No;
if(ask) {
ret = QMessageBox::warning(this, "companion",
tr("Delete Selected Mixes?"),
QMessageBox::Yes | QMessageBox::No);
}
if ((ret == QMessageBox::Yes) || (!ask)) {
mixersDeleteList(list);
emit modified();
update();
}
}
void MixesPanel::mixersCut()
{
mixersCopy();
mixersDelete(false);
}
void MixesPanel::mixersCopy()
{
QList<int> list = createMixListFromSelected();
QByteArray mxData;
foreach(int idx, list) {
mxData.append((char*)&model->mixData[idx], sizeof(MixData));
}
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-companion-mix", mxData);
QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard);
}
void MixesPanel::pasteMixerMimeData(const QMimeData * mimeData, int destIdx)
{
if(mimeData->hasFormat("application/x-companion-mix")) {
int idx; // mixer index
int dch;
if (destIdx < 0) {
dch = -destIdx;
idx = getMixerIndex(dch) - 1; //get mixer index to insert
}
else {
idx = destIdx;
dch = model->mixData[idx].destCh;
}
QByteArray mxData = mimeData->data("application/x-companion-mix");
int i = 0;
while (i < mxData.size()) {
idx++;
if (idx == firmware->getCapability(Mixes)) break;
if (!gm_insertMix(idx)) break;
MixData * md = &model->mixData[idx];
memcpy(md, mxData.mid(i, sizeof(MixData)).constData(), sizeof(MixData));
md->destCh = dch;
i += sizeof(MixData);
}
emit modified();
update();
}
}
void MixesPanel::mixersPaste()
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
QListWidgetItem *item = MixerlistWidget->currentItem();
if (item) {
pasteMixerMimeData(mimeData, item->data(Qt::UserRole).toByteArray().at(0));
}
}
void MixesPanel::mixersDuplicate()
{
mixersCopy();
mixersPaste();
}
void MixesPanel::mixerOpen()
{
int idx = MixerlistWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0);
if(idx < 0) {
int i = -idx;
idx = getMixerIndex(i); //get mixer index to insert
if (!gm_insertMix(idx)) return;
model->mixData[idx].destCh = i;
mixInserted = true;
}
else {
mixInserted = false;
}
gm_openMix(idx);
}
void MixesPanel::mixerHighlight()
{
int idx = MixerlistWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0);
int dest;
if (idx<0) {
dest = -idx;
}
else {
dest = model->mixData[idx].destCh;
}
highlightedSource = ( (int)highlightedSource == dest) ? 0 : dest;
// qDebug() << "MixesPanel::mixerHighlight(): " << highlightedSource ;
for(int i=0; i<MixerlistWidget->count(); i++) {
int t = MixerlistWidget->item(i)->data(Qt::UserRole).toByteArray().at(0);
MixerlistWidget->item(i)->setText(getMixerText(t, 0));
}
}
void MixesPanel::mixerAdd()
{
if (!MixerlistWidget->currentItem()) return;
int index = MixerlistWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0);
if(index < 0) { // if empty then return relavent index
int i = -index;
index = getMixerIndex(i); //get mixer index to insert
if (!gm_insertMix(index)) return;
model->mixData[index].destCh = i;
mixInserted = true;
}
else {
index++;
if (!gm_insertMix(index)) return;
model->mixData[index].destCh = model->mixData[index-1].destCh;
mixInserted = true;
}
gm_openMix(index);
}
void MixesPanel::mixerlistWidget_customContextMenuRequested(QPoint pos)
{
QPoint globalPos = MixerlistWidget->mapToGlobal(pos);
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
bool hasData = mimeData->hasFormat("application/x-companion-mix");
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("add.png"), tr("&Add"), this, SLOT(mixerAdd()), tr("Ctrl+A"));
contextMenu.addAction(CompanionIcon("edit.png"), tr("&Edit"), this, SLOT(mixerOpen()), tr("Enter"));
contextMenu.addAction(CompanionIcon("fuses.png"), tr("&Toggle highlight"), this, SLOT(mixerHighlight()), tr("Ctrl+T"));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("&Delete"), this, SLOT(mixersDelete()), tr("Delete"));
contextMenu.addAction(CompanionIcon("copy.png"), tr("&Copy"), this, SLOT(mixersCopy()), tr("Ctrl+C"));
contextMenu.addAction(CompanionIcon("cut.png"), tr("C&ut"), this, SLOT(mixersCut()), tr("Ctrl+X"));
contextMenu.addAction(CompanionIcon("paste.png"), tr("&Paste"), this, SLOT(mixersPaste()), tr("Ctrl+V"))->setEnabled(hasData);
contextMenu.addAction(CompanionIcon("duplicate.png"), tr("Du&plicate"), this, SLOT(mixersDuplicate()), tr("Ctrl+U"));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"), this, SLOT(moveMixUp()), tr("Ctrl+Up"));
contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"), this, SLOT(moveMixDown()), tr("Ctrl+Down"));
contextMenu.exec(globalPos);
}
void MixesPanel::mimeMixerDropped(int index, const QMimeData *data, Qt::DropAction /*action*/)
{
int idx= MixerlistWidget->item(index > 0 ? index-1 : 0)->data(Qt::UserRole).toByteArray().at(0);
//qDebug() << "MixesPanel::mimeMixerDropped()" << index << data;
pasteMixerMimeData(data, idx);
}
void MixesPanel::mixesEdited()
{
emit modified();
}
void MixesPanel::mixerlistWidget_KeyPress(QKeyEvent *event)
{
if(event->matches(QKeySequence::SelectAll)) mixerAdd(); //Ctrl A
if(event->matches(QKeySequence::Delete)) mixersDelete();
if(event->matches(QKeySequence::Copy)) mixersCopy();
if(event->matches(QKeySequence::Cut)) mixersCut();
if(event->matches(QKeySequence::Paste)) mixersPaste();
if(event->matches(QKeySequence::Underline)) mixersDuplicate();
if(event->matches(QKeySequence::AddTab)) mixerHighlight();
if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) mixerOpen();
if(event->matches(QKeySequence::MoveToNextLine)) {
MixerlistWidget->setCurrentRow(MixerlistWidget->currentRow()+1);
}
if(event->matches(QKeySequence::MoveToPreviousLine)) {
MixerlistWidget->setCurrentRow(MixerlistWidget->currentRow()-1);
}
}
int MixesPanel::gm_moveMix(int idx, bool dir) //true=inc=down false=dec=up
{
if(idx > firmware->getCapability(Mixes) || (idx == firmware->getCapability(Mixes) && dir)) return idx;
MixData &src=model->mixData[idx];
if (idx == 0 && !dir) {
//special case: topmost mixer moving up
if (src.destCh > 1) src.destCh--;
return idx;
}
int tdx = dir ? idx+1 : idx-1;
MixData & tgt = model->mixData[tdx];
unsigned int outputs = firmware->getCapability(Outputs);
if(src.destCh == 0 || src.destCh > outputs || tgt.destCh > outputs) return idx;
if (tgt.destCh != src.destCh) {
if (dir && src.destCh < outputs) src.destCh++;
if (!dir && src.destCh > 0) src.destCh--;
return idx;
}
//flip between idx and tgt
MixData temp;
memcpy(&temp, &src, sizeof(MixData));
memcpy(&src, &tgt, sizeof(MixData));
memcpy(&tgt, &temp, sizeof(MixData));
return tdx;
}
void MixesPanel::moveMixUp()
{
QList<int> list = createMixListFromSelected();
QList<int> highlightList;
foreach(int idx, list) {
highlightList << gm_moveMix(idx, false);
}
emit modified();
update();
setSelectedByMixList(highlightList);
}
void MixesPanel::moveMixDown()
{
QList<int> list = createMixListFromSelected();
QList<int> highlightList;
foreach(int idx, list) {
highlightList << gm_moveMix(idx, true);
}
emit modified();
update();
setSelectedByMixList(highlightList);
}
void MixesPanel::clearMixes()
{
if (QMessageBox::question(this, tr("Clear Mixes?"), tr("Really clear all the mixes?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
model->clearMixes();
emit modified();
update();
}
}