File indexing completed on 2024-05-12 04:52:09
0001 /* 0002 * dvbchannel.cpp 0003 * 0004 * Copyright (C) 2007-2011 Christoph Pfister <christophpfister@gmail.com> 0005 * 0006 * This program is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU General Public License as published by 0008 * the Free Software Foundation; either version 2 of the License, or 0009 * (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License along 0017 * with this program; if not, write to the Free Software Foundation, Inc., 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 0019 */ 0020 0021 #include "../log.h" 0022 0023 #include <QDataStream> 0024 #include <QFile> 0025 #include <QStandardPaths> 0026 #include <QVariant> 0027 0028 #include "../ensurenopendingoperation.h" 0029 #include "dvbchannel.h" 0030 #include "dvbsi.h" 0031 0032 bool DvbChannel::validate() 0033 { 0034 if (!name.isEmpty() && (number >= 1) && !source.isEmpty() && transponder.isValid() && 0035 (networkId >= -1) && (networkId <= 0xffff) && (transportStreamId >= 0) && 0036 (transportStreamId <= 0xffff) && (pmtPid >= 0) && (pmtPid <= 0x1fff) && 0037 (audioPid >= -1) && (audioPid <= 0x1fff)) { 0038 if ((serviceId >= 0) && (serviceId <= 0xffff)) { 0039 if (pmtSectionData.size() < 5) { 0040 pmtSectionData = QByteArray(5, 0); 0041 } 0042 0043 pmtSectionData[3] = ((serviceId >> 8) & 0xff); 0044 pmtSectionData[4] = (serviceId & 0xff); 0045 return true; 0046 } else if (pmtSectionData.size() >= 5) { 0047 serviceId = ((static_cast<unsigned char>(pmtSectionData.at(3)) << 8) | 0048 static_cast<unsigned char>(pmtSectionData.at(4))); 0049 return true; 0050 } 0051 } 0052 0053 return false; 0054 } 0055 0056 bool DvbChannelId::operator==(const DvbChannelId &other) const 0057 { 0058 if ((channel->source != other.channel->source) || 0059 (channel->transponder.getTransmissionType() != 0060 other.channel->transponder.getTransmissionType()) || 0061 (channel->networkId != other.channel->networkId)) { 0062 return false; 0063 } 0064 0065 switch (channel->transponder.getTransmissionType()) { 0066 case DvbTransponderBase::Invalid: 0067 break; 0068 case DvbTransponderBase::DvbC: 0069 case DvbTransponderBase::DvbS: 0070 case DvbTransponderBase::DvbS2: 0071 case DvbTransponderBase::DvbT: 0072 case DvbTransponderBase::DvbT2: 0073 case DvbTransponderBase::IsdbT: 0074 return ((channel->transportStreamId == other.channel->transportStreamId) && 0075 (channel->serviceId == other.channel->serviceId)); 0076 case DvbTransponderBase::Atsc: 0077 // source id has to be unique only within a transport stream 0078 // --> we need to check transponder as well 0079 return channel->transponder.corresponds(other.channel->transponder); 0080 } 0081 0082 return false; 0083 } 0084 0085 uint qHash(const DvbChannelId &channel) 0086 { 0087 uint hash = (qHash(channel.channel->source) ^ qHash(channel.channel->networkId)); 0088 0089 switch (channel.channel->transponder.getTransmissionType()) { 0090 case DvbTransponderBase::Invalid: 0091 break; 0092 case DvbTransponderBase::DvbC: 0093 case DvbTransponderBase::DvbS: 0094 case DvbTransponderBase::DvbS2: 0095 case DvbTransponderBase::DvbT: 0096 case DvbTransponderBase::DvbT2: 0097 case DvbTransponderBase::IsdbT: 0098 hash ^= (qHash(channel.channel->transportStreamId) << 8); 0099 hash ^= (qHash(channel.channel->serviceId) << 16); 0100 break; 0101 case DvbTransponderBase::Atsc: 0102 break; 0103 } 0104 0105 return hash; 0106 } 0107 0108 DvbChannelModel::DvbChannelModel(QObject *parent) : QObject(parent), hasPendingOperation(false), 0109 isSqlModel(false) 0110 { 0111 } 0112 0113 DvbChannelModel::~DvbChannelModel() 0114 { 0115 if (hasPendingOperation) { 0116 qCWarning(logDvb, "Illegal recursive call"); 0117 } 0118 0119 if (isSqlModel) { 0120 sqlFlush(); 0121 } 0122 } 0123 0124 void DvbChannelModel::channelFlush() 0125 { 0126 if (isSqlModel) 0127 sqlFlush(); 0128 } 0129 0130 DvbChannelModel *DvbChannelModel::createSqlModel(QObject *parent) 0131 { 0132 DvbChannelModel *channelModel = new DvbChannelModel(parent); 0133 channelModel->isSqlModel = true; 0134 channelModel->sqlInit(QLatin1String("Channels"), 0135 QStringList() << QLatin1String("Name") << QLatin1String("Number") << QLatin1String("Source") << 0136 QLatin1String("Transponder") << QLatin1String("NetworkId") << QLatin1String("TransportStreamId") << 0137 QLatin1String("PmtPid") << QLatin1String("PmtSection") << QLatin1String("AudioPid") << 0138 QLatin1String("Flags")); 0139 0140 // compatibility code 0141 0142 QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/channels.dtv")); 0143 0144 if (!file.exists()) { 0145 return channelModel; 0146 } 0147 0148 if (!file.open(QIODevice::ReadOnly)) { 0149 qCWarning(logDvb, "Cannot open %s", qPrintable(file.fileName())); 0150 return channelModel; 0151 } 0152 0153 QDataStream stream(&file); 0154 stream.setVersion(QDataStream::Qt_4_4); 0155 0156 while (!stream.atEnd()) { 0157 DvbChannel channel; 0158 int type; 0159 stream >> type; 0160 stream >> channel.name; 0161 stream >> channel.number; 0162 stream >> channel.source; 0163 0164 switch (type) { 0165 case DvbTransponderBase::DvbC: 0166 channel.transponder = DvbTransponder(DvbTransponderBase::DvbC); 0167 channel.transponder.as<DvbCTransponder>()->readTransponder(stream); 0168 break; 0169 case DvbTransponderBase::DvbS: 0170 channel.transponder = DvbTransponder(DvbTransponderBase::DvbS); 0171 channel.transponder.as<DvbSTransponder>()->readTransponder(stream); 0172 break; 0173 case DvbTransponderBase::DvbS2: 0174 channel.transponder = DvbTransponder(DvbTransponderBase::DvbS2); 0175 channel.transponder.as<DvbS2Transponder>()->readTransponder(stream); 0176 break; 0177 case DvbTransponderBase::DvbT: 0178 channel.transponder = DvbTransponder(DvbTransponderBase::DvbT); 0179 channel.transponder.as<DvbTTransponder>()->readTransponder(stream); 0180 break; 0181 case DvbTransponderBase::DvbT2: 0182 channel.transponder = DvbTransponder(DvbTransponderBase::DvbT2); 0183 channel.transponder.as<DvbT2Transponder>()->readTransponder(stream); 0184 break; 0185 case DvbTransponderBase::Atsc: 0186 channel.transponder = DvbTransponder(DvbTransponderBase::Atsc); 0187 channel.transponder.as<AtscTransponder>()->readTransponder(stream); 0188 break; 0189 case DvbTransponderBase::IsdbT: 0190 channel.transponder = DvbTransponder(DvbTransponderBase::IsdbT); 0191 channel.transponder.as<IsdbTTransponder>()->readTransponder(stream); 0192 break; 0193 default: 0194 stream.setStatus(QDataStream::ReadCorruptData); 0195 break; 0196 } 0197 0198 stream >> channel.networkId; 0199 stream >> channel.transportStreamId; 0200 int serviceId; 0201 stream >> serviceId; 0202 stream >> channel.pmtPid; 0203 0204 stream >> channel.pmtSectionData; 0205 int videoPid; 0206 stream >> videoPid; 0207 stream >> channel.audioPid; 0208 0209 int flags; 0210 stream >> flags; 0211 channel.hasVideo = (videoPid >= 0); 0212 channel.isScrambled = (flags & 0x1) != 0; 0213 0214 if (stream.status() != QDataStream::Ok) { 0215 qCWarning(logDvb, "Invalid channels in file %s", qPrintable(file.fileName())); 0216 break; 0217 } 0218 0219 channelModel->addChannel(channel); 0220 } 0221 0222 // As we'll remove the old channel file, flush the DB content 0223 channelModel->channelFlush(); 0224 0225 if (!file.remove()) { 0226 qCWarning(logDvb, "Cannot remove '%s' from DB", qPrintable(file.fileName())); 0227 } 0228 0229 return channelModel; 0230 } 0231 0232 QMap<int, DvbSharedChannel> DvbChannelModel::getChannels() const 0233 { 0234 return channelNumbers; 0235 } 0236 0237 DvbSharedChannel DvbChannelModel::findChannelByName(const QString &channelName) const 0238 { 0239 return channelNames.value(channelName); 0240 } 0241 0242 bool DvbChannelModel::hasChannelByName(const QString &channelName) 0243 { 0244 if (channelNames.contains(channelName)) 0245 return true; 0246 return false; 0247 } 0248 0249 DvbSharedChannel DvbChannelModel::findChannelByNumber(int channelNumber) const 0250 { 0251 return channelNumbers.value(channelNumber); 0252 } 0253 0254 DvbSharedChannel DvbChannelModel::findChannelById(const DvbChannel &channel) const 0255 { 0256 return channelIds.value(DvbChannelId(&channel)); 0257 } 0258 0259 void DvbChannelModel::cloneFrom(DvbChannelModel *other) 0260 { 0261 if (!isSqlModel && other->isSqlModel && channelNumbers.isEmpty()) { 0262 if (hasPendingOperation) { 0263 qCWarning(logDvb, "Illegal recursive call"); 0264 return; 0265 } 0266 0267 EnsureNoPendingOperation ensureNoPendingOperation(hasPendingOperation); 0268 channelNames = other->channelNames; 0269 channelNumbers = other->channelNumbers; 0270 channelIds = other->channelIds; 0271 0272 foreach (const DvbSharedChannel &channel, channelNumbers) { 0273 emit channelAdded(channel); 0274 } 0275 } else if (isSqlModel && !other->isSqlModel) { 0276 QMultiMap<SqlKey, DvbSharedChannel> otherChannelKeys; 0277 0278 foreach (const DvbSharedChannel &channel, other->getChannels()) { 0279 otherChannelKeys.insert(*channel, channel); 0280 } 0281 0282 for (QMap<SqlKey, DvbSharedChannel>::ConstIterator it = channels.constBegin(); 0283 it != channels.constEnd();) { 0284 const DvbSharedChannel &channel = *it; 0285 ++it; 0286 DvbSharedChannel otherChannel = otherChannelKeys.take(*channel); 0287 0288 if (otherChannel.isValid()) { 0289 if (otherChannel != channel) { 0290 DvbChannel modifiedChannel(*otherChannel); 0291 updateChannel(channel, modifiedChannel); 0292 } 0293 } else { 0294 removeChannel(channel); 0295 } 0296 } 0297 0298 foreach (const DvbSharedChannel &channel, otherChannelKeys) { 0299 DvbChannel newChannel(*channel); 0300 addChannel(newChannel); 0301 } 0302 } else { 0303 qCWarning(logDvb, "Illegal type of clone"); 0304 } 0305 } 0306 0307 void DvbChannelModel::addChannel(DvbChannel &channel) 0308 { 0309 bool forceAdd; 0310 0311 if (channel.number < 1) { 0312 channel.number = 1; 0313 forceAdd = false; 0314 } else { 0315 forceAdd = true; 0316 } 0317 0318 if (!channel.validate()) { 0319 qCWarning(logDvb, "Invalid channel"); 0320 return; 0321 } 0322 0323 if (forceAdd) { 0324 DvbSharedChannel existingChannel = channelNames.value(channel.name); 0325 0326 if (existingChannel.isValid()) { 0327 DvbChannel updatedChannel = *existingChannel; 0328 updatedChannel.name = findNextFreeChannelName(updatedChannel.name); 0329 updateChannel(existingChannel, updatedChannel); 0330 } 0331 0332 existingChannel = channelNumbers.value(channel.number); 0333 0334 if (existingChannel.isValid()) { 0335 DvbChannel updatedChannel = *existingChannel; 0336 updatedChannel.number = findNextFreeChannelNumber(updatedChannel.number); 0337 updateChannel(existingChannel, updatedChannel); 0338 } 0339 } else { 0340 DvbSharedChannel existingChannel = channelIds.value(DvbChannelId(&channel)); 0341 0342 if (existingChannel.isValid()) { 0343 if (channel.name == extractBaseName(existingChannel->name)) { 0344 channel.name = existingChannel->name; 0345 } else { 0346 channel.name = findNextFreeChannelName(channel.name); 0347 } 0348 0349 channel.number = existingChannel->number; 0350 DvbPmtSection pmtSection(channel.pmtSectionData); 0351 DvbPmtParser pmtParser(pmtSection); 0352 0353 for (int i = 0; i < pmtParser.audioPids.size(); ++i) { 0354 if (pmtParser.audioPids.at(i).first == existingChannel->audioPid) { 0355 channel.audioPid = existingChannel->audioPid; 0356 break; 0357 } 0358 } 0359 0360 updateChannel(existingChannel, channel); 0361 return; 0362 } 0363 0364 channel.name = findNextFreeChannelName(channel.name); 0365 channel.number = findNextFreeChannelNumber(channel.number); 0366 } 0367 0368 if (hasPendingOperation) { 0369 qCWarning(logDvb, "Illegal recursive call"); 0370 return; 0371 } 0372 0373 EnsureNoPendingOperation ensureNoPendingOperation(hasPendingOperation); 0374 0375 if (isSqlModel) { 0376 channel.setSqlKey(sqlFindFreeKey(channels)); 0377 } else { 0378 channel.setSqlKey(SqlKey()); 0379 } 0380 0381 DvbSharedChannel newChannel(new DvbChannel(channel)); 0382 channelNames.insert(newChannel->name, newChannel); 0383 channelNumbers.insert(newChannel->number, newChannel); 0384 channelIds.insert(DvbChannelId(newChannel), newChannel); 0385 0386 if (isSqlModel) { 0387 channels.insert(*newChannel, newChannel); 0388 sqlInsert(*newChannel); 0389 } 0390 0391 emit channelAdded(newChannel); 0392 } 0393 0394 void DvbChannelModel::updateChannel(DvbSharedChannel channel, DvbChannel &modifiedChannel) 0395 { 0396 if (!channel.isValid() || (channelNumbers.value(channel->number) != channel) || 0397 !modifiedChannel.validate()) { 0398 qCWarning(logDvb, "Invalid channel"); 0399 return; 0400 } 0401 0402 if (channel->name != modifiedChannel.name) { 0403 DvbSharedChannel existingChannel = channelNames.value(modifiedChannel.name); 0404 0405 if (existingChannel.isValid()) { 0406 DvbChannel updatedChannel = *existingChannel; 0407 updatedChannel.name = findNextFreeChannelName(updatedChannel.name); 0408 updateChannel(existingChannel, updatedChannel); 0409 } 0410 } 0411 0412 if (channel->number != modifiedChannel.number) { 0413 DvbSharedChannel existingChannel = channelNumbers.value(modifiedChannel.number); 0414 0415 if (existingChannel.isValid()) { 0416 DvbChannel updatedChannel = *existingChannel; 0417 updatedChannel.number = findNextFreeChannelNumber(updatedChannel.number); 0418 updateChannel(existingChannel, updatedChannel); 0419 } 0420 } 0421 0422 if (hasPendingOperation) { 0423 qCWarning(logDvb, "Illegal recursive call"); 0424 return; 0425 } 0426 0427 EnsureNoPendingOperation ensureNoPendingOperation(hasPendingOperation); 0428 modifiedChannel.setSqlKey(*channel); 0429 bool channelNameChanged = (channel->name != modifiedChannel.name); 0430 bool channelNumberChanged = (channel->number != modifiedChannel.number); 0431 bool channelIdChanged = (DvbChannelId(channel) != DvbChannelId(&modifiedChannel)); 0432 emit channelAboutToBeUpdated(channel); 0433 0434 if (channelNameChanged) { 0435 channelNames.remove(channel->name); 0436 } 0437 0438 if (channelNumberChanged) { 0439 channelNumbers.remove(channel->number); 0440 } 0441 0442 if (channelIdChanged) { 0443 channelIds.remove(DvbChannelId(channel), channel); 0444 } 0445 0446 if (!isSqlModel && channel->isSqlKeyValid()) { 0447 DvbSharedChannel detachedChannel(new DvbChannel(modifiedChannel)); 0448 channelNames.insert(detachedChannel->name, detachedChannel); 0449 channelNumbers.insert(detachedChannel->number, detachedChannel); 0450 channelIds.insert(DvbChannelId(detachedChannel), detachedChannel); 0451 emit channelUpdated(detachedChannel); 0452 } else { 0453 *const_cast<DvbChannel *>(channel.constData()) = modifiedChannel; 0454 0455 if (channelNameChanged) { 0456 channelNames.insert(channel->name, channel); 0457 } 0458 0459 if (channelNumberChanged) { 0460 channelNumbers.insert(channel->number, channel); 0461 } 0462 0463 if (channelIdChanged) { 0464 channelIds.insert(DvbChannelId(channel), channel); 0465 } 0466 0467 if (isSqlModel) { 0468 sqlUpdate(*channel); 0469 } 0470 0471 emit channelUpdated(channel); 0472 } 0473 } 0474 0475 void DvbChannelModel::removeChannel(DvbSharedChannel channel) 0476 { 0477 if (!channel.isValid() || (channelNumbers.value(channel->number) != channel)) { 0478 qCWarning(logDvb, "Invalid channel"); 0479 return; 0480 } 0481 0482 if (hasPendingOperation) { 0483 qCWarning(logDvb, "Illegal recursive call"); 0484 return; 0485 } 0486 0487 EnsureNoPendingOperation ensureNoPendingOperation(hasPendingOperation); 0488 channelNames.remove(channel->name); 0489 channelNumbers.remove(channel->number); 0490 channelIds.remove(DvbChannelId(channel), channel); 0491 0492 if (isSqlModel) { 0493 channels.remove(*channel); 0494 sqlRemove(*channel); 0495 } 0496 0497 emit channelRemoved(channel); 0498 } 0499 0500 void DvbChannelModel::dndMoveChannels(const QList<DvbSharedChannel> &selectedChannels, 0501 int insertBeforeNumber) 0502 { 0503 if (hasPendingOperation) { 0504 qCWarning(logDvb, "Illegal recursive call"); 0505 return; 0506 } 0507 0508 EnsureNoPendingOperation ensureNoPendingOperation(hasPendingOperation); 0509 typedef QMap<int, DvbSharedChannel>::ConstIterator ConstIterator; 0510 QList<DvbSharedChannel> channelQueue; 0511 0512 foreach (const DvbSharedChannel &channel, selectedChannels) { 0513 if (channel.isValid()) { 0514 ConstIterator it = channelNumbers.constFind(channel->number); 0515 0516 if ((it != channelNumbers.constEnd()) && (*it == channel)) { 0517 channelNumbers.remove(channel->number); 0518 channelQueue.append(channel); 0519 } 0520 } 0521 } 0522 0523 ConstIterator it = channelNumbers.constFind(insertBeforeNumber); 0524 int currentNumber = 1; 0525 0526 if (it != channelNumbers.constBegin()) { 0527 currentNumber = ((it - 1).key() + 1); 0528 } 0529 0530 while (!channelQueue.isEmpty()) { 0531 DvbSharedChannel channel = channelQueue.takeFirst(); 0532 0533 if (channel->number != currentNumber) { 0534 emit channelAboutToBeUpdated(channel); 0535 DvbSharedChannel existingChannel = channelNumbers.take(currentNumber); 0536 0537 if (existingChannel.isValid()) { 0538 channelQueue.append(existingChannel); 0539 } 0540 0541 if (!isSqlModel && channel->isSqlKeyValid()) { 0542 DvbChannel *newChannel = new DvbChannel(*channel); 0543 newChannel->number = currentNumber; 0544 DvbSharedChannel detachedChannel(newChannel); 0545 channelNames.insert(detachedChannel->name, detachedChannel); 0546 channelNumbers.insert(detachedChannel->number, detachedChannel); 0547 channelIds.insert(DvbChannelId(detachedChannel), detachedChannel); 0548 emit channelUpdated(detachedChannel); 0549 } else { 0550 const_cast<DvbChannel *>(channel.constData())->number = 0551 currentNumber; 0552 channelNumbers.insert(channel->number, channel); 0553 0554 if (isSqlModel) { 0555 sqlUpdate(*channel); 0556 } 0557 0558 emit channelUpdated(channel); 0559 } 0560 } else { 0561 channelNumbers.insert(channel->number, channel); 0562 } 0563 0564 ++currentNumber; 0565 } 0566 } 0567 0568 void DvbChannelModel::bindToSqlQuery(SqlKey sqlKey, QSqlQuery &query, int index) const 0569 { 0570 DvbSharedChannel channel = channels.value(sqlKey); 0571 0572 if (!channel.isValid()) { 0573 qCWarning(logDvb, "Invalid channel"); 0574 return; 0575 } 0576 0577 query.bindValue(index++, channel->name); 0578 query.bindValue(index++, channel->number); 0579 query.bindValue(index++, channel->source); 0580 query.bindValue(index++, channel->transponder.toString()); 0581 query.bindValue(index++, channel->networkId); 0582 query.bindValue(index++, channel->transportStreamId); 0583 query.bindValue(index++, channel->pmtPid); 0584 query.bindValue(index++, channel->pmtSectionData); 0585 query.bindValue(index++, channel->audioPid); 0586 query.bindValue(index++, (channel->hasVideo ? 0x01 : 0) | 0587 (channel->isScrambled ? 0x02 : 0)); 0588 } 0589 0590 bool DvbChannelModel::insertFromSqlQuery(SqlKey sqlKey, const QSqlQuery &query, int index) 0591 { 0592 DvbChannel *channel = new DvbChannel(); 0593 DvbSharedChannel sharedChannel(channel); 0594 channel->setSqlKey(sqlKey); 0595 channel->name = query.value(index++).toString(); 0596 channel->number = query.value(index++).toInt(); 0597 channel->source = query.value(index++).toString(); 0598 channel->transponder = DvbTransponder::fromString(query.value(index++).toString()); 0599 channel->networkId = query.value(index++).toInt(); 0600 channel->transportStreamId = query.value(index++).toInt(); 0601 channel->pmtPid = query.value(index++).toInt(); 0602 channel->pmtSectionData = query.value(index++).toByteArray(); 0603 channel->audioPid = query.value(index++).toInt(); 0604 int flags = query.value(index++).toInt(); 0605 channel->hasVideo = ((flags & 0x01) != 0); 0606 channel->isScrambled = ((flags & 0x02) != 0); 0607 0608 if (channel->validate() && !channelNames.contains(channel->name) && 0609 !channelNumbers.contains(channel->number)) { 0610 channelNames.insert(sharedChannel->name, sharedChannel); 0611 channelNumbers.insert(sharedChannel->number, sharedChannel); 0612 channelIds.insert(DvbChannelId(sharedChannel), sharedChannel); 0613 channels.insert(*sharedChannel, sharedChannel); 0614 return true; 0615 } 0616 0617 return false; 0618 } 0619 0620 bool DvbChannelModel::areInTheSameBunch(DvbSharedChannel channel1, DvbSharedChannel channel2) 0621 { 0622 if (channel1->transportStreamId == channel2->transportStreamId) { 0623 return true; 0624 } 0625 0626 return false; 0627 } 0628 0629 QString DvbChannelModel::extractBaseName(const QString &name) const 0630 { 0631 QString baseName = name; 0632 int position = baseName.lastIndexOf(QLatin1Char('-')); 0633 0634 if (position > 0) { 0635 QString suffix = baseName.mid(position + 1); 0636 0637 if (suffix == QString::number(suffix.toInt())) { 0638 baseName.truncate(position); 0639 } 0640 } 0641 0642 return baseName; 0643 } 0644 0645 QString DvbChannelModel::findNextFreeChannelName(const QString &name) const 0646 { 0647 if (!channelNames.contains(name)) { 0648 return name; 0649 } 0650 0651 QString baseName = extractBaseName(name); 0652 int suffix = 0; 0653 QString newName = baseName; 0654 0655 while (channelNames.contains(newName)) { 0656 ++suffix; 0657 newName = baseName + QLatin1Char('-') + QString::number(suffix); 0658 } 0659 0660 return newName; 0661 } 0662 0663 int DvbChannelModel::findNextFreeChannelNumber(int number) const 0664 { 0665 while (channelNumbers.contains(number)) { 0666 ++number; 0667 } 0668 0669 return number; 0670 } 0671 0672 #include "moc_dvbchannel.cpp"