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"