File indexing completed on 2024-05-19 04:56:04
0001 /** 0002 * \file frametablemodel.cpp 0003 * Model for table with frames. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 01 May 2011 0008 * 0009 * Copyright (C) 2011-2024 Urs Fleisch 0010 * 0011 * This file is part of Kid3. 0012 * 0013 * Kid3 is free software; you can redistribute it and/or modify 0014 * it under the terms of the GNU General Public License as published by 0015 * the Free Software Foundation; either version 2 of the License, or 0016 * (at your option) any later version. 0017 * 0018 * Kid3 is distributed in the hope that it will be useful, 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0021 * GNU General Public License for more details. 0022 * 0023 * You should have received a copy of the GNU General Public License 0024 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0025 */ 0026 0027 #include "frametablemodel.h" 0028 #include <algorithm> 0029 #include "coretaggedfileiconprovider.h" 0030 #include "fileconfig.h" 0031 #include "pictureframe.h" 0032 #include "framenotice.h" 0033 0034 namespace { 0035 0036 QHash<int,QByteArray> getRoleHash() 0037 { 0038 QHash<int, QByteArray> roles; 0039 roles[Qt::CheckStateRole] = "checkState"; 0040 roles[FrameTableModel::FrameTypeRole] = "frameType"; 0041 roles[FrameTableModel::NameRole] = "name"; 0042 roles[FrameTableModel::ValueRole] = "value"; 0043 roles[FrameTableModel::ModifiedRole] = "modified"; 0044 roles[FrameTableModel::TruncatedRole] = "truncated"; 0045 roles[FrameTableModel::InternalNameRole] = "internalName"; 0046 roles[FrameTableModel::FieldIdsRole] = "fieldIds"; 0047 roles[FrameTableModel::FieldValuesRole] = "fieldValues"; 0048 roles[FrameTableModel::CompletionsRole] = "completions"; 0049 roles[FrameTableModel::NoticeRole] = "notice"; 0050 roles[FrameTableModel::NoticeWarningRole] = "noticeWarning"; 0051 return roles; 0052 } 0053 0054 } 0055 0056 /** 0057 * Constructor. 0058 * @param id3v1 true if model for ID3v1 frames 0059 * @param colorProvider colorProvider 0060 * @param parent parent widget 0061 */ 0062 FrameTableModel::FrameTableModel( 0063 bool id3v1, CoreTaggedFileIconProvider* colorProvider, QObject* parent) 0064 : QAbstractTableModel(parent), m_markedRows(0), m_changedFrames(0), 0065 m_colorProvider(colorProvider), m_id3v1(id3v1), m_emptyHeaders(false) 0066 { 0067 setObjectName(QLatin1String("FrameTableModel")); 0068 } 0069 0070 /** 0071 * Get item flags for index. 0072 * @param index model index 0073 * @return item flags 0074 */ 0075 Qt::ItemFlags FrameTableModel::flags(const QModelIndex& index) const 0076 { 0077 Qt::ItemFlags theFlags = QAbstractTableModel::flags(index); 0078 if (index.isValid()) { 0079 if (index.column() == CI_Enable) { 0080 theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; 0081 } else if (index.column() == CI_Value) { 0082 theFlags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; 0083 } 0084 } 0085 return theFlags; 0086 } 0087 0088 /** 0089 * Get data for a given role. 0090 * @param index model index 0091 * @param role item data role 0092 * @return data for role 0093 */ 0094 QVariant FrameTableModel::data(const QModelIndex& index, int role) const 0095 { 0096 if (!index.isValid() || 0097 index.row() < 0 || index.row() >= static_cast<int>(frames().size()) || 0098 index.column() < 0 || index.column() >= CI_NumColumns) 0099 return QVariant(); 0100 auto it = frameAt(index.row()); 0101 bool isModified = false, isTruncated = false; 0102 if ((role == Qt::BackgroundRole && index.column() == CI_Enable) || 0103 role == ModifiedRole) { 0104 Frame::ExtendedType extendedType = it->getExtendedType(); 0105 Frame::Type type = extendedType.getType(); 0106 isModified = FileConfig::instance().markChanges() && 0107 (it->isValueChanged() || 0108 (type != Frame::FT_Other && 0109 static_cast<unsigned>(type) < sizeof(m_changedFrames) * 8 && 0110 (m_changedFrames & (1ULL << type)) != 0) || 0111 (type == Frame::FT_Other && 0112 m_changedOtherFrameNames.contains(extendedType.getInternalName()))); 0113 } 0114 if (((role == Qt::BackgroundRole || role == Qt::ToolTipRole) && 0115 index.column() == CI_Value) || 0116 (role == TruncatedRole || role == NoticeRole || role == NoticeWarningRole)) { 0117 isTruncated = (static_cast<unsigned>(index.row()) < sizeof(m_markedRows) * 8 && 0118 (m_markedRows & (1ULL << index.row())) != 0) || it->isMarked(); 0119 } 0120 if (role == Qt::DisplayRole || role == Qt::EditRole) { 0121 if (index.column() == CI_Enable) { 0122 QString displayName = Frame::getDisplayName(it->getName()); 0123 if (it->getValue() != Frame::differentRepresentation()) { 0124 if (it->getType() == Frame::FT_Picture) { 0125 if (QVariant fieldValue = it->getFieldValue(Frame::ID_PictureType); 0126 fieldValue.isValid()) { 0127 if (auto pictureType = 0128 static_cast<PictureFrame::PictureType>(fieldValue.toInt()); 0129 pictureType != PictureFrame::PT_Other) { 0130 if (QString typeName = PictureFrame::getPictureTypeName(pictureType); 0131 !typeName.isEmpty()) { 0132 displayName += QLatin1String(": "); 0133 displayName += typeName; 0134 } 0135 } 0136 } 0137 } else if (it->getType() == Frame::FT_Other) { 0138 if (it->getInternalName().startsWith(QLatin1String("RVA2"))) { 0139 if (QVariant fieldValue = it->getFieldValue(Frame::ID_Id); 0140 fieldValue.isValid()) { 0141 if (auto identifier = fieldValue.toString(); 0142 !identifier.isEmpty()) { 0143 displayName = tr("Volume"); 0144 displayName += QLatin1String(": "); 0145 displayName += identifier; 0146 } 0147 } 0148 } else if (it->getInternalName().startsWith(QLatin1String("UFID"))) { 0149 if (QVariant fieldValue = it->getFieldValue(Frame::ID_Owner); 0150 fieldValue.isValid()) { 0151 if (auto owner = fieldValue.toString(); !owner.isEmpty()) { 0152 // Shorten the owner so that it is visible in the frame type column. 0153 // For example http://musicbrainz.org -> musicbrainz 0154 // http://www.cddb.com/id3/taginfo.html -> taginfo 0155 // http://www.id3.org/dummy/ufid.html -> ufid 0156 if (int endPos = owner.lastIndexOf(QLatin1Char('.')); 0157 endPos != -1) { 0158 int startPos = owner.lastIndexOf(QLatin1Char('.'), endPos - 1); 0159 if (int slashPos = owner.lastIndexOf(QLatin1Char('/'), endPos - 1); 0160 slashPos != -1 && slashPos > startPos) { 0161 startPos = slashPos; 0162 } 0163 if (startPos != -1) { 0164 owner = owner.mid(startPos + 1, endPos - startPos - 1); 0165 } 0166 } 0167 displayName = tr("File ID"); 0168 displayName += QLatin1String(": "); 0169 displayName += owner; 0170 } 0171 } 0172 } 0173 } 0174 } 0175 return displayName; 0176 } 0177 if (index.column() == CI_Value) 0178 return it->getValue(); 0179 } else if (role == Qt::CheckStateRole && index.column() == CI_Enable) { 0180 return m_frameSelected.at(index.row()) ? Qt::Checked : Qt::Unchecked; 0181 } else if (role == Qt::BackgroundRole) { 0182 if (m_colorProvider) { 0183 if (index.column() == CI_Enable) { 0184 return m_colorProvider->colorForContext( 0185 isModified ? ColorContext::Marked : ColorContext::None); 0186 } 0187 if (index.column() == CI_Value) { 0188 return m_colorProvider->colorForContext( 0189 isTruncated ? ColorContext::Error : ColorContext::None); 0190 } 0191 } 0192 } else if (role == Qt::ToolTipRole) { 0193 QString toolTip; 0194 if (isTruncated && index.column() == CI_Value) { 0195 FrameNotice notice = it->isMarked() ? it->getNotice() 0196 : FrameNotice::Truncated; 0197 toolTip = notice.getDescription(); 0198 } 0199 return toolTip; 0200 } else if (role == FrameTypeRole) { 0201 return it->getType(); 0202 } else if (role == NameRole) { 0203 return Frame::getDisplayName(it->getName()); 0204 } else if (role == ValueRole) { 0205 return it->getValue(); 0206 } else if (role == ModifiedRole) { 0207 return isModified; 0208 } else if (role == TruncatedRole) { 0209 return isTruncated; 0210 } else if (role == InternalNameRole) { 0211 return it->getInternalName(); 0212 } else if (role == FieldIdsRole) { 0213 QVariantList result; 0214 const Frame::FieldList& fields = it->getFieldList(); 0215 for (auto fit = fields.constBegin(); fit != fields.constEnd(); ++fit) { 0216 result.append(fit->m_id); 0217 } 0218 return result; 0219 } else if (role == FieldValuesRole) { 0220 QVariantList result; 0221 const Frame::FieldList& fields = it->getFieldList(); 0222 for (auto fit = fields.constBegin(); fit != fields.constEnd(); ++fit) { 0223 result.append(fit->m_value); 0224 } 0225 return result; 0226 } else if (role == CompletionsRole) { 0227 #if QT_VERSION >= 0x050e00 0228 const QSet<QString> completions = 0229 getCompletionsForType(it->getExtendedType()); 0230 QStringList result(completions.constBegin(), completions.constEnd()); 0231 #else 0232 QStringList result = getCompletionsForType(it->getExtendedType()).toList(); 0233 #endif 0234 result.sort(); 0235 return result; 0236 } else if (role == NoticeRole) { 0237 QString toolTip; 0238 if (isTruncated) { 0239 FrameNotice notice = it->isMarked() ? it->getNotice() 0240 : FrameNotice::Truncated; 0241 toolTip = notice.getDescription(); 0242 } 0243 return toolTip; 0244 } else if (role == NoticeWarningRole) { 0245 return isTruncated ? (it->isMarked() ? it->getNotice().getWarning() 0246 : FrameNotice::Truncated) 0247 : FrameNotice::None; 0248 } 0249 return QVariant(); 0250 } 0251 0252 /** 0253 * Set data for a given role. 0254 * @param index model index 0255 * @param value data value 0256 * @param role item data role 0257 * @return true if successful 0258 */ 0259 bool FrameTableModel::setData(const QModelIndex& index, 0260 const QVariant& value, int role) 0261 { 0262 if (!index.isValid() || 0263 index.row() < 0 || index.row() >= static_cast<int>(frames().size()) || 0264 index.column() < 0 || index.column() >= CI_NumColumns) 0265 return false; 0266 if ((role == Qt::EditRole && index.column() == CI_Value) || 0267 role == ValueRole) { 0268 QString valueStr(value.toString()); 0269 if (auto it = frameAt(index.row()); valueStr != it->getValue()) { 0270 auto& frame = const_cast<Frame&>(*it); 0271 if (valueStr.isNull()) valueStr = QLatin1String(""); 0272 frame.setValueIfChanged(valueStr); 0273 emit dataChanged(index, index); 0274 0275 // Automatically set the checkbox when a value is changed 0276 if (!m_frameSelected.at(index.row())) { 0277 m_frameSelected[index.row()] = true; 0278 QModelIndex checkIndex(index.sibling(index.row(), CI_Enable)); 0279 emit dataChanged(checkIndex, checkIndex); 0280 } 0281 } 0282 return true; 0283 } 0284 if (role == Qt::CheckStateRole && index.column() == CI_Enable) { 0285 if (bool isChecked(value.toInt() == Qt::Checked); 0286 isChecked != m_frameSelected.at(index.row())) { 0287 m_frameSelected[index.row()] = isChecked; 0288 emit dataChanged(index, index); 0289 } 0290 return true; 0291 } 0292 return false; 0293 } 0294 0295 /** 0296 * Get data for header section. 0297 * @param section column or row 0298 * @param orientation horizontal or vertical 0299 * @param role item data role 0300 * @return header data for role 0301 */ 0302 QVariant FrameTableModel::headerData( 0303 int section, Qt::Orientation orientation, int role) const 0304 { 0305 if (role != Qt::DisplayRole || m_emptyHeaders) 0306 return QVariant(); 0307 if (orientation == Qt::Horizontal) { 0308 return section == CI_Enable ? tr("Name") : tr("Data"); 0309 } 0310 return section + 1; 0311 } 0312 0313 /** 0314 * Get number of rows. 0315 * @param parent parent model index, invalid for table models 0316 * @return number of rows, 0317 * if parent is valid number of children (0 for table models) 0318 */ 0319 int FrameTableModel::rowCount(const QModelIndex& parent) const 0320 { 0321 return parent.isValid() ? 0 : static_cast<int>(frames().size()); 0322 } 0323 0324 /** 0325 * Get number of columns. 0326 * @param parent parent model index, invalid for table models 0327 * @return number of columns, 0328 * if parent is valid number of children (0 for table models) 0329 */ 0330 int FrameTableModel::columnCount(const QModelIndex& parent) const 0331 { 0332 return parent.isValid() ? 0 : CI_NumColumns; 0333 } 0334 0335 /** 0336 * Insert rows. 0337 * @param count number of rows to insert 0338 * @return true if successful 0339 */ 0340 bool FrameTableModel::insertRows(int, int count, const QModelIndex&) 0341 { 0342 for (int i = 0; i < count; ++i) 0343 insertFrame(Frame()); 0344 return true; 0345 } 0346 0347 /** 0348 * Insert a frame. 0349 * @param frame frame to insert 0350 * @return true if successful 0351 */ 0352 void FrameTableModel::insertFrame(const Frame& frame) 0353 { 0354 auto it = m_frames.upper_bound(frame); 0355 int row = rowOf(it); 0356 beginInsertRows(QModelIndex(), row, row); 0357 it = m_frames.insert(it, frame); 0358 updateFrameRowMapping(); 0359 resizeFrameSelected(); 0360 endInsertRows(); 0361 } 0362 0363 /** 0364 * Remove rows. 0365 * @param row rows are removed starting with this row 0366 * @param count number of rows to remove 0367 * @return true if successful 0368 */ 0369 bool FrameTableModel::removeRows(int row, int count, 0370 const QModelIndex&) 0371 { 0372 if (count > 0) { 0373 beginRemoveRows(QModelIndex(), row, row + count - 1); 0374 for (int i = row; i < row + count; ++i) { 0375 m_frames.erase(frameAt(i)); 0376 } 0377 updateFrameRowMapping(); 0378 resizeFrameSelected(); 0379 endRemoveRows(); 0380 } 0381 return true; 0382 } 0383 0384 /** 0385 * Map role identifiers to role property names in scripting languages. 0386 * @return hash mapping role identifiers to names. 0387 */ 0388 QHash<int,QByteArray> FrameTableModel::roleNames() const 0389 { 0390 static QHash<int, QByteArray> roles = getRoleHash(); 0391 return roles; 0392 } 0393 0394 /** 0395 * Get the frame at a specific position in the collection. 0396 * @param row position of frame 0397 * @return iterator to frame 0398 */ 0399 FrameCollection::iterator FrameTableModel::frameAt(int row) const { 0400 return row >= 0 && row < m_frameOfRow.size() 0401 ? m_frameOfRow.at(row) : frames().end(); 0402 } 0403 0404 /** 0405 * Get the row corresponding to a frame iterator. 0406 * @param frameIt frame iterator 0407 * @return row number, number of rows if not found. 0408 */ 0409 int FrameTableModel::rowOf(FrameCollection::iterator frameIt) const { 0410 int row = 0; 0411 for (auto it = m_frameOfRow.constBegin(); it != m_frameOfRow.constEnd(); ++it) { 0412 if (frameIt == *it) 0413 break; 0414 ++row; 0415 } 0416 return row; 0417 } 0418 0419 /** 0420 * Mark rows. 0421 * @param rowMask mask with bits of rows to mark 0422 */ 0423 void FrameTableModel::markRows(quint64 rowMask) 0424 { 0425 quint64 changedBits = m_markedRows ^ rowMask; 0426 m_markedRows = rowMask; 0427 0428 // Emit a change signal for all indexes affected by the change. 0429 if (!changedBits) 0430 return; 0431 0432 quint64 mask; 0433 int row; 0434 for (mask = 1ULL, row = 0; 0435 static_cast<unsigned>(row) < sizeof(changedBits) * 8; 0436 mask <<= 1, ++row) { 0437 if ((changedBits & mask) != 0) { 0438 // Include both the columns for Qt::BackgroundRole and TruncatedRole. 0439 emit dataChanged(index(row, 0), index(row, 1)); 0440 } 0441 } 0442 } 0443 0444 /** 0445 * Mark changed frames. 0446 * @param types frame types to mark 0447 */ 0448 void FrameTableModel::markChangedFrames(const QList<Frame::ExtendedType>& types) 0449 { 0450 quint64 mask = 0; 0451 QSet<QString> changedOtherFrameNames; 0452 changedOtherFrameNames.clear(); 0453 for (const auto& extendedType : types) { 0454 Frame::Type type = extendedType.getType(); 0455 mask |= 1ULL << type; 0456 if (type == Frame::FT_Other) { 0457 if (const QString internalName = extendedType.getInternalName(); 0458 !internalName.isEmpty()) { 0459 changedOtherFrameNames.insert(internalName); 0460 } 0461 } 0462 } 0463 0464 quint64 changedBits = m_changedFrames ^ mask; 0465 m_changedFrames = mask; 0466 QSet<QString> addedNames = changedOtherFrameNames - m_changedOtherFrameNames; 0467 QSet<QString> removedNames = m_changedOtherFrameNames - changedOtherFrameNames; 0468 m_changedOtherFrameNames.swap(changedOtherFrameNames); 0469 0470 // Emit a change signal for all indexes affected by the change. 0471 if (!FileConfig::instance().markChanges() || 0472 (!changedBits && addedNames.isEmpty() && removedNames.isEmpty())) 0473 return; 0474 0475 const FrameCollection& frameCollection = frames(); 0476 auto it = frameCollection.cbegin(); 0477 int row = 0; 0478 for (; it != frameCollection.cend(); ++it, ++row) { 0479 Frame::ExtendedType extendedType = it->getExtendedType(); 0480 if (Frame::Type type = extendedType.getType(); type != Frame::FT_Other) { 0481 if (it->isValueChanged() || 0482 (static_cast<unsigned>(type) < sizeof(changedBits) * 8 && 0483 (changedBits & (1ULL << type)) != 0)) { 0484 QModelIndex idx = index(row, CI_Enable); 0485 emit dataChanged(idx, idx); 0486 } 0487 } else { 0488 if (const QString name = extendedType.getInternalName(); 0489 it->isValueChanged() || 0490 addedNames.contains(name) || removedNames.contains(name)) { 0491 QModelIndex idx = index(row, CI_Enable); 0492 emit dataChanged(idx, idx); 0493 } 0494 } 0495 } 0496 } 0497 0498 /** 0499 * Get frame for index. 0500 * @param index model index 0501 * @return frame, 0 if no frame. 0502 */ 0503 const Frame* FrameTableModel::getFrameOfIndex(const QModelIndex& index) const 0504 { 0505 if (index.isValid() && index.row() < static_cast<int>(frames().size())) { 0506 auto it = frameAt(index.row()); 0507 return &(*it); 0508 } 0509 return nullptr; 0510 } 0511 0512 /** 0513 * Get row with frame with a specific frame index. 0514 * @param index frame index 0515 * @return row number, -1 if not found. 0516 */ 0517 int FrameTableModel::getRowWithFrameIndex(int index) const 0518 { 0519 int row = 0; 0520 for (auto it = m_frameOfRow.constBegin(); it != m_frameOfRow.constEnd(); ++it) { 0521 if ((*it)->getIndex() == index) { 0522 return row; 0523 } 0524 ++row; 0525 } 0526 return -1; 0527 } 0528 0529 /** 0530 * Get row with frame with a specific frame name. 0531 * @param name name of frame 0532 * @return row number, -1 if not found. 0533 */ 0534 int FrameTableModel::getRowWithFrameName(const QString& name) const 0535 { 0536 int row = 0; 0537 for (auto it = m_frameOfRow.constBegin(); it != m_frameOfRow.constEnd(); ++it) { 0538 if ((*it)->getName() == name) { 0539 return row; 0540 } 0541 ++row; 0542 } 0543 return -1; 0544 } 0545 0546 /** 0547 * Get filter with enabled frames. 0548 * 0549 * @param allDisabledToAllEnabled true to enable all if all are disabled 0550 * 0551 * @return filter with enabled frames. 0552 */ 0553 FrameFilter FrameTableModel::getEnabledFrameFilter( 0554 bool allDisabledToAllEnabled) const 0555 { 0556 FrameFilter filter; 0557 filter.enableAll(); 0558 bool allDisabled = true; 0559 int numberRows = rowCount(); 0560 int row = 0; 0561 for (auto it = m_frameOfRow.constBegin(); it != m_frameOfRow.constEnd(); ++it) { 0562 if (row >= numberRows) break; 0563 if (!m_frameSelected.at(row)) { 0564 filter.enable((*it)->getType(), (*it)->getName(), false); 0565 } else { 0566 allDisabled = false; 0567 } 0568 ++row; 0569 } 0570 if (allDisabledToAllEnabled && allDisabled) { 0571 filter.enableAll(); 0572 } 0573 return filter; 0574 } 0575 0576 /** 0577 * Get enabled frames. 0578 * @return frame collection with enabled frames. 0579 */ 0580 FrameCollection FrameTableModel::getEnabledFrames() const 0581 { 0582 FrameCollection enabledFrames; 0583 const int numberRows = m_frameSelected.size(); 0584 int row = 0; 0585 for (auto it = m_frameOfRow.constBegin(); it != m_frameOfRow.constEnd(); ++it) { 0586 if (row >= numberRows) break; 0587 if (m_frameSelected.at(row)) { 0588 enabledFrames.insert(**it); 0589 } 0590 ++row; 0591 } 0592 return enabledFrames; 0593 } 0594 0595 /** 0596 * Clear frame collection. 0597 */ 0598 void FrameTableModel::clearFrames() 0599 { 0600 if (const int numFrames = static_cast<int>(m_frames.size()); numFrames > 0) { 0601 beginRemoveRows(QModelIndex(), 0, numFrames - 1); 0602 m_frames.clear(); 0603 updateFrameRowMapping(); 0604 m_frameSelected.clear(); 0605 endRemoveRows(); 0606 } 0607 } 0608 0609 /** 0610 * Transfer frames to frame collection. 0611 * @param src frames to move into frame collection, will be cleared 0612 */ 0613 void FrameTableModel::transferFrames(FrameCollection& src) 0614 { 0615 int oldNumFrames = static_cast<int>(m_frames.size()); 0616 int newNumFrames = static_cast<int>(src.size()); 0617 int numRowsChanged = qMin(oldNumFrames, newNumFrames); 0618 if (newNumFrames < oldNumFrames) 0619 beginRemoveRows(QModelIndex(), newNumFrames, oldNumFrames - 1); 0620 else if (newNumFrames > oldNumFrames) 0621 beginInsertRows(QModelIndex(), oldNumFrames, newNumFrames - 1); 0622 0623 m_frames.clear(); 0624 src.swap(m_frames); 0625 updateFrameRowMapping(); 0626 resizeFrameSelected(); 0627 0628 if (newNumFrames < oldNumFrames) 0629 endRemoveRows(); 0630 else if (newNumFrames > oldNumFrames) 0631 endInsertRows(); 0632 if (numRowsChanged > 0) 0633 emit dataChanged(index(0, 0), index(numRowsChanged - 1, CI_NumColumns - 1)); 0634 } 0635 0636 /** 0637 * Start filtering different values. 0638 */ 0639 void FrameTableModel::beginFilterDifferent() 0640 { 0641 m_differentValues.clear(); 0642 } 0643 0644 /** 0645 * End filtering different values. 0646 */ 0647 void FrameTableModel::endFilterDifferent() 0648 { 0649 } 0650 0651 /** 0652 * Get the different values which have been filtered for a frame type. 0653 * @param type frame type 0654 * @return different values. 0655 */ 0656 QSet<QString> FrameTableModel::getCompletionsForType( 0657 Frame::ExtendedType type) const 0658 { 0659 return m_differentValues.value(type); 0660 } 0661 0662 /** 0663 * Set values which are different inactive. 0664 * 0665 * @param others frames to compare, will be modified 0666 */ 0667 void FrameTableModel::filterDifferent(FrameCollection& others) 0668 { 0669 int oldNumFrames = static_cast<int>(m_frames.size()); 0670 0671 m_frames.filterDifferent(others, &m_differentValues); 0672 updateFrameRowMapping(); 0673 resizeFrameSelected(); 0674 0675 if (oldNumFrames > 0) 0676 emit dataChanged(index(0, 0), index(oldNumFrames - 1, CI_NumColumns - 1)); 0677 if (int newNumFrames = static_cast<int>(m_frames.size()); 0678 newNumFrames > oldNumFrames) { 0679 beginInsertRows(QModelIndex(), oldNumFrames, newNumFrames - 1); 0680 endInsertRows(); 0681 } 0682 } 0683 0684 /** 0685 * Set the check state of all frames in the table. 0686 * 0687 * @param checked true to check the frames 0688 */ 0689 void FrameTableModel::setAllCheckStates(bool checked) 0690 { 0691 const int numRows = rowCount(); 0692 m_frameSelected.fill(checked, 0, numRows); 0693 emit dataChanged(index(0, CI_Enable), index(numRows - 1, CI_Enable)); 0694 } 0695 0696 /** 0697 * Select all frames in the table. 0698 */ 0699 void FrameTableModel::selectAllFrames() 0700 { 0701 setAllCheckStates(true); 0702 } 0703 0704 /** 0705 * Deselect all frames in the table. 0706 */ 0707 void FrameTableModel::deselectAllFrames() 0708 { 0709 setAllCheckStates(false); 0710 } 0711 0712 /** 0713 * Select changed frames in the table. 0714 */ 0715 void FrameTableModel::selectChangedFrames() 0716 { 0717 int row = 0; 0718 auto it = m_frameOfRow.constBegin(); 0719 for (; row < m_frameSelected.size() && it != m_frameOfRow.constEnd(); 0720 ++row, ++it) { 0721 if ((*it)->isValueChanged()) { 0722 m_frameSelected[row] = true; 0723 QModelIndex idx = index(row, CI_Enable); 0724 emit dataChanged(idx, idx); 0725 } 0726 } 0727 } 0728 0729 /** 0730 * Resize the bit array with the frame selection to match the frames size. 0731 */ 0732 void FrameTableModel::resizeFrameSelected() 0733 { 0734 // If all bits are set, set also the new bits. 0735 int oldSize = m_frameSelected.size(); 0736 int newSize = static_cast<int>(frames().size()); 0737 bool setNewBits = newSize > oldSize && oldSize > 0 && 0738 m_frameSelected.count(true) == oldSize; 0739 0740 m_frameSelected.resize(newSize); 0741 0742 if (setNewBits) { 0743 for (int i = oldSize; i < newSize; ++i) { 0744 m_frameSelected.setBit(i, true); 0745 } 0746 } 0747 } 0748 0749 /** 0750 * Update the frame to row mapping. 0751 */ 0752 void FrameTableModel::updateFrameRowMapping() 0753 { 0754 const FrameCollection& frameCollection = frames(); 0755 m_frameOfRow.resize(static_cast<int>(frameCollection.size())); 0756 auto frameIt = frameCollection.cbegin(); 0757 auto rowIt = m_frameOfRow.begin(); // clazy:exclude=detaching-member 0758 for (; frameIt != frameCollection.cend(); ++frameIt, ++rowIt) { 0759 *rowIt = frameIt; 0760 } 0761 if (!m_frameTypeSeqNr.isEmpty()) { 0762 const QVector<int>& frameTypeSeqNr = m_frameTypeSeqNr; 0763 std::stable_sort(m_frameOfRow.begin(), m_frameOfRow.end(), // clazy:exclude=detaching-member 0764 [&frameTypeSeqNr](FrameCollection::iterator lhs, 0765 FrameCollection::iterator rhs) { 0766 int lhsType = lhs->getType(); 0767 int rhsType = rhs->getType(); 0768 int lhsSeqNr = frameTypeSeqNr.at(lhsType); 0769 int rhsSeqNr = frameTypeSeqNr.at(rhsType); 0770 return lhsSeqNr < rhsSeqNr || 0771 (lhsType == Frame::FT_Other && lhsType == rhsType && 0772 lhs->getInternalName() < rhs->getInternalName()); 0773 }); 0774 } 0775 } 0776 0777 /** 0778 * Set order of frames in frame table. 0779 * @param frameTypes ordered sequence of frame types 0780 * @remark This order is not used for ID3v1 frames. 0781 * @see TagConfig::quickAccessFrameOrder(). 0782 */ 0783 void FrameTableModel::setFrameOrder(const QList<int>& frameTypes) 0784 { 0785 if (frameTypes.isEmpty()) { 0786 m_frameTypeSeqNr.clear(); 0787 return; 0788 } 0789 if (frameTypes.size() < Frame::FT_Custom1) { 0790 qWarning("FrameTableModel::setFrameOrder: Invalid parameter size"); 0791 m_frameTypeSeqNr.clear(); 0792 return; 0793 } 0794 m_frameTypeSeqNr.resize(Frame::FT_UnknownFrame + 1); 0795 m_frameTypeSeqNr[Frame::FT_UnknownFrame] = Frame::FT_UnknownFrame; 0796 m_frameTypeSeqNr[Frame::FT_Other] = Frame::FT_Other; 0797 0798 int seqNr = 0; 0799 auto it = frameTypes.constBegin(); 0800 for (; it != frameTypes.constEnd(); ++it, ++seqNr) { 0801 int frameType = *it; 0802 if (frameType < 0 || frameType > Frame::FT_LastFrame) { 0803 qWarning("FrameTableModel::setFrameOrder: Invalid frame type %d", 0804 frameType); 0805 m_frameTypeSeqNr.clear(); 0806 return; 0807 } 0808 m_frameTypeSeqNr[frameType] = seqNr; 0809 } 0810 while (seqNr <= Frame::FT_LastFrame) { 0811 m_frameTypeSeqNr[seqNr] = seqNr; 0812 ++seqNr; 0813 } 0814 }