#include "mixes.h" #include "helpers.h" // #include // #include "expodialog.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 QByteArray qba; MixerlistWidget->clear(); firstLine = true; unsigned int curDest = 0; int i; unsigned int outputs = firmware->getCapability(Outputs); for (i=0; igetCapability(Mixes); i++) { MixData *md = &model->mixData[i]; // qDebug() << "md->destCh: " << md->destCh; if (md->destCh==0 || md->destCh>outputs) continue; QString str = ""; while (curDest < md->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; if (new_ch) *new_ch = 0; if (dest < 0) { str = modelPrinter.printMixerName(-dest); //highlight channel if needed if (-dest == (int)highlightedSource) { str = "" + str + ""; } if (new_ch) *new_ch = 1; } else { MixData *md = &model->mixData[dest]; //md->destCh from 1 to 32 str = modelPrinter.printMixerName(md->destCh); if ((dest == 0) || (model->mixData[dest-1].destCh != md->destCh)) { if (new_ch) *new_ch = 1; //highlight channel if needed if (md->destCh == highlightedSource) { str = "" + str + ""; } } else { str.fill(' '); } str += modelPrinter.printMixerLine(md, highlightedSource); } 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) && (igetCapability(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(igetCapability(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.destCh0)) 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(); } }