diff --git a/phonon/kcm/CMakeLists.txt b/phonon/kcm/CMakeLists.txt index ffca59505e..9d8357b1bb 100644 --- a/phonon/kcm/CMakeLists.txt +++ b/phonon/kcm/CMakeLists.txt @@ -1,14 +1,14 @@ include_directories(${KDE4_KIO_INCLUDES} ${CMAKE_SOURCE_DIR}/kutils ${CMAKE_SOURCE_DIR}/solid ${CMAKE_BINARY_DIR}/solid) -set(kcmphonon_SRCS main.cpp outputdevicechoice.cpp backendselection.cpp) +set(kcmphonon_SRCS main.cpp kwidgetblendanimation.cpp outputdevicechoice.cpp backendselection.cpp ../globalconfig.cpp) kde4_add_ui_files(kcmphonon_SRCS outputdevicechoice.ui backendselection.ui) kde4_add_plugin(kcm_phonon ${kcmphonon_SRCS}) target_link_libraries(kcm_phonon ${KDE4_PHONON_LIBS} ${KDE4_KUTILS_LIBS} ${KDE4_KAUDIODEVICELIST_LIBS}) install(TARGETS kcm_phonon DESTINATION ${PLUGIN_INSTALL_DIR} ) ########### install files ############### install( FILES kcm_phonon.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install(FILES listview-background.png DESTINATION ${DATA_INSTALL_DIR}/kcm_phonon) diff --git a/phonon/kcm/kwidgetblendanimation.cpp b/phonon/kcm/kwidgetblendanimation.cpp new file mode 100644 index 0000000000..5dc5e17780 --- /dev/null +++ b/phonon/kcm/kwidgetblendanimation.cpp @@ -0,0 +1,75 @@ +/* This file is part of the KDE project + Copyright (C) 2008 Matthias Kretz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) version 3. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "kwidgetblendanimation.h" +#include "kwidgetblendanimation_p.h" +#include +#include + +KWidgetBlendAnimationPrivate::KWidgetBlendAnimationPrivate(QWidget *_destWidget) + : destWidget(_destWidget) +{ +} + +KWidgetBlendAnimation::KWidgetBlendAnimation(QWidget *destWidget) + : QWidget(destWidget ? destWidget->parentWidget() : 0), + d_ptr(new KWidgetBlendAnimationPrivate(destWidget)) +{ + Q_D(KWidgetBlendAnimation); + d->q_ptr = this; + Q_ASSERT(destWidget && destWidget->parentWidget()); + setGeometry(QRect(destWidget->mapTo(parentWidget(), QPoint(0, 0)), destWidget->size())); + d->oldPixmap = QPixmap::grabWidget(destWidget); + d->timeLine.setFrameRange(0, 255); + connect(&d->timeLine, SIGNAL(finished()), SLOT(finished())); + connect(&d->timeLine, SIGNAL(frameChanged(int)), SLOT(repaint())); + show(); +} + +KWidgetBlendAnimation::~KWidgetBlendAnimation() +{ + delete d_ptr; +} + +void KWidgetBlendAnimationPrivate::finished() +{ + Q_Q(KWidgetBlendAnimation); + destWidget->setUpdatesEnabled(false); + q->hide(); + q->deleteLater(); + destWidget->setUpdatesEnabled(true); +} + +void KWidgetBlendAnimation::start(int duration) +{ + Q_D(KWidgetBlendAnimation); + d->timeLine.setDuration(duration); + d->timeLine.start(); +} + +void KWidgetBlendAnimation::paintEvent(QPaintEvent *) +{ + Q_D(KWidgetBlendAnimation); + QPainter p(this); + p.setOpacity(1.0 - d->timeLine.currentValue()); + p.drawPixmap(rect(), d->oldPixmap); +} + +#include "moc_kwidgetblendanimation.cpp" diff --git a/phonon/kcm/kwidgetblendanimation.h b/phonon/kcm/kwidgetblendanimation.h new file mode 100644 index 0000000000..90613b49af --- /dev/null +++ b/phonon/kcm/kwidgetblendanimation.h @@ -0,0 +1,93 @@ +/* This file is part of the KDE project + Copyright (C) 2008 Matthias Kretz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) version 3. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef KWIDGETBLENDANIMATION_H +#define KWIDGETBLENDANIMATION_H + +#include +#include +#include + +class KWidgetBlendAnimationPrivate; + +/** \class KWidgetBlendAnimation kwidgetblendanimation.h KWidgetBlendAnimation + * \brief Animates changes fading the new UI over the old look. + * + * This widget will put itself above the widget that will change and show a fading transition from + * the old to the new UI. It will delete itself after the animation is finished. + * Example: + * \code + * KWidgetBlendAnimation *animation = new KWidgetBlendAnimation(widgetThatWillChange); + * // do changes on widgetThatWillChange + * // ... + * animation->start(); + * \endcode + * + * \note The widget that changes needs to have a parent widget. KWidgetBlendAnimation does not work + * for toplevel widgets (windows). + * + * \author Matthias Kretz + * \since 4.1 + */ +class KWidgetBlendAnimation : public QWidget +{ + Q_OBJECT + Q_DECLARE_PRIVATE(KWidgetBlendAnimation) + public: + /** + * Create the animation widget. Takes a snapshot of the \p destWidget to use as old image + * that gets faded out. + * + * \param destWidget The widget that will change and should fade to the new look. + */ + KWidgetBlendAnimation(QWidget *destWidget); + + /** + * Destructor. + * + * \warning KWidgetBlendAnimation deletes itself after the animation is finished. + */ + ~KWidgetBlendAnimation(); + + /** + * Starts the animation. + * + * Call this function after all visual changes are done. + * + * \param duration The duration of the animation in milliseconds. + */ + void start(int duration = 250); + + protected: + /** + * \internal + */ + void paintEvent(QPaintEvent *); + + /** + * \internal + */ + KWidgetBlendAnimationPrivate *const d_ptr; + + private: + Q_PRIVATE_SLOT(d_func(), void finished()) +}; + +#endif // KWIDGETBLENDANIMATION_H diff --git a/phonon/kcm/kwidgetblendanimation_p.h b/phonon/kcm/kwidgetblendanimation_p.h new file mode 100644 index 0000000000..041b2f9b7e --- /dev/null +++ b/phonon/kcm/kwidgetblendanimation_p.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (C) 2008 Matthias Kretz + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) version 3. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef KWIDGETBLENDANIMATION_P_H +#define KWIDGETBLENDANIMATION_P_H + +#include "kwidgetblendanimation.h" + +class KWidgetBlendAnimationPrivate +{ + Q_DECLARE_PUBLIC(KWidgetBlendAnimation) + protected: + KWidgetBlendAnimationPrivate(QWidget *_destWidget); + KWidgetBlendAnimation *q_ptr; + + private: + void finished(); + + QTimeLine timeLine; + QPixmap oldPixmap; + QWidget *destWidget; +}; + +#endif // KWIDGETBLENDANIMATION_P_H diff --git a/phonon/kcm/outputdevicechoice.cpp b/phonon/kcm/outputdevicechoice.cpp index d948f15ffe..f2d526279b 100644 --- a/phonon/kcm/outputdevicechoice.cpp +++ b/phonon/kcm/outputdevicechoice.cpp @@ -1,382 +1,637 @@ /* This file is part of the KDE project Copyright (C) 2006-2007 Matthias Kretz This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) version 3. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "outputdevicechoice.h" #include +#include +#include +#include +#include +#include #include -#include #include #include #include #include "../libkaudiodevicelist/audiodeviceenumerator.h" #include "../libkaudiodevicelist/audiodevice.h" #include "../qsettingsgroup_p.h" +#include "../globalconfig.h" +#include "kwidgetblendanimation.h" +#include +#include #include +#include +#include #include #include #ifndef METATYPE_QLIST_INT_DEFINED #define METATYPE_QLIST_INT_DEFINED // Want this exactly once, see phonondefs_p.h kcm/outputdevicechoice.cpp Q_DECLARE_METATYPE(QList) #endif using Phonon::QSettingsGroup; class CategoryItem : public QStandardItem { public: CategoryItem(Phonon::Category cat) - : QStandardItem(Phonon::categoryToString(cat)), + : QStandardItem(cat == Phonon::NoCategory ? i18n("Audio Output") : Phonon::categoryToString(cat)), m_cat(cat) { } int type() const { return 1001; } Phonon::Category category() const { return m_cat; } private: Phonon::Category m_cat; }; +class DeviceTreeDelegate : public QItemDelegate +{ + public: + DeviceTreeDelegate(QObject *parent = 0); + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +DeviceTreeDelegate::DeviceTreeDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +QSize DeviceTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QVariant value = index.data(Qt::SizeHintRole); + if (value.isValid()) { + return qvariant_cast(value); + } + + QRect iconRect; + value = index.data(Qt::DecorationRole); + if (value.type() == QVariant::Icon) { + QIcon icon = qvariant_cast(value); + iconRect = QRect(QPoint(0, 0), icon.actualSize(option.decorationSize)); + } + + QRect textRect; + value = index.data(Qt::DisplayRole); + if (value.isValid()) { + QString text = value.toString(); + const QSize size = option.fontMetrics.size(Qt::TextSingleLine, text); + const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + textRect = QRect(0, 0, size.width() + 2 * textMargin, size.height()); + } + QRect checkRect = rect(option, index, Qt::CheckStateRole); + + doLayout(option, &checkRect, &iconRect, &textRect, true); + + return (iconRect | textRect | checkRect).size(); +} + +void DeviceTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV3 opt = setOptions(index, option); + + const QStyleOptionViewItemV2 *v2 = qstyleoption_cast(&option); + const QStyleOptionViewItemV3 *v3 = qstyleoption_cast(&option); + opt.features = v2 ? v2->features : QStyleOptionViewItemV2::ViewItemFeatures(QStyleOptionViewItemV2::None); + opt.locale = v3 ? v3->locale : QLocale(); + opt.widget = v3 ? v3->widget : 0; + + painter->save(); + // clip painter to the actual item - i.e. don't paint outside + painter->setClipRect(opt.rect); + + QIcon icon; + QIcon::Mode mode = QIcon::Normal; + QIcon::State state = QIcon::Off; + QRect iconRect; + QVariant value = index.data(Qt::DecorationRole); + if (value.type() == QVariant::Icon) { + icon = qvariant_cast(value); + if (!(option.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if (option.state & QStyle::State_Selected) { + mode = QIcon::Selected; + } + if (option.state & QStyle::State_Open) { + state = QIcon::On; + } + iconRect = QRect(QPoint(0, 0), icon.actualSize(option.decorationSize, mode, state)); + } + + QString text; + QRect textRect; + value = index.data(Qt::DisplayRole); + if (value.isValid()) { + text = value.toString(); + const QSize size = opt.fontMetrics.size(Qt::TextSingleLine, text); + const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + textRect = QRect(0, 0, size.width() + 2 * textMargin, size.height()); + } + + QRect checkRect; + Qt::CheckState checkState = Qt::Unchecked; + value = index.data(Qt::CheckStateRole); + if (value.isValid()) { + checkState = static_cast(value.toInt()); + checkRect = check(opt, opt.rect, value); + } + + doLayout(opt, &checkRect, &iconRect, &textRect, false); + + drawBackground(painter, opt, index); + drawCheck(painter, opt, checkRect, checkState); + icon.paint(painter, iconRect, option.decorationAlignment, mode, state); + drawDisplay(painter, opt, textRect, text); + drawFocus(painter, opt, textRect); + + painter->restore(); +} + /** * Need this to change the colors of the ListView if the Palette changed. With CSS set this won't * change automatically */ void OutputDeviceChoice::changeEvent(QEvent *e) { QWidget::changeEvent(e); if (e->type() == QEvent::PaletteChange) { + //deviceList->viewport()->setStyleSheet(deviceList->viewport()->styleSheet()); deviceList->setStyleSheet(deviceList->styleSheet()); } } OutputDeviceChoice::OutputDeviceChoice(QWidget *parent) - : QWidget(parent) + : QWidget(parent), + m_headerModel(0, 1, 0) { setupUi(this); + deviceList->setItemDelegate(new DeviceTreeDelegate(deviceList)); removeButton->setIcon(KIcon("list-remove")); deferButton->setIcon(KIcon("go-down")); preferButton->setIcon(KIcon("go-up")); deviceList->setDragDropMode(QAbstractItemView::InternalMove); - deviceList->setStyleSheet(QString("QListView {" + //deviceList->viewport()->setStyleSheet(QString("QWidget#qt_scrollarea_viewport {" + deviceList->setStyleSheet(QString("QTreeView {" "background-color: palette(base);" "background-image: url(%1);" "background-position: bottom left;" "background-attachment: fixed;" "background-repeat: no-repeat;" "}") .arg(KStandardDirs::locate("data", "kcm_phonon/listview-background.png"))); deviceList->setAlternatingRowColors(false); QStandardItem *parentItem = m_categoryModel.invisibleRootItem(); - QStandardItem *outputItem = new QStandardItem(i18n("Audio Output")); + QStandardItem *outputItem = new CategoryItem(Phonon::NoCategory); + m_outputModel[Phonon::NoCategory] = new Phonon::AudioOutputDeviceModel; outputItem->setEditable(false); - outputItem->setSelectable(false); + outputItem->setToolTip(i18n("Defines the default ordering of devices which can be overridden by individual categories.")); parentItem->appendRow(outputItem); /* m_captureItem = new QStandardItem(i18n("Audio Capture")); m_captureItem->setEditable(false); parentItem->appendRow(m_captureItem); */ parentItem = outputItem; for (int i = 0; i <= Phonon::LastCategory; ++i) { m_outputModel[i] = new Phonon::AudioOutputDeviceModel; QStandardItem *item = new CategoryItem(static_cast(i)); item->setEditable(false); parentItem->appendRow(item); } //parentItem = m_captureItem; categoryTree->setModel(&m_categoryModel); if (categoryTree->header()) { categoryTree->header()->hide(); } categoryTree->expandAll(); connect(categoryTree->selectionModel(), SIGNAL(currentChanged(const QModelIndex &,const QModelIndex &)), SLOT(updateDeviceList())); - for (int i = 0; i <= Phonon::LastCategory; ++i) { + for (int i = -1; i <= Phonon::LastCategory; ++i) { connect(m_outputModel[i], SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(changed())); connect(m_outputModel[i], SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(changed())); connect(m_outputModel[i], SIGNAL(layoutChanged()), this, SIGNAL(changed())); connect(m_outputModel[i], SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SIGNAL(changed())); } /* connect(&m_captureModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(changed())); connect(&m_captureModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(changed())); connect(&m_captureModel, SIGNAL(layoutChanged()), this, SIGNAL(changed())); connect(&m_captureModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SIGNAL(changed())); */ connect(Phonon::BackendCapabilities::notifier(), SIGNAL(availableAudioOutputDevicesChanged()), SLOT(updateAudioOutputDevices())); if (!categoryTree->currentIndex().isValid()) { categoryTree->setCurrentIndex(m_categoryModel.index(0, 0).child(1, 0)); } } void OutputDeviceChoice::updateDeviceList() { QStandardItem *currentItem = m_categoryModel.itemFromIndex(categoryTree->currentIndex()); + KWidgetBlendAnimation *animation = new KWidgetBlendAnimation(deviceList); if (deviceList->selectionModel()) { disconnect(deviceList->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &,const QModelIndex &)), this, SLOT(updateButtonsEnabled())); } if (currentItem->type() == 1001) { CategoryItem *catItem = static_cast(currentItem); - deviceList->setModel(m_outputModel[catItem->category()]); + const Phonon::Category cat = catItem->category(); + deviceList->setModel(m_outputModel[cat]); + if (cat == Phonon::NoCategory) { + m_headerModel.setHeaderData(0, Qt::Horizontal, i18n("Default Device Preference:"), Qt::DisplayRole); + } else { + m_headerModel.setHeaderData(0, Qt::Horizontal, i18n("Device Preference for the '%1' Category:", Phonon::categoryToString(cat)), Qt::DisplayRole); + } /* } else if (currentItem == m_captureItem) { deviceList->setModel(&m_captureModel); */ } else { + m_headerModel.setHeaderData(0, Qt::Horizontal, QString(), Qt::DisplayRole); deviceList->setModel(0); } + deviceList->header()->setModel(&m_headerModel); updateButtonsEnabled(); if (deviceList->selectionModel()) { connect(deviceList->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &,const QModelIndex &)), this, SLOT(updateButtonsEnabled())); } + deviceList->resizeColumnToContents(0); + animation->start(); } void OutputDeviceChoice::updateAudioOutputDevices() { - QList list = Phonon::BackendCapabilities::availableAudioOutputDevices(); + const QList list = availableAudioOutputDevices(); QHash hash; - foreach (Phonon::AudioOutputDevice dev, list) { + foreach (const Phonon::AudioOutputDevice &dev, list) { hash.insert(dev.index(), dev); } - for (int i = 0; i <= Phonon::LastCategory; ++i) { + for (int i = -1; i <= Phonon::LastCategory; ++i) { Phonon::AudioOutputDeviceModel *model = m_outputModel.value(i); Q_ASSERT(model); QHash hashCopy(hash); QList orderedList; if (model->rowCount() > 0) { QList order = model->tupleIndexOrder(); foreach (int idx, order) { if (hashCopy.contains(idx)) { orderedList << hashCopy.take(idx); } } if (hashCopy.size() > 1) { // keep the order of the original list foreach (const Phonon::AudioOutputDevice &dev, list) { if (hashCopy.contains(dev.index())) { orderedList << hashCopy.take(dev.index()); } } } else if (hashCopy.size() == 1) { orderedList += hashCopy.values(); } model->setModelData(orderedList); } else { model->setModelData(list); } } + deviceList->resizeColumnToContents(0); +} + +QList OutputDeviceChoice::availableAudioOutputDevices() const +{ + QList ret; + const QList deviceIndexes = Phonon::GlobalConfig().audioOutputDeviceListFor(Phonon::NoCategory, + showCheckBox->isChecked() + ? Phonon::GlobalConfig::ShowAdvancedDevices + : Phonon::GlobalConfig::HideAdvancedDevices); + foreach (int i, deviceIndexes) { + ret.append(Phonon::AudioOutputDevice::fromIndex(i)); + } + return ret; } void OutputDeviceChoice::load() { QSettings phononConfig(QLatin1String("kde.org"), QLatin1String("libphonon")); QSettingsGroup outputDeviceGroup(&phononConfig, QLatin1String("AudioOutputDevice")); QSettingsGroup captureDeviceGroup(&phononConfig, QLatin1String("AudioCaptureDevice")); + QSettingsGroup generalGroup(&phononConfig, QLatin1String("General")); + showCheckBox->setChecked(!generalGroup.value(QLatin1String("HideAdvancedDevices"), true)); // the following call returns ordered according to NoCategory - QList list = Phonon::BackendCapabilities::availableAudioOutputDevices(); + const QList list = availableAudioOutputDevices(); + m_outputModel[Phonon::NoCategory]->setModelData(list); + QHash hash; - foreach (Phonon::AudioOutputDevice dev, list) { + foreach (const Phonon::AudioOutputDevice &dev, list) { hash.insert(dev.index(), dev); } for (int i = 0; i <= Phonon::LastCategory; ++i) { const QString configKey(QLatin1String("Category") + QString::number(i)); if (!outputDeviceGroup.hasKey(configKey)) { m_outputModel[i]->setModelData(list); // use the NoCategory order - m_outputModel[i]->setProperty("dirty", false); continue; } QHash hashCopy(hash); - QList order = outputDeviceGroup.value(configKey, QList()); + const QList order = outputDeviceGroup.value(configKey, QList()); QList orderedList; foreach (int idx, order) { if (hashCopy.contains(idx)) { orderedList << hashCopy.take(idx); } } if (hashCopy.size() > 1) { // keep the order of the original list foreach (const Phonon::AudioOutputDevice &dev, list) { if (hashCopy.contains(dev.index())) { orderedList << hashCopy.take(dev.index()); } } } else if (hashCopy.size() == 1) { orderedList += hashCopy.values(); } m_outputModel[i]->setModelData(orderedList); } /* QList list2 = Phonon::BackendCapabilities::availableAudioCaptureDevices(); QList order = captureDeviceGroup.value(QLatin1String("DeviceOrder"), QList()); QList orderedList; foreach (int idx, order) { for (int i = 0; i < list2.size(); ++i) { if (list2.at(i).index() == idx) { orderedList << list2.takeAt(i); break; // out of the inner for loop to get the next idx } } } - foreach (Phonon::AudioCaptureDevice dev, list2) { + foreach (const Phonon::AudioCaptureDevice &dev, list2) { orderedList << dev; } m_captureModel.setModelData(orderedList); */ + + deviceList->resizeColumnToContents(0); } void OutputDeviceChoice::save() { QSettings config(QLatin1String("kde.org"), QLatin1String("libphonon")); + { + QSettingsGroup generalGroup(&config, QLatin1String("General")); + generalGroup.setValue(QLatin1String("HideAdvancedDevices"), !showCheckBox->isChecked()); + } { QSettingsGroup globalGroup(&config, QLatin1String("AudioOutputDevice")); + const QList noCategoryOrder = m_outputModel.value(Phonon::NoCategory)->tupleIndexOrder(); + globalGroup.setValue(QLatin1String("Category") + QString::number(Phonon::NoCategory), noCategoryOrder); for (int i = 0; i <= Phonon::LastCategory; ++i) { - if (m_outputModel.value(i)) { - globalGroup.setValue(QLatin1String("Category") + QString::number(i), m_outputModel.value(i)->tupleIndexOrder()); + Phonon::AudioOutputDeviceModel *model = m_outputModel.value(i); + Q_ASSERT(model); + const QList order = m_outputModel.value(i)->tupleIndexOrder(); + if (order == noCategoryOrder) { + globalGroup.removeEntry(QLatin1String("Category") + QString::number(i)); + } else { + globalGroup.setValue(QLatin1String("Category") + QString::number(i), order); } } } /* { QSettingsGroup globalGroup(&config, QLatin1String("AudioCaptureDevice")); globalGroup.setValue(QLatin1String("DeviceOrder"), m_captureModel.tupleIndexOrder()); } */ } void OutputDeviceChoice::defaults() { - QList list = Phonon::BackendCapabilities::availableAudioOutputDevices(); - for (int i = 0; i <= Phonon::LastCategory; ++i) { + QList list = availableAudioOutputDevices(); + for (int i = -1; i <= Phonon::LastCategory; ++i) { m_outputModel[i]->setModelData(list); } /* QList list2 = Phonon::BackendCapabilities::availableAudioCaptureDevices(); m_captureModel.setModelData(list2); */ + + deviceList->resizeColumnToContents(0); } void OutputDeviceChoice::on_preferButton_clicked() { QAbstractItemModel *model = deviceList->model(); Phonon::AudioOutputDeviceModel *deviceModel = qobject_cast(model); if (deviceModel) { deviceModel->moveUp(deviceList->currentIndex()); updateButtonsEnabled(); emit changed(); } } void OutputDeviceChoice::on_deferButton_clicked() { QAbstractItemModel *model = deviceList->model(); Phonon::AudioOutputDeviceModel *deviceModel = qobject_cast(model); if (deviceModel) { deviceModel->moveDown(deviceList->currentIndex()); updateButtonsEnabled(); emit changed(); } } void OutputDeviceChoice::on_removeButton_clicked() { - QModelIndex idx = deviceList->currentIndex(); + const QModelIndex idx = deviceList->currentIndex(); QAbstractItemModel *model = deviceList->model(); Phonon::AudioOutputDeviceModel *playbackModel = qobject_cast(model); if (playbackModel && idx.isValid()) { - Phonon::AudioOutputDevice deviceToRemove = playbackModel->modelData(idx); - QList deviceList = Phonon::AudioDeviceEnumerator::availablePlaybackDevices(); + const Phonon::AudioOutputDevice deviceToRemove = playbackModel->modelData(idx); + const QList deviceList = Phonon::AudioDeviceEnumerator::availablePlaybackDevices(); foreach (Phonon::AudioDevice dev, deviceList) { if (dev.index() == deviceToRemove.index()) { // remove from persistent store if (dev.ceaseToExist()) { // remove from all models, idx.row() is only correct for the current model foreach (Phonon::AudioOutputDeviceModel *model, m_outputModel) { QList data = model->modelData(); for (int row = 0; row < data.size(); ++row) { if (data[row] == deviceToRemove) { model->removeRows(row, 1); break; } } } updateButtonsEnabled(); emit changed(); } } } /* } else { Phonon::AudioCaptureDeviceModel *captureModel = qobject_cast(model); if (captureModel && idx.isValid()) { Phonon::AudioCaptureDevice deviceToRemove = captureModel->modelData(idx); QList deviceList = Phonon::AudioDeviceEnumerator::availableCaptureDevices(); foreach (Phonon::AudioDevice dev, deviceList) { if (dev.index() == deviceToRemove.index()) { // remove from persistent store if (dev.ceaseToExist()) { m_captureModel.removeRows(idx.row(), 1); updateButtonsEnabled(); emit changed(); } } } } */ } + + deviceList->resizeColumnToContents(0); +} + +void operator++(Phonon::Category &c) +{ + c = static_cast(1 + static_cast(c)); +} + +void OutputDeviceChoice::on_applyPreferencesButton_clicked() +{ + const QModelIndex idx = categoryTree->currentIndex(); + const QStandardItem *item = m_categoryModel.itemFromIndex(idx); + Q_ASSERT(item->type() == 1001); + const CategoryItem *catItem = static_cast(item); + const QList modelData = m_outputModel.value(catItem->category())->modelData(); + + KDialog dialog(this); + dialog.setButtons(KDialog::Ok | KDialog::Cancel); + dialog.setDefaultButton(KDialog::Ok); + + QWidget mainWidget(&dialog); + dialog.setMainWidget(&mainWidget); + + QLabel label(&mainWidget); + label.setText(i18n("Apply the currently shown device preference list to the following other " + "audio output categories:")); + label.setWordWrap(true); + + KListWidget list(&mainWidget); + for (Phonon::Category cat = Phonon::NoCategory; cat <= Phonon::LastCategory; ++cat) { + QListWidgetItem *item = new QListWidgetItem(cat == Phonon::NoCategory + ? i18n("Default/Unspecified Category") : Phonon::categoryToString(cat), &list, cat); + item->setCheckState(Qt::Checked); + if (cat == catItem->category()) { + item->setFlags(item->flags() & ~Qt::ItemIsEnabled); + } + } + + QVBoxLayout layout(&mainWidget); + layout.setMargin(0); + layout.addWidget(&label); + layout.addWidget(&list); + + switch (dialog.exec()) { + case QDialog::Accepted: + for (Phonon::Category cat = Phonon::NoCategory; cat <= Phonon::LastCategory; ++cat) { + if (cat != catItem->category()) { + QListWidgetItem *item = list.item(static_cast(cat) + 1); + Q_ASSERT(item->type() == cat); + if (item->checkState() == Qt::Checked) { + m_outputModel.value(cat)->setModelData(modelData); + } + } + } + break; + case QDialog::Rejected: + // nothing to do + break; + } +} + +void OutputDeviceChoice::on_showCheckBox_toggled() +{ + // the following call returns ordered according to NoCategory + const QList list = availableAudioOutputDevices(); + m_outputModel[Phonon::NoCategory]->setModelData(list); + + QHash hash; + foreach (const Phonon::AudioOutputDevice &dev, list) { + hash.insert(dev.index(), dev); + } + for (int i = 0; i <= Phonon::LastCategory; ++i) { + QHash hashCopy(hash); + const QList order = m_outputModel[i]->tupleIndexOrder(); + QList orderedList; + foreach (int idx, order) { + if (hashCopy.contains(idx)) { + orderedList << hashCopy.take(idx); + } + } + if (hashCopy.size() > 1) { + // keep the order of the original list + foreach (const Phonon::AudioOutputDevice &dev, list) { + if (hashCopy.contains(dev.index())) { + orderedList << hashCopy.take(dev.index()); + } + } + } else if (hashCopy.size() == 1) { + orderedList += hashCopy.values(); + } + m_outputModel[i]->setModelData(orderedList); + } + deviceList->resizeColumnToContents(0); } void OutputDeviceChoice::updateButtonsEnabled() { //kDebug() ; if (deviceList->model()) { //kDebug() << "model available"; QModelIndex idx = deviceList->currentIndex(); preferButton->setEnabled(idx.isValid() && idx.row() > 0); deferButton->setEnabled(idx.isValid() && idx.row() < deviceList->model()->rowCount() - 1); removeButton->setEnabled(idx.isValid() && !(idx.flags() & Qt::ItemIsEnabled)); } else { preferButton->setEnabled(false); deferButton->setEnabled(false); removeButton->setEnabled(false); } } -#include "outputdevicechoice.moc" +#include "moc_outputdevicechoice.cpp" // vim: sw=4 ts=4 diff --git a/phonon/kcm/outputdevicechoice.h b/phonon/kcm/outputdevicechoice.h index f142c939e8..6dc9ca5632 100644 --- a/phonon/kcm/outputdevicechoice.h +++ b/phonon/kcm/outputdevicechoice.h @@ -1,61 +1,67 @@ /* This file is part of the KDE project Copyright (C) 2006-2007 Matthias Kretz This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) version 3. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef OUTPUTDEVICECHOICE_H_STUPID_UIC #define OUTPUTDEVICECHOICE_H_STUPID_UIC #include "ui_outputdevicechoice.h" #include #include #include #include #include +class QLabel; class OutputDeviceChoice : public QWidget, private Ui::OutputDeviceChoice { Q_OBJECT public: OutputDeviceChoice(QWidget *parent = 0); void load(); void save(); void defaults(); Q_SIGNALS: void changed(); protected: void changeEvent(QEvent *); private Q_SLOTS: void on_preferButton_clicked(); void on_deferButton_clicked(); void on_removeButton_clicked(); + void on_showCheckBox_toggled(); + void on_applyPreferencesButton_clicked(); void updateButtonsEnabled(); void updateDeviceList(); void updateAudioOutputDevices(); private: + QList availableAudioOutputDevices() const; QMap m_outputModel; //Phonon::AudioCaptureDeviceModel m_captureModel; QStandardItemModel m_categoryModel; //QStandardItem *m_captureItem; + QStandardItemModel m_headerModel; + bool m_noCategoryChangeEventQueued; }; #endif // OUTPUTDEVICECHOICE_H_STUPID_UIC diff --git a/phonon/kcm/outputdevicechoice.ui b/phonon/kcm/outputdevicechoice.ui index f3bc0a7588..912874a5fc 100644 --- a/phonon/kcm/outputdevicechoice.ui +++ b/phonon/kcm/outputdevicechoice.ui @@ -1,147 +1,217 @@ Matthias Kretz <kretz@kde.org> OutputDeviceChoice 0 0 - 538 - 300 + 600 + 400 - - - - - - 0 - 0 - + + + + + + + + 0 + 0 + + + + Qt::CustomContextMenu + + + Various categories of outputs. For each category you may choose what device you wish to output to. + + + Various categories of outputs. For each category you may choose what device you wish to output to. + + + false + + + + + + + 0 + + + + + Show Advanced Devices + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 0 + 20 + + + + + + + + + + 0 + + + + + Apply Device List to ... + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 0 + 20 + + + + + + + + + + + + true - Various categories of outputs. For each category you may choose what device you wish to output to. + Audio Outputs found on your system. Choose the device that you wish sound to come out of. - Various categories of outputs. For each category you may choose what device you wish to output to. + The order determines the preference of the output devices. If for some reason the first device cannot be used Phonon will try to use the second, and +so on. + + + Qt::ScrollBarAsNeeded + + + true + + + QAbstractItemView::InternalMove + + + QAbstractItemView::SelectRows + + + + 32 + 32 + + + + Qt::ElideNone + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false false - - - + + + + + + false + + + Remove + + + Qt::ToolButtonTextBesideIcon + + + + Qt::Horizontal 16 29 - - - - true - - - Audio Outputs found on your system. Choose the device that you wish sound to come out of. - - - The order determines the preference of the output devices. If for some reason the first device cannot be used Phonon will try to use the second, and -so on. - - - true - - - QAbstractItemView::InternalMove - - - QAbstractItemView::SelectRows - - - - 32 - 32 - - - - Qt::ElideNone - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - QListView::Adjust - - - true - - - - - + + false - no preference for the selected device - - - Defer - - - Qt::ToolButtonTextBesideIcon - - - - - - - false + prefer the selected device - Remove + Prefer Qt::ToolButtonTextBesideIcon - - + + false - prefer the selected device + no preference for the selected device - Prefer + Defer Qt::ToolButtonTextBesideIcon kdialog.h