#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; igetCapability(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 = "" + str + ""; } 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 = "" + str + ""; } } else { str.fill(' '); } str += modelPrinter.printMixerLine(mix, !newChannel, highlightedSource); } if (new_ch) *new_ch = newChannel; return str.replace(" ", " "); } 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 list) { qSort(list.begin(), list.end()); int iDec = 0; foreach(int idx, list) { gm_deleteMix(idx - iDec); iDec++; } } QList MixesPanel::createMixListFromSelected() { QList list; foreach(QListWidgetItem *item, MixerlistWidget->selectedItems()) { int idx= item->data(Qt::UserRole).toByteArray().at(0); if(idx >= 0 && idxgetCapability(Mixes)) list << idx; } return list; } // TODO duplicated code void MixesPanel::setSelectedByMixList(QList list) { for(int i=0; icount(); 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 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 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; icount(); 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 list = createMixListFromSelected(); QList highlightList; foreach(int idx, list) { highlightList << gm_moveMix(idx, false); } emit modified(); update(); setSelectedByMixList(highlightList); } void MixesPanel::moveMixDown() { QList list = createMixListFromSelected(); QList 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(); } }