File indexing completed on 2024-04-14 04:38:30
0001 /* 0002 Copyright (C) 2006-2008 Matthias Kretz <kretz@kde.org> 0003 Copyright (C) 2011 Casian Andrei <skeletk13@gmail.com> 0004 Copyright (C) 2014-2019 Harald Sitter <sitter@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) version 3. 0010 0011 This library 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 GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "devicepreference.h" 0023 0024 #include <QDialogButtonBox> 0025 #include <QListWidget> 0026 #include <QLabel> 0027 #include <QMessageBox> 0028 #include <QPointer> 0029 #include <QStandardPaths> 0030 0031 #include <phonon/AudioOutput> 0032 #include <phonon/BackendCapabilities> 0033 #include <phonon/MediaObject> 0034 #include <phonon/VideoWidget> 0035 #include <phonon/globalconfig.h> 0036 #include <phonon/phononnamespace.h> 0037 0038 #ifndef METATYPE_QLIST_INT_DEFINED 0039 #define METATYPE_QLIST_INT_DEFINED 0040 // Want this exactly once, see phonondefs_p.h kcm/devicepreference.cpp 0041 Q_DECLARE_METATYPE(QList<int>) 0042 #endif 0043 0044 namespace Phonon { 0045 0046 /* 0047 * Lists of categories for every device type 0048 */ 0049 static const Category audioOutCategories[] = { 0050 NoCategory, 0051 NotificationCategory, 0052 MusicCategory, 0053 VideoCategory, 0054 CommunicationCategory, 0055 GameCategory, 0056 AccessibilityCategory, 0057 }; 0058 0059 static const CaptureCategory audioCapCategories[] = { 0060 NoCaptureCategory, 0061 CommunicationCaptureCategory, 0062 RecordingCaptureCategory, 0063 ControlCaptureCategory 0064 }; 0065 0066 static const CaptureCategory videoCapCategories[] = { 0067 NoCaptureCategory, 0068 CommunicationCaptureCategory, 0069 RecordingCaptureCategory, 0070 }; 0071 0072 static const int audioOutCategoriesCount = sizeof(audioOutCategories) / sizeof(Category); 0073 static const int audioCapCategoriesCount = sizeof(audioCapCategories) / sizeof(CaptureCategory); 0074 static const int videoCapCategoriesCount = sizeof(videoCapCategories) / sizeof(CaptureCategory); 0075 0076 void operator++(Category &c) 0077 { 0078 c = static_cast<Category>(1 + static_cast<int>(c)); 0079 //Q_ASSERT(c <= LastCategory); 0080 } 0081 0082 class CategoryItem : public QStandardItem { 0083 public: 0084 CategoryItem(Category cat) 0085 : QStandardItem(), 0086 m_cat(cat), 0087 m_odtype(AudioOutputDeviceType) 0088 { 0089 if (cat == NoCategory) { 0090 setText(QObject::tr("Audio Playback")); 0091 } else { 0092 setText(categoryToString(cat)); 0093 } 0094 } 0095 0096 CategoryItem(CaptureCategory cat, ObjectDescriptionType t = AudioCaptureDeviceType) 0097 : QStandardItem(), 0098 m_capcat(cat), 0099 m_odtype(t) 0100 { 0101 if (cat == NoCaptureCategory) { 0102 switch(t) { 0103 case AudioCaptureDeviceType: 0104 setText(QObject::tr("Audio Recording")); 0105 break; 0106 case VideoCaptureDeviceType: 0107 setText(QObject::tr("Video Recording")); 0108 break; 0109 default: 0110 setText(QObject::tr("Invalid")); 0111 } 0112 } else { 0113 setText(categoryToString(cat)); 0114 } 0115 } 0116 0117 int type() const override { return 1001; } 0118 Category category() const { return m_cat; } 0119 CaptureCategory captureCategory() const { return m_capcat; } 0120 ObjectDescriptionType odtype() const { return m_odtype; } 0121 0122 private: 0123 Category m_cat; 0124 CaptureCategory m_capcat; 0125 ObjectDescriptionType m_odtype; 0126 }; 0127 0128 /** 0129 * Need this to change the colors of the ListView if the Palette changed. With CSS set this won't 0130 * change automatically 0131 */ 0132 void DevicePreference::changeEvent(QEvent *e) 0133 { 0134 QWidget::changeEvent(e); 0135 if (e->type() == QEvent::PaletteChange) { 0136 deviceList->setStyleSheet(deviceList->styleSheet()); 0137 } 0138 } 0139 0140 DevicePreference::DevicePreference(QWidget *parent) 0141 : QWidget(parent), 0142 m_headerModel(0, 1, nullptr), 0143 m_media(nullptr), m_audioOutput(nullptr), m_videoWidget(nullptr) 0144 { 0145 setupUi(this); 0146 0147 // Setup the buttons 0148 testPlaybackButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); 0149 testPlaybackButton->setEnabled(false); 0150 testPlaybackButton->setToolTip(tr("Test the selected device")); 0151 deferButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); 0152 preferButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); 0153 0154 // Configure the device list 0155 deviceList->setDragDropMode(QAbstractItemView::InternalMove); 0156 deviceList->setStyleSheet(QStringLiteral("QTreeView {" 0157 "background-color: palette(base);" 0158 "background-image: url(:/phononsettings/listview-background.png);" 0159 "background-position: bottom left;" 0160 "background-attachment: fixed;" 0161 "background-repeat: no-repeat;" 0162 "background-clip: padding;" 0163 "}")); 0164 deviceList->setAlternatingRowColors(false); 0165 0166 // The root item for the categories 0167 QStandardItem *parentItem = m_categoryModel.invisibleRootItem(); 0168 0169 // Audio Output Parent 0170 QStandardItem *aOutputItem = new CategoryItem(NoCategory); 0171 m_audioOutputModel[NoCategory] = new AudioOutputDeviceModel(this); 0172 aOutputItem->setEditable(false); 0173 aOutputItem->setToolTip(tr("Defines the default ordering of devices which can be overridden by individual categories.")); 0174 parentItem->appendRow(aOutputItem); 0175 0176 // Audio Capture Parent 0177 QStandardItem *aCaptureItem = new CategoryItem(NoCaptureCategory, AudioCaptureDeviceType); 0178 m_audioCaptureModel[NoCaptureCategory] = new AudioCaptureDeviceModel(this); 0179 aCaptureItem->setEditable(false); 0180 aCaptureItem->setToolTip(tr("Defines the default ordering of devices which can be overridden by individual categories.")); 0181 parentItem->appendRow(aCaptureItem); 0182 0183 // Video Capture Parent 0184 QStandardItem *vCaptureItem = new CategoryItem(NoCaptureCategory, VideoCaptureDeviceType); 0185 m_videoCaptureModel[NoCaptureCategory] = new VideoCaptureDeviceModel(this); 0186 vCaptureItem->setEditable(false); 0187 vCaptureItem->setToolTip(tr("Defines the default ordering of devices which can be overridden by individual categories.")); 0188 parentItem->appendRow(vCaptureItem); 0189 0190 // Audio Output Children 0191 parentItem = aOutputItem; 0192 for (int i = 1; i < audioOutCategoriesCount; ++i) { // i == 1 to skip NoCategory 0193 m_audioOutputModel[audioOutCategories[i]] = new AudioOutputDeviceModel(this); 0194 QStandardItem *item = new CategoryItem(audioOutCategories[i]); 0195 item->setEditable(false); 0196 parentItem->appendRow(item); 0197 } 0198 0199 // Audio Capture Children 0200 parentItem = aCaptureItem; 0201 for (int i = 1; i < audioCapCategoriesCount; ++i) { // i == 1 to skip NoCategory 0202 m_audioCaptureModel[audioCapCategories[i]] = new AudioCaptureDeviceModel(this); 0203 QStandardItem *item = new CategoryItem(audioCapCategories[i], AudioCaptureDeviceType); 0204 item->setEditable(false); 0205 parentItem->appendRow(item); 0206 } 0207 0208 // Video Capture Children 0209 parentItem = vCaptureItem; 0210 for (int i = 1; i < videoCapCategoriesCount; ++i) { // i == 1 to skip NoCategory 0211 m_videoCaptureModel[videoCapCategories[i]] = new VideoCaptureDeviceModel(this); 0212 QStandardItem *item = new CategoryItem(videoCapCategories[i], VideoCaptureDeviceType); 0213 item->setEditable(false); 0214 parentItem->appendRow(item); 0215 } 0216 0217 // Configure the category tree 0218 categoryTree->setModel(&m_categoryModel); 0219 if (categoryTree->header()) { 0220 categoryTree->header()->hide(); 0221 } 0222 categoryTree->expandAll(); 0223 0224 connect(categoryTree->selectionModel(), SIGNAL(currentChanged(const QModelIndex &,const QModelIndex &)), 0225 SLOT(updateDeviceList())); 0226 0227 // Connect all model data change signals to the changed slot 0228 for (int i = -1; i <= LastCategory; ++i) { 0229 connect(m_audioOutputModel[i], SIGNAL(rowsInserted(QModelIndex, int, int)), this, SIGNAL(changed())); 0230 connect(m_audioOutputModel[i], SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SIGNAL(changed())); 0231 connect(m_audioOutputModel[i], SIGNAL(layoutChanged()), this, SIGNAL(changed())); 0232 connect(m_audioOutputModel[i], SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SIGNAL(changed())); 0233 if (m_audioCaptureModel.contains(i)) { 0234 connect(m_audioCaptureModel[i], SIGNAL(rowsInserted(QModelIndex, int, int)), this, SIGNAL(changed())); 0235 connect(m_audioCaptureModel[i], SIGNAL(rowsRemoved(QModelIndex , int, int)), this, SIGNAL(changed())); 0236 connect(m_audioCaptureModel[i], SIGNAL(layoutChanged()), this, SIGNAL(changed())); 0237 connect(m_audioCaptureModel[i], SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SIGNAL(changed())); 0238 } 0239 if (m_videoCaptureModel.contains(i)) { 0240 connect(m_videoCaptureModel[i], SIGNAL(rowsInserted(QModelIndex, int, int)), this, SIGNAL(changed())); 0241 connect(m_videoCaptureModel[i], SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SIGNAL(changed())); 0242 connect(m_videoCaptureModel[i], SIGNAL(layoutChanged()), this, SIGNAL(changed())); 0243 connect(m_videoCaptureModel[i], SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SIGNAL(changed())); 0244 } 0245 } 0246 0247 connect(showAdvancedDevicesCheckBox, &QCheckBox::stateChanged, this, &DevicePreference::changed); 0248 0249 // Connect the signals from Phonon that notify changes in the device lists 0250 connect(BackendCapabilities::notifier(), SIGNAL(availableAudioOutputDevicesChanged()), SLOT(updateAudioOutputDevices())); 0251 connect(BackendCapabilities::notifier(), SIGNAL(availableAudioCaptureDevicesChanged()), SLOT(updateAudioCaptureDevices())); 0252 connect(BackendCapabilities::notifier(), SIGNAL(availableVideoCaptureDevicesChanged()), SLOT(updateVideoCaptureDevices())); 0253 connect(BackendCapabilities::notifier(), SIGNAL(capabilitiesChanged()), SLOT(updateAudioOutputDevices())); 0254 connect(BackendCapabilities::notifier(), SIGNAL(capabilitiesChanged()), SLOT(updateAudioCaptureDevices())); 0255 connect(BackendCapabilities::notifier(), SIGNAL(capabilitiesChanged()), SLOT(updateVideoCaptureDevices())); 0256 0257 if (!categoryTree->currentIndex().isValid()) { 0258 categoryTree->setCurrentIndex(m_categoryModel.index(1, 0, m_categoryModel.index(0, 0))); 0259 } 0260 } 0261 0262 DevicePreference::~DevicePreference() 0263 { 0264 // Ensure that the video widget is destroyed, if it remains active 0265 delete m_videoWidget; 0266 } 0267 0268 void DevicePreference::updateDeviceList() 0269 { 0270 // Temporarily disconnect the device list selection model 0271 if (deviceList->selectionModel()) { 0272 disconnect(deviceList->selectionModel(), 0273 SIGNAL(currentRowChanged(const QModelIndex &,const QModelIndex &)), 0274 this, SLOT(updateButtonsEnabled())); 0275 } 0276 0277 // Get the current selected category item 0278 QStandardItem *currentItem = m_categoryModel.itemFromIndex(categoryTree->currentIndex()); 0279 if (currentItem && currentItem->type() == 1001) { 0280 CategoryItem *catItem = static_cast<CategoryItem *>(currentItem); 0281 bool cap = catItem->odtype() != AudioOutputDeviceType; 0282 const Category cat = catItem->category(); 0283 const CaptureCategory capcat = catItem->captureCategory(); 0284 0285 // Update the device list, by setting it's model to the one for the corresponding category 0286 switch (catItem->odtype()) { 0287 case AudioOutputDeviceType: 0288 deviceList->setModel(m_audioOutputModel[cat]); 0289 break; 0290 case AudioCaptureDeviceType: 0291 deviceList->setModel(m_audioCaptureModel[capcat]); 0292 break; 0293 case VideoCaptureDeviceType: 0294 deviceList->setModel(m_videoCaptureModel[capcat]); 0295 break; 0296 default: ; 0297 } 0298 0299 // Update the header 0300 if (cap ? capcat == NoCaptureCategory : cat == NoCategory) { 0301 switch (catItem->odtype()) { 0302 case AudioOutputDeviceType: 0303 m_headerModel.setHeaderData(0, Qt::Horizontal, tr("Default Audio Playback Device Preference"), Qt::DisplayRole); 0304 break; 0305 case AudioCaptureDeviceType: 0306 m_headerModel.setHeaderData(0, Qt::Horizontal, tr("Default Audio Recording Device Preference"), Qt::DisplayRole); 0307 break; 0308 case VideoCaptureDeviceType: 0309 m_headerModel.setHeaderData(0, Qt::Horizontal, tr("Default Video Recording Device Preference"), Qt::DisplayRole); 0310 break; 0311 default: ; 0312 } 0313 } else { 0314 switch (catItem->odtype()) { 0315 case AudioOutputDeviceType: 0316 m_headerModel.setHeaderData(0, Qt::Horizontal, tr("Audio Playback Device Preference for the '%1' Category").arg( 0317 categoryToString(cat)), Qt::DisplayRole); 0318 break; 0319 case AudioCaptureDeviceType: 0320 m_headerModel.setHeaderData(0, Qt::Horizontal, tr("Audio Recording Device Preference for the '%1' Category").arg( 0321 categoryToString(capcat)), Qt::DisplayRole); 0322 break; 0323 case VideoCaptureDeviceType: 0324 m_headerModel.setHeaderData(0, Qt::Horizontal, tr("Video Recording Device Preference for the '%1' Category ").arg( 0325 categoryToString(capcat)), Qt::DisplayRole); 0326 break; 0327 default: ; 0328 } 0329 } 0330 } else { 0331 // No valid category selected 0332 m_headerModel.setHeaderData(0, Qt::Horizontal, QString(), Qt::DisplayRole); 0333 deviceList->setModel(nullptr); 0334 } 0335 0336 // Update the header, the buttons enabled state 0337 deviceList->header()->setModel(&m_headerModel); 0338 updateButtonsEnabled(); 0339 0340 // Reconnect the device list selection model 0341 if (deviceList->selectionModel()) { 0342 connect(deviceList->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &,const QModelIndex &)), 0343 this, SLOT(updateButtonsEnabled())); 0344 } 0345 0346 deviceList->resizeColumnToContents(0); 0347 } 0348 0349 void DevicePreference::updateAudioCaptureDevices() 0350 { 0351 const QList<AudioCaptureDevice> list = availableAudioCaptureDevices(); 0352 QHash<int, AudioCaptureDevice> hash; 0353 foreach (const AudioCaptureDevice &dev, list) { 0354 hash.insert(dev.index(), dev); 0355 } 0356 0357 for (int catIndex = 0; catIndex < audioCapCategoriesCount; ++ catIndex) { 0358 const int i = audioCapCategories[catIndex]; 0359 AudioCaptureDeviceModel *model = m_audioCaptureModel.value(i); 0360 Q_ASSERT(model); 0361 0362 QHash<int, AudioCaptureDevice> hashCopy(hash); 0363 QList<AudioCaptureDevice> orderedList; 0364 0365 if (model->rowCount() > 0) { 0366 QList<int> order = model->tupleIndexOrder(); 0367 foreach (int idx, order) { 0368 if (hashCopy.contains(idx)) { 0369 orderedList << hashCopy.take(idx); 0370 } 0371 } 0372 0373 if (hashCopy.size() > 1) { 0374 // keep the order of the original list 0375 foreach (const AudioCaptureDevice &dev, list) { 0376 if (hashCopy.contains(dev.index())) { 0377 orderedList << hashCopy.take(dev.index()); 0378 } 0379 } 0380 } else if (hashCopy.size() == 1) { 0381 orderedList += hashCopy.values(); 0382 } 0383 0384 model->setModelData(orderedList); 0385 } else { 0386 model->setModelData(list); 0387 } 0388 } 0389 0390 deviceList->resizeColumnToContents(0); 0391 } 0392 0393 void DevicePreference::updateVideoCaptureDevices() 0394 { 0395 const QList<VideoCaptureDevice> list = availableVideoCaptureDevices(); 0396 QHash<int, VideoCaptureDevice> hash; 0397 foreach (const VideoCaptureDevice &dev, list) { 0398 hash.insert(dev.index(), dev); 0399 } 0400 0401 for (int catIndex = 0; catIndex < videoCapCategoriesCount; ++ catIndex) { 0402 const int i = videoCapCategories[catIndex]; 0403 VideoCaptureDeviceModel *model = m_videoCaptureModel.value(i); 0404 Q_ASSERT(model); 0405 0406 QHash<int, VideoCaptureDevice> hashCopy(hash); 0407 QList<VideoCaptureDevice> orderedList; 0408 0409 if (model->rowCount() > 0) { 0410 QList<int> order = model->tupleIndexOrder(); 0411 foreach (int idx, order) { 0412 if (hashCopy.contains(idx)) { 0413 orderedList << hashCopy.take(idx); 0414 } 0415 } 0416 0417 if (hashCopy.size() > 1) { 0418 // keep the order of the original list 0419 foreach (const VideoCaptureDevice &dev, list) { 0420 if (hashCopy.contains(dev.index())) { 0421 orderedList << hashCopy.take(dev.index()); 0422 } 0423 } 0424 } else if (hashCopy.size() == 1) { 0425 orderedList += hashCopy.values(); 0426 } 0427 0428 model->setModelData(orderedList); 0429 } else { 0430 model->setModelData(list); 0431 } 0432 } 0433 0434 deviceList->resizeColumnToContents(0); 0435 } 0436 0437 void DevicePreference::updateAudioOutputDevices() 0438 { 0439 const QList<AudioOutputDevice> list = availableAudioOutputDevices(); 0440 QHash<int, AudioOutputDevice> hash; 0441 foreach (const AudioOutputDevice &dev, list) { 0442 hash.insert(dev.index(), dev); 0443 } 0444 0445 for (int catIndex = 0; catIndex < audioOutCategoriesCount; ++ catIndex) { 0446 const int i = audioOutCategories[catIndex]; 0447 AudioOutputDeviceModel *model = m_audioOutputModel.value(i); 0448 Q_ASSERT(model); 0449 0450 QHash<int, AudioOutputDevice> hashCopy(hash); 0451 QList<AudioOutputDevice> orderedList; 0452 0453 if (model->rowCount() > 0) { 0454 QList<int> order = model->tupleIndexOrder(); 0455 foreach (int idx, order) { 0456 if (hashCopy.contains(idx)) { 0457 orderedList << hashCopy.take(idx); 0458 } 0459 } 0460 0461 if (hashCopy.size() > 1) { 0462 // keep the order of the original list 0463 foreach (const AudioOutputDevice &dev, list) { 0464 if (hashCopy.contains(dev.index())) { 0465 orderedList << hashCopy.take(dev.index()); 0466 } 0467 } 0468 } else if (hashCopy.size() == 1) { 0469 orderedList += hashCopy.values(); 0470 } 0471 0472 model->setModelData(orderedList); 0473 } else { 0474 model->setModelData(list); 0475 } 0476 } 0477 0478 deviceList->resizeColumnToContents(0); 0479 } 0480 0481 QList<AudioOutputDevice> DevicePreference::availableAudioOutputDevices() const 0482 { 0483 return BackendCapabilities::availableAudioOutputDevices(); 0484 } 0485 0486 QList<AudioCaptureDevice> DevicePreference::availableAudioCaptureDevices() const 0487 { 0488 #ifndef PHONON_NO_AUDIOCAPTURE 0489 return BackendCapabilities::availableAudioCaptureDevices(); 0490 #else 0491 return QList<AudioCaptureDevice>(); 0492 #endif 0493 } 0494 0495 QList<VideoCaptureDevice> DevicePreference::availableVideoCaptureDevices() const 0496 { 0497 #ifndef PHONON_NO_VIDEOCAPTURE 0498 return BackendCapabilities::availableVideoCaptureDevices(); 0499 #else 0500 return QList<VideoCaptureDevice>(); 0501 #endif 0502 } 0503 0504 void DevicePreference::load() 0505 { 0506 showAdvancedDevicesCheckBox->setChecked(!GlobalConfig().hideAdvancedDevices()); 0507 loadCategoryDevices(); 0508 } 0509 0510 void DevicePreference::loadCategoryDevices() 0511 { 0512 // "Load" the settings from the backend. 0513 for (int i = 0; i < audioOutCategoriesCount; ++ i) { 0514 const Category cat = audioOutCategories[i]; 0515 QList<AudioOutputDevice> list; 0516 const QList<int> deviceIndexes = GlobalConfig().audioOutputDeviceListFor(cat); 0517 foreach (int i, deviceIndexes) { 0518 list.append(AudioOutputDevice::fromIndex(i)); 0519 } 0520 0521 m_audioOutputModel[cat]->setModelData(list); 0522 } 0523 0524 #ifndef PHONON_NO_AUDIOCAPTURE 0525 for (int i = 0; i < audioCapCategoriesCount; ++ i) { 0526 const CaptureCategory cat = audioCapCategories[i]; 0527 QList<AudioCaptureDevice> list; 0528 const QList<int> deviceIndexes = GlobalConfig().audioCaptureDeviceListFor(cat); 0529 foreach (int i, deviceIndexes) { 0530 list.append(AudioCaptureDevice::fromIndex(i)); 0531 } 0532 0533 m_audioCaptureModel[cat]->setModelData(list); 0534 } 0535 #endif 0536 0537 #ifndef PHONON_NO_VIDEOCAPTURE 0538 for (int i = 0; i < videoCapCategoriesCount; ++ i) { 0539 const CaptureCategory cat = videoCapCategories[i]; 0540 QList<VideoCaptureDevice> list; 0541 const QList<int> deviceIndexes = GlobalConfig().videoCaptureDeviceListFor(cat); 0542 foreach (int i, deviceIndexes) { 0543 list.append(VideoCaptureDevice::fromIndex(i)); 0544 } 0545 0546 m_videoCaptureModel[cat]->setModelData(list); 0547 } 0548 #endif 0549 0550 deviceList->resizeColumnToContents(0); 0551 } 0552 0553 void DevicePreference::save() 0554 { 0555 for (int i = 0; i < audioOutCategoriesCount; ++i) { 0556 const Category cat = audioOutCategories[i]; 0557 Q_ASSERT(m_audioOutputModel.value(cat)); 0558 const QList<int> order = m_audioOutputModel.value(cat)->tupleIndexOrder(); 0559 GlobalConfig().setAudioOutputDeviceListFor(cat, order); 0560 } 0561 0562 #ifndef PHONON_NO_AUDIOCAPTURE 0563 for (int i = 0; i < audioCapCategoriesCount; ++i) { 0564 const CaptureCategory cat = audioCapCategories[i]; 0565 Q_ASSERT(m_audioCaptureModel.value(cat)); 0566 const QList<int> order = m_audioCaptureModel.value(cat)->tupleIndexOrder(); 0567 GlobalConfig().setAudioCaptureDeviceListFor(cat, order); 0568 } 0569 #endif 0570 0571 #ifndef PHONON_NO_VIDEOCAPTURE 0572 for (int i = 0; i < videoCapCategoriesCount; ++i) { 0573 const CaptureCategory cat = videoCapCategories[i]; 0574 Q_ASSERT(m_videoCaptureModel.value(cat)); 0575 const QList<int> order = m_videoCaptureModel.value(cat)->tupleIndexOrder(); 0576 GlobalConfig().setVideoCaptureDeviceListFor(cat, order); 0577 } 0578 #endif 0579 } 0580 0581 void DevicePreference::defaults() 0582 { 0583 { 0584 const QList<AudioOutputDevice> list = availableAudioOutputDevices(); 0585 for (int i = 0; i < audioOutCategoriesCount; ++i) { 0586 m_audioOutputModel[audioOutCategories[i]]->setModelData(list); 0587 } 0588 } 0589 { 0590 const QList<AudioCaptureDevice> list = availableAudioCaptureDevices(); 0591 for (int i = 0; i < audioCapCategoriesCount; ++i) { 0592 m_audioCaptureModel[audioCapCategories[i]]->setModelData(list); 0593 } 0594 } 0595 { 0596 const QList<VideoCaptureDevice> list = availableVideoCaptureDevices(); 0597 for (int i = 0; i < videoCapCategoriesCount; ++i) { 0598 m_videoCaptureModel[videoCapCategories[i]]->setModelData(list); 0599 } 0600 } 0601 0602 /* 0603 * Save this list (that contains even hidden devices) to GlobaConfig, and then 0604 * load them back. All devices that should be hidden will be hidden 0605 */ 0606 save(); 0607 loadCategoryDevices(); 0608 0609 deviceList->resizeColumnToContents(0); 0610 } 0611 0612 void DevicePreference::pulseAudioEnabled() 0613 { 0614 showAdvancedDevicesContainer->removeItem(showAdvancedDevicesSpacer); 0615 delete showAdvancedDevicesSpacer; 0616 showAdvancedDevicesCheckBox->setVisible(false); 0617 } 0618 0619 void DevicePreference::on_preferButton_clicked() 0620 { 0621 QAbstractItemModel *model = deviceList->model(); 0622 { 0623 AudioOutputDeviceModel *deviceModel = dynamic_cast<AudioOutputDeviceModel *>(model); 0624 if (deviceModel) { 0625 deviceModel->moveUp(deviceList->currentIndex()); 0626 updateButtonsEnabled(); 0627 emit changed(); 0628 } 0629 } 0630 { 0631 AudioCaptureDeviceModel *deviceModel = dynamic_cast<AudioCaptureDeviceModel *>(model); 0632 if (deviceModel) { 0633 deviceModel->moveUp(deviceList->currentIndex()); 0634 updateButtonsEnabled(); 0635 emit changed(); 0636 } 0637 } 0638 { 0639 VideoCaptureDeviceModel *deviceModel = dynamic_cast<VideoCaptureDeviceModel *>(model); 0640 if (deviceModel) { 0641 deviceModel->moveUp(deviceList->currentIndex()); 0642 updateButtonsEnabled(); 0643 emit changed(); 0644 } 0645 } 0646 } 0647 0648 void DevicePreference::on_deferButton_clicked() 0649 { 0650 QAbstractItemModel *model = deviceList->model(); 0651 { 0652 AudioOutputDeviceModel *deviceModel = dynamic_cast<AudioOutputDeviceModel *>(model); 0653 if (deviceModel) { 0654 deviceModel->moveDown(deviceList->currentIndex()); 0655 updateButtonsEnabled(); 0656 emit changed(); 0657 } 0658 } 0659 { 0660 AudioCaptureDeviceModel *deviceModel = dynamic_cast<AudioCaptureDeviceModel *>(model); 0661 if (deviceModel) { 0662 deviceModel->moveDown(deviceList->currentIndex()); 0663 updateButtonsEnabled(); 0664 emit changed(); 0665 } 0666 } 0667 { 0668 VideoCaptureDeviceModel *deviceModel = dynamic_cast<VideoCaptureDeviceModel *>(model); 0669 if (deviceModel) { 0670 deviceModel->moveDown(deviceList->currentIndex()); 0671 updateButtonsEnabled(); 0672 emit changed(); 0673 } 0674 } 0675 } 0676 0677 DevicePreference::DeviceType DevicePreference::shownModelType() const 0678 { 0679 const QStandardItem *item = m_categoryModel.itemFromIndex(categoryTree->currentIndex()); 0680 if (!item) 0681 return dtInvalidDevice; 0682 Q_ASSERT(item->type() == 1001); 0683 0684 const CategoryItem *catItem = static_cast<const CategoryItem *>(item); 0685 if (!catItem) 0686 return dtInvalidDevice; 0687 0688 switch (catItem->odtype()) { 0689 case AudioOutputDeviceType: 0690 return dtAudioOutput; 0691 case AudioCaptureDeviceType: 0692 return dtAudioCapture; 0693 case VideoCaptureDeviceType: 0694 return dtVideoCapture; 0695 default: 0696 return dtInvalidDevice; 0697 } 0698 } 0699 0700 void DevicePreference::on_applyPreferencesButton_clicked() 0701 { 0702 const QModelIndex idx = categoryTree->currentIndex(); 0703 const QStandardItem *item = m_categoryModel.itemFromIndex(idx); 0704 if (!item) 0705 return; 0706 Q_ASSERT(item->type() == 1001); 0707 0708 const CategoryItem *catItem = static_cast<const CategoryItem *>(item); 0709 0710 QList<AudioOutputDevice> aoPreferredList; 0711 QList<AudioCaptureDevice> acPreferredList; 0712 QList<VideoCaptureDevice> vcPreferredList; 0713 const Category *categoryList = nullptr; 0714 const CaptureCategory *capCategoryList = nullptr; 0715 int categoryListCount; 0716 int catIndex; 0717 bool cap = false; 0718 0719 switch (catItem->odtype()) { 0720 case AudioOutputDeviceType: 0721 aoPreferredList = m_audioOutputModel.value(catItem->category())->modelData(); 0722 categoryList = audioOutCategories; 0723 categoryListCount = audioOutCategoriesCount; 0724 cap = false; 0725 break; 0726 0727 case AudioCaptureDeviceType: 0728 acPreferredList = m_audioCaptureModel.value(catItem->captureCategory())->modelData(); 0729 capCategoryList = audioCapCategories; 0730 categoryListCount = audioCapCategoriesCount; 0731 cap = true; 0732 break; 0733 0734 case VideoCaptureDeviceType: 0735 vcPreferredList = m_videoCaptureModel.value(catItem->captureCategory())->modelData(); 0736 capCategoryList = videoCapCategories; 0737 categoryListCount = videoCapCategoriesCount; 0738 cap = true; 0739 break; 0740 0741 default: 0742 return; 0743 } 0744 0745 QPointer<QDialog> dialog = new QDialog(this); 0746 0747 QLabel *label = new QLabel(dialog); 0748 label->setText(tr("Apply the currently shown device preference list to the following other " 0749 "audio playback categories:")); 0750 label->setWordWrap(true); 0751 0752 QListWidget *list = new QListWidget(dialog); 0753 0754 for (catIndex = 0; catIndex < categoryListCount; catIndex ++) { 0755 Category cat = cap ? NoCategory : categoryList[catIndex]; 0756 CaptureCategory capcat = cap ? capCategoryList[catIndex] : NoCaptureCategory; 0757 0758 QListWidgetItem *item = nullptr; 0759 if (cap) { 0760 if (capcat == NoCaptureCategory) { 0761 item = new QListWidgetItem(tr("Default/Unspecified Category"), list, capcat); 0762 } else { 0763 item = new QListWidgetItem(categoryToString(capcat), list, capcat); 0764 } 0765 } else { 0766 if (cat == NoCategory) { 0767 item = new QListWidgetItem(tr("Default/Unspecified Category"), list, cat); 0768 } else { 0769 item = new QListWidgetItem(categoryToString(cat), list, cat); 0770 } 0771 } 0772 0773 item->setCheckState(Qt::Checked); 0774 if (cat == catItem->category()) { 0775 item->setFlags(item->flags() & ~Qt::ItemIsEnabled); 0776 } 0777 } 0778 0779 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok 0780 | QDialogButtonBox::Cancel, dialog); 0781 connect(buttonBox, &QDialogButtonBox::accepted, dialog.data(), &QDialog::accept); 0782 connect(buttonBox, &QDialogButtonBox::rejected, dialog.data(), &QDialog::reject); 0783 0784 QVBoxLayout *layout = new QVBoxLayout(dialog); 0785 layout->addWidget(label); 0786 layout->addWidget(list); 0787 layout->addWidget(buttonBox); 0788 0789 switch (dialog->exec()) { 0790 case QDialog::Accepted: 0791 for (catIndex = 0; catIndex < categoryListCount; catIndex ++) { 0792 Category cat = cap ? NoCategory : categoryList[catIndex]; 0793 CaptureCategory capcat = cap ? capCategoryList[catIndex] : NoCaptureCategory; 0794 0795 if (cap ? capcat != catItem->captureCategory() : cat != catItem->category()) { 0796 QListWidgetItem *item = list->item(catIndex); 0797 Q_ASSERT(item->type() == cap ? (int) capcat : (int) cat); 0798 if (item->checkState() == Qt::Checked) { 0799 switch (catItem->odtype()) { 0800 case AudioOutputDeviceType: 0801 m_audioOutputModel.value(cat)->setModelData(aoPreferredList); 0802 break; 0803 0804 case AudioCaptureDeviceType: 0805 m_audioCaptureModel.value(capcat)->setModelData(acPreferredList); 0806 break; 0807 0808 case VideoCaptureDeviceType: 0809 m_videoCaptureModel.value(capcat)->setModelData(vcPreferredList); 0810 break; 0811 0812 default: ; 0813 } 0814 } 0815 } 0816 } 0817 0818 emit changed(); 0819 break; 0820 0821 case QDialog::Rejected: 0822 // nothing to do 0823 break; 0824 } 0825 0826 delete dialog; 0827 } 0828 0829 void DevicePreference::on_showAdvancedDevicesCheckBox_toggled() 0830 { 0831 // In order to get the right list from the backend, we need to update the settings now 0832 // before calling availableAudio{Output,Capture}Devices() 0833 GlobalConfig().setHideAdvancedDevices(!showAdvancedDevicesCheckBox->isChecked()); 0834 loadCategoryDevices(); 0835 } 0836 0837 void DevicePreference::on_testPlaybackButton_toggled(bool down) 0838 { 0839 if (down) { 0840 QModelIndex idx = deviceList->currentIndex(); 0841 if (!idx.isValid()) { 0842 return; 0843 } 0844 0845 // Shouldn't happen, but better to be on the safe side 0846 if (m_testingType != dtInvalidDevice) { 0847 delete m_media; 0848 m_media = nullptr; 0849 delete m_audioOutput; 0850 m_audioOutput = nullptr; 0851 delete m_videoWidget; 0852 m_videoWidget = nullptr; 0853 } 0854 0855 // Setup the Phonon objects according to the testing type 0856 m_testingType = shownModelType(); 0857 switch (m_testingType) { 0858 case dtAudioOutput: { 0859 // Create an audio output with the selected device 0860 m_media = new MediaObject(this); 0861 const AudioOutputDeviceModel *model = static_cast<const AudioOutputDeviceModel *>(idx.model()); 0862 const AudioOutputDevice &device = model->modelData(idx); 0863 m_audioOutput = new AudioOutput(this); 0864 if (!m_audioOutput->setOutputDevice(device)) { 0865 QMessageBox::critical(this, tr("Failed to set the selected audio output device"), tr("Failed to set the selected audio output device")); 0866 break; 0867 } 0868 0869 // Just to be very sure that nothing messes our test sound up 0870 m_audioOutput->setVolume(1.0); 0871 m_audioOutput->setMuted(false); 0872 0873 createPath(m_media, m_audioOutput); 0874 static QUrl testUrl = QUrl::fromLocalFile(QStandardPaths::locate( 0875 QStandardPaths::GenericDataLocation, 0876 QStringLiteral("sounds/Oxygen-Sys-Log-In.ogg"))); 0877 m_media->setCurrentSource(testUrl); 0878 connect(m_media, &MediaObject::finished, testPlaybackButton, &QToolButton::toggle); 0879 0880 break; 0881 } 0882 0883 #ifndef PHONON_NO_AUDIOCAPTURE 0884 case dtAudioCapture: { 0885 // Create a media object and an audio output 0886 m_media = new MediaObject(this); 0887 m_audioOutput = new AudioOutput(NoCategory, this); 0888 0889 // Just to be very sure that nothing messes our test sound up 0890 m_audioOutput->setVolume(1.0); 0891 m_audioOutput->setMuted(false); 0892 0893 // Try to create a path 0894 if (!createPath(m_media, m_audioOutput).isValid()) { 0895 QMessageBox::critical(this, tr("Your backend may not support audio recording"), tr("Your backend may not support audio recording")); 0896 break; 0897 } 0898 0899 // Determine the selected device 0900 const AudioCaptureDeviceModel *model = static_cast<const AudioCaptureDeviceModel *>(idx.model()); 0901 const AudioCaptureDevice &device = model->modelData(idx); 0902 m_media->setCurrentSource(device); 0903 0904 break; 0905 } 0906 #endif 0907 0908 #ifndef PHONON_NO_VIDEOCAPTURE 0909 case dtVideoCapture: { 0910 // Create a media object and a video output 0911 m_media = new MediaObject(this); 0912 m_videoWidget = new VideoWidget(nullptr); 0913 0914 // Try to create a path 0915 if (!createPath(m_media, m_videoWidget).isValid()) { 0916 QMessageBox::critical(this, tr("Your backend may not support video recording"), tr("Your backend may not support video recording")); 0917 break; 0918 } 0919 0920 // Determine the selected device 0921 const VideoCaptureDeviceModel *model = static_cast<const VideoCaptureDeviceModel *>(idx.model()); 0922 const VideoCaptureDevice &device = model->modelData(idx); 0923 m_media->setCurrentSource(device); 0924 0925 // Set up the testing video widget 0926 m_videoWidget->setWindowTitle(tr("Testing %1").arg(device.name())); 0927 m_videoWidget->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint); 0928 if (device.property("icon").canConvert(QVariant::String)) 0929 m_videoWidget->setWindowIcon(QIcon::fromTheme(device.property("icon").toString())); 0930 m_videoWidget->move(QCursor::pos() - QPoint(250, 295)); 0931 m_videoWidget->resize(320, 240); 0932 m_videoWidget->show(); 0933 0934 break; 0935 } 0936 #endif 0937 0938 default: 0939 return; 0940 } 0941 0942 m_media->play(); 0943 } else { 0944 // Uninitialize the Phonon objects according to the testing type 0945 switch (m_testingType) { 0946 case dtAudioOutput: 0947 disconnect(m_media, &MediaObject::finished, testPlaybackButton, &QToolButton::toggle); 0948 delete m_media; 0949 delete m_audioOutput; 0950 break; 0951 0952 case dtAudioCapture: 0953 delete m_media; 0954 delete m_audioOutput; 0955 break; 0956 0957 case dtVideoCapture: 0958 delete m_media; 0959 delete m_videoWidget; 0960 break; 0961 0962 default: 0963 return; 0964 } 0965 0966 m_media = nullptr; 0967 m_videoWidget = nullptr; 0968 m_audioOutput = nullptr; 0969 m_testingType = dtInvalidDevice; 0970 } 0971 } 0972 0973 void DevicePreference::updateButtonsEnabled() 0974 { 0975 if (deviceList->model()) { 0976 QModelIndex idx = deviceList->currentIndex(); 0977 preferButton->setEnabled(idx.isValid() && idx.row() > 0); 0978 deferButton->setEnabled(idx.isValid() && idx.row() < deviceList->model()->rowCount() - 1); 0979 testPlaybackButton->setEnabled(idx.isValid() && (idx.flags() & Qt::ItemIsEnabled)); 0980 } else { 0981 preferButton->setEnabled(false); 0982 deferButton->setEnabled(false); 0983 testPlaybackButton->setEnabled(false); 0984 } 0985 } 0986 0987 } // Phonon namespace 0988 0989 #include "moc_devicepreference.cpp"