diff --git a/companion/src/modeledit/inputs.cpp b/companion/src/modeledit/inputs.cpp index a3cece6c1..3852264c7 100644 --- a/companion/src/modeledit/inputs.cpp +++ b/companion/src/modeledit/inputs.cpp @@ -55,6 +55,7 @@ void InputsPanel::update() // i -> mixer number QByteArray qba; ExposlistWidget->clear(); + firstLine = true; int curDest = -1; for (int i=0; ichn-1) { curDest++; - str = getInputStr(model, curDest); - qba.clear(); - qba.append((quint8)-curDest-1); - QListWidgetItem *itm = new QListWidgetItem(str); - itm->setData(Qt::UserRole,qba); - ExposlistWidget->addItem(itm); + AddInputLine(-curDest-1); + } + if (AddInputLine(i)) { + curDest++; } - if (curDest!=(int)md->chn) { + } + + while (curDest Input05) + if dest >=0 then create used input based on model input data from slot dest (dest=4 -> model expoData[4]) + + @retval true created input number is different from the previous list item + false created input number is the same as previous list item +*/ +bool InputsPanel::AddInputLine(int dest) +{ + bool new_ch; + QString str = getInputText(dest, &new_ch); + QListWidgetItem *itm = new QListWidgetItem(str); + QByteArray qba(1, (quint8)dest); + if (dest >= 0) { + //add input data + ExpoData *md = &model.expoData[dest]; + qba.append((const char*)md, sizeof(ExpoData)); + } + 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 + ExposlistWidget->addItem(itm); + firstLine = false; + //qDebug() << "InputsPanel::AddInputLine(): dest" << dest << "text" << str; + return new_ch; +} + + +/** + @brief Returns HTML formated input representation + + @param[in] dest defines which input line to create. + If dest < 0 then create empty input slot for input -dest ( dest=-6 -> Input05) + if dest >=0 then create used input based on model input data from slot dest (dest=4 -> model expoData[4]) + + @retval string input line in HTML +*/ +QString InputsPanel::getInputText(int dest, bool * new_ch) +{ + QString str; + if (new_ch) *new_ch = 0; + if (dest < 0) { + str = getInputStr(model, -dest-1); + if (new_ch) *new_ch = 1; + } + else { + ExpoData *md = &model.expoData[dest]; + + if ((dest == 0) || (model.expoData[dest-1].chn != md->chn)) { + if (new_ch) *new_ch = 1; if (firmware->getCapability(VirtualInputs)) str = QString("%1").arg(getInputStr(model, md->chn), -8, ' '); else str = getInputStr(model, md->chn); - curDest = md->chn; } else { if (firmware->getCapability(VirtualInputs)) @@ -117,29 +184,10 @@ void InputsPanel::update() QString expoName = md->name; if (!expoName.isEmpty()) str += QString(" [%1]").arg(expoName); } - - qba.clear(); - qba.append((quint8)i); - qba.append((const char*)md, sizeof(ExpoData)); - QListWidgetItem *itm = new QListWidgetItem(str); - itm->setData(Qt::UserRole,qba); // expo number - ExposlistWidget->addItem(itm); //(str); } - - while (curDestsetData(Qt::UserRole,qba); // add new expo - ExposlistWidget->addItem(itm); - } - - lock = false; + return Qt::escape(str).replace(" ", " "); } - bool InputsPanel::gm_insertExpo(int idx) { if (idx<0 || idx>=C9X_MAX_EXPOS || model.expoData[C9X_MAX_EXPOS-1].mode > 0) { diff --git a/companion/src/modeledit/inputs.h b/companion/src/modeledit/inputs.h index 393901b2c..83db760a6 100644 --- a/companion/src/modeledit/inputs.h +++ b/companion/src/modeledit/inputs.h @@ -33,6 +33,7 @@ class InputsPanel : public ModelPanel private: bool expoInserted; MixersList *ExposlistWidget; + bool firstLine; int getExpoIndex(unsigned int dch); bool gm_insertExpo(int idx); @@ -43,6 +44,8 @@ class InputsPanel : public ModelPanel QList createExpoListFromSelected(); void setSelectedByExpoList(QList list); void pasteExpoMimeData(const QMimeData * mimeData, int destIdx); + bool AddInputLine(int dest); + QString getInputText(int dest, bool * new_ch); }; diff --git a/companion/src/modeledit/mixerslist.cpp b/companion/src/modeledit/mixerslist.cpp index 06d8cd3f5..f1a368a03 100644 --- a/companion/src/modeledit/mixerslist.cpp +++ b/companion/src/modeledit/mixerslist.cpp @@ -10,6 +10,7 @@ MixersList::MixersList(QWidget *parent, bool expo) : setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(true); + setItemDelegate(new MixersDelegate(parent)); //set custom paint handler } void MixersList::keyPressEvent(QKeyEvent *event) @@ -17,38 +18,110 @@ void MixersList::keyPressEvent(QKeyEvent *event) emit keyWasPressed(event); } + +/** + @brief Override to give us different mime type for mixers and inputs +*/ +QStringList MixersList::mimeTypes () const +{ + QStringList types; + if (expo) types << "application/x-companion-expo-item"; + else types << "application/x-companion-mix-item"; + return types; +} + bool MixersList::dropMimeData( int index, const QMimeData * data, Qt::DropAction action ) { - QByteArray dropData = data->data("application/x-qabstractitemmodeldatalist"); + // qDebug() << "MixersList::dropMimeData() index:" << index << "formats" << data->formats(); + QByteArray dropData = data->data(expo ? "application/x-companion-expo-item" : "application/x-companion-mix-item"); + if (dropData.isNull() ) return false; QDataStream stream(&dropData, QIODevice::ReadOnly); QByteArray qba; - while (!stream.atEnd()) - { + while (!stream.atEnd()) { int r,c; QMap v; stream >> r >> c >> v; QList lsVars; lsVars = v.values(); - QString itemString = lsVars.at(0).toString(); qba.append(lsVars.at(1).toByteArray().mid(1)); - - if(itemString.isEmpty()) {}; + // qDebug() << "MixersList::dropMimeData() added" << lsVars.count() << "items, data:" << lsVars; } - if(qba.length()>0) - { + if(qba.length()>0) { QMimeData *mimeData = new QMimeData; - if (expo) - mimeData->setData("application/x-companion-expo", qba); - else - mimeData->setData("application/x-companion-mix", qba); - + mimeData->setData(expo ? "application/x-companion-expo" : "application/x-companion-mix", qba); emit mimeDropped(index, mimeData, action); + delete mimeData; //is this correct? is mimeData freed elswere?? } - return true; } +/** + @brief Paints our HTML formated list item text +*/ +void MixersDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 options = option; + initStyleOption(&options, index); + painter->save(); + + //setup html document + QTextDocument doc; + SetupDocument(doc, options); + + options.text = ""; + options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); + + if ( index.model()->data(index, GroupHeaderRole).toInt() > 0 ) { + //paint with added space + painter->translate(options.rect.left(), options.rect.top() + MIX_ROW_HEIGHT_INCREASE); + QRect clip(0, 0, options.rect.width(), options.rect.height() - MIX_ROW_HEIGHT_INCREASE); + doc.drawContents(painter, clip); + } + else { + painter->translate(options.rect.left(), options.rect.top()); + QRect clip(0, 0, options.rect.width(), options.rect.height()); + doc.drawContents(painter, clip); + } + painter->restore(); +} + + +/** + @brief Returns needed size for our HTML formated list item text +*/ +QSize MixersDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + QStyleOptionViewItemV4 options = option; + initStyleOption(&options, index); + + //setup html document + QTextDocument doc; + SetupDocument(doc, options); + + //qDebug() << "MixersDelegate::sizeHint() UserRole-> " << index.model()->data(index, Qt::UserRole + 2); + int height = doc.size().height(); + if ( index.model()->data(index, Qt::UserRole + 2).toInt() > 0 ) { + //qDebug() << "MixersDelegate::sizeHint() detected channel head"; + height = doc.size().height() + MIX_ROW_HEIGHT_INCREASE; + } + //qDebug() << "MixersDelegate::sizeHint() options.rect " << options.rect; + //qDebug() << "MixersDelegate::sizeHint() result " << QSize(doc.idealWidth(), height) << options.rect.width(); + return QSize(doc.idealWidth(), height); +} + +void MixersDelegate::SetupDocument(QTextDocument & doc, const QStyleOptionViewItemV4 & options) const +{ + //setup html document + doc.setHtml(options.text); + doc.setDefaultFont(options.font); + + //minimize margins (default margins look ugly) + QTextFrame *tf = doc.rootFrame(); + QTextFrameFormat tff = tf->frameFormat(); + tff.setMargin(0); + tf->setFrameFormat(tff); +} diff --git a/companion/src/modeledit/mixerslist.h b/companion/src/modeledit/mixerslist.h index 52e9ffebb..a0cc9f0c7 100644 --- a/companion/src/modeledit/mixerslist.h +++ b/companion/src/modeledit/mixerslist.h @@ -3,6 +3,10 @@ #include + +#define MIX_ROW_HEIGHT_INCREASE 8 //how much space is added above mixer row (for new channel), if 0 space adding is disabled +const int GroupHeaderRole = (Qt::UserRole+2); //defines new user role for list items. If value is > 0, then space is added before that item + class MixersList : public QListWidget { Q_OBJECT @@ -18,10 +22,32 @@ class MixersList : public QListWidget public slots: bool dropMimeData(int index, const QMimeData *data, Qt::DropAction action); + protected: + virtual QStringList mimeTypes() const; + private: QPoint dragStartPosition; bool expo; }; +/** + @brief Provides sizing and painting of items for our mixes and inputs lists + + This class draws HTML formated strings into QListWidget class +*/ +class MixersDelegate : public QStyledItemDelegate +{ + Q_OBJECT + public: + inline MixersDelegate(QObject *parent) : QStyledItemDelegate(parent) {}; + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; + + private: + void SetupDocument(QTextDocument & doc, const QStyleOptionViewItemV4 & options) const; + +}; + #endif // MIXERSLIST_H diff --git a/companion/src/modeledit/mixes.cpp b/companion/src/modeledit/mixes.cpp index a40f66ff4..56bf98c47 100644 --- a/companion/src/modeledit/mixes.cpp +++ b/companion/src/modeledit/mixes.cpp @@ -57,15 +57,16 @@ QString MixesPanel::getChannelLabel(int curDest) name.append(" "); str += name.left(8); } - return str; + return Qt::escape(str); } void MixesPanel::update() { - // curDest -> destination channel4 + // curDest -> destination channel // i -> mixer number QByteArray qba; MixerlistWidget->clear(); + firstLine = true; unsigned int curDest = 0; int i; unsigned int outputs = firmware->getCapability(Outputs); @@ -90,50 +91,62 @@ void MixesPanel::update() } } -#define MIX_ROW_HEIGHT_INCREASE 8 //how much space is added above mixer row (for new channel), if 0 space adding is disabled +/** + @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(); - itm->setData(Qt::UserRole, QByteArray(1, (quint8)dest)); + 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 && (dest > 0)) || (dest < 0)) { + if (new_ch && !firstLine) { //increase size of this row - QFontMetrics * fm = new QFontMetrics(itm->font()); - QRect rect = fm->boundingRect("C)"); - itm->setSizeHint(QSize(-1, (rect.height() * (14+MIX_ROW_HEIGHT_INCREASE))/10)); + itm->setData(GroupHeaderRole, 1); } #endif MixerlistWidget->addItem(itm); - MixerlistWidget->setItemWidget(itm, getMixerWidget(str)); - // qDebug() << "MixesPanel::AddMixerLine(): " << str; + firstLine = false; + // qDebug() << "MixesPanel::AddMixerLine(): dest" << dest << "text" << str; return new_ch; } -QWidget * MixesPanel::getMixerWidget(const QString & mixer_text) -{ - QLabel * l = new QLabel(); - l->setFont(QFont("Courier New",12)); - l->setTextFormat(Qt::RichText); - l->setAlignment(Qt::AlignBottom); - QString formated_str(mixer_text); - formated_str.replace(" ", " "); - l->setText(formated_str); - return l; -} +/** + @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; - *new_ch = 0; + if (new_ch) *new_ch = 0; if (dest < 0) { str = getChannelLabel(-dest); //highlight channell if needed if (-dest == (int)highlightedSource) { str = "" + str + ""; } + if (new_ch) *new_ch = 1; } else { MixData *md = &model.mixData[dest]; @@ -141,12 +154,12 @@ QString MixesPanel::getMixerText(int dest, bool * new_ch) str = getChannelLabel(md->destCh); if ((dest == 0) || (model.mixData[dest-1].destCh != md->destCh)) { - *new_ch = 1; + if (new_ch) *new_ch = 1; //highlight channell if needed if (md->destCh == highlightedSource) { str = "" + str + ""; } - } + } else { str.fill(' '); } @@ -201,7 +214,7 @@ QString MixesPanel::getMixerText(int dest, bool * new_ch) } } } - return str; + return str.replace(" ", " "); } bool MixesPanel::gm_insertMix(int idx) @@ -426,9 +439,7 @@ void MixesPanel::mixerHighlight() // qDebug() << "MixesPanel::mixerHighlight(): " << highlightedSource ; for(int i=0; icount(); i++) { int t = MixerlistWidget->item(i)->data(Qt::UserRole).toByteArray().at(0); - bool dummy; - QWidget *w = getMixerWidget(getMixerText(t, &dummy)); - MixerlistWidget->setItemWidget(MixerlistWidget->item(i), w); + MixerlistWidget->item(i)->setText(getMixerText(t, 0)); } } @@ -483,6 +494,7 @@ void MixesPanel::mixerlistWidget_customContextMenuRequested(QPoint pos) 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); } @@ -510,14 +522,11 @@ void MixesPanel::mixerlistWidget_KeyPress(QKeyEvent *event) 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; + if(idx>firmware->getCapability(Mixes) || (idx==0 && !dir) || (idx==firmware->getCapability(Mixes) && dir)) return idx; int tdx = dir ? idx+1 : idx-1; MixData &src=model.mixData[idx]; MixData &tgt=model.mixData[tdx]; - - if (src.destCh == 1 && !dir) - return idx; unsigned int outputs = firmware->getCapability(Outputs); if((src.destCh==0) || (src.destCh>outputs) || (tgt.destCh>outputs)) return idx; diff --git a/companion/src/modeledit/mixes.h b/companion/src/modeledit/mixes.h index fda60d8fc..eae80ca98 100644 --- a/companion/src/modeledit/mixes.h +++ b/companion/src/modeledit/mixes.h @@ -43,6 +43,7 @@ class MixesPanel : public ModelPanel MixersList *MixerlistWidget; bool mixInserted; unsigned int highlightedSource; + bool firstLine; int getMixerIndex(unsigned int dch); bool gm_insertMix(int idx); @@ -55,7 +56,6 @@ class MixesPanel : public ModelPanel QString getChannelLabel(int curDest); bool AddMixerLine(int dest); QString getMixerText(int dest, bool * new_ch); - QWidget * getMixerWidget(const QString & mixer_text); }; #endif // MIXES_H