diff --git a/applet/contents/ui/main.qml b/applet/contents/ui/main.qml --- a/applet/contents/ui/main.qml +++ b/applet/contents/ui/main.qml @@ -219,6 +219,7 @@ Layout.maximumHeight: contentHeight model: PulseObjectFilterModel { + filters: [ { role: "VirtualStream", value: false } ] sourceModel: SinkInputModel {} } boundsBehavior: Flickable.StopAtBounds; @@ -238,6 +239,7 @@ Layout.maximumHeight: contentHeight model: PulseObjectFilterModel { + filters: [ { role: "VirtualStream", value: false } ] sourceModel: SourceOutputModel {} } boundsBehavior: Flickable.StopAtBounds; @@ -264,8 +266,12 @@ Layout.minimumHeight: contentHeight Layout.maximumHeight: contentHeight - model: SinkModel { - id: sinkModel + model: PulseObjectFilterModel { + sortRole: "SortByDefault" + sortOrder: Qt.DescendingOrder + sourceModel: SinkModel { + id: sinkModel + } } boundsBehavior: Flickable.StopAtBounds; delegate: DeviceListItem {} @@ -283,8 +289,12 @@ Layout.minimumHeight: contentHeight Layout.maximumHeight: contentHeight - model: SourceModel { - id: sourceModel + model: PulseObjectFilterModel { + sortRole: "SortByDefault" + sortOrder: Qt.DescendingOrder + sourceModel: SourceModel { + id: sourceModel + } } boundsBehavior: Flickable.StopAtBounds; delegate: DeviceListItem {} diff --git a/src/kcm/package/contents/ui/main.qml b/src/kcm/package/contents/ui/main.qml --- a/src/kcm/package/contents/ui/main.qml +++ b/src/kcm/package/contents/ui/main.qml @@ -39,6 +39,9 @@ title: i18nc("@title:tab", "Applications") SinkInputView { model: PulseObjectFilterModel { + sortRole: "EventStream" + sortOrder: Qt.DescendingOrder + filters: [ { role: "VirtualStream", value: false } ] sourceModel: SinkInputModel {} } emptyText: i18nc("@label", "No Applications Playing Audio") @@ -48,6 +51,7 @@ title: i18nc("@title:tab", "Recording") SourceOutputView { model: PulseObjectFilterModel { + filters: [ { role: "VirtualStream", value: false } ] sourceModel: SourceOutputModel {} } emptyText: i18nc("@label", "No Applications Recording Audio") diff --git a/src/pulseaudio.h b/src/pulseaudio.h --- a/src/pulseaudio.h +++ b/src/pulseaudio.h @@ -40,7 +40,7 @@ QHash roleNames() const Q_DECL_FINAL; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; bool setData(const QModelIndex &index, const QVariant &value, int role) Q_DECL_FINAL; Q_INVOKABLE int role(const QByteArray &roleName) const; @@ -80,8 +80,14 @@ Q_OBJECT Q_PROPERTY(QPulseAudio::Sink *defaultSink READ defaultSink NOTIFY defaultSinkChanged) public: + enum ItemRole { + SortByDefaultRole = PulseObjectRole + 1 + }; + Q_ENUMS(ItemRole) + SinkModel(QObject *parent = nullptr); Sink *defaultSink() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; signals: void defaultSinkChanged(); @@ -99,8 +105,14 @@ Q_OBJECT Q_PROPERTY(QPulseAudio::Source *defaultSource READ defaultSource NOTIFY defaultSourceChanged) public: + enum ItemRole { + SortByDefaultRole = PulseObjectRole + 1 + }; + Q_ENUMS(ItemRole) + SourceModel(QObject *parent = nullptr); Source *defaultSource() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; signals: void defaultSourceChanged(); diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -92,8 +92,36 @@ void AbstractModel::initRoleNames(const QMetaObject &qobjectMetaObject) { m_roles[PulseObjectRole] = QByteArrayLiteral("PulseObject"); - int maxEnumValue = PulseObjectRole; + QMetaEnum enumerator; + for (int i = 0; i < metaObject()->enumeratorCount(); ++i) { + if (metaObject()->enumerator(i).name() == QLatin1String("ItemRole")) { + enumerator = metaObject()->enumerator(i); + break; + } + } + + Q_ASSERT(enumerator.scope() == metaObject()->className()); + // No valid enum found, leaf probably doesn't implement ItemRole (correctly). + Q_ASSERT(enumerator.isValid()); + + for (int i = 0; i < enumerator.keyCount(); ++i) { + // Clip the Role suffix and glue it in the hash. + const int roleLength = 4; + QByteArray key(enumerator.key(i)); + // Enum values must end in Role or the enum is crap + Q_ASSERT(key.right(roleLength) == QByteArrayLiteral("Role")); + key.chop(roleLength); + m_roles[enumerator.value(i)] = key; + } + + int maxEnumValue = -1; + for (auto it = m_roles.constBegin(); it != m_roles.constEnd(); ++it) { + if (it.key() > maxEnumValue) { + maxEnumValue = it.key(); + } + } + Q_ASSERT(maxEnumValue != -1); auto mo = qobjectMetaObject; for (int i = 0; i < mo.propertyCount(); ++i) { QMetaProperty property = mo.property(i); @@ -175,6 +203,17 @@ return context()->server()->defaultSink(); } +QVariant SinkModel::data(const QModelIndex &index, int role) const +{ + if (role == SortByDefaultRole) { + // Workaround QTBUG-1548 + const QString pulseIndex = data(index, AbstractModel::role(QByteArrayLiteral("Index"))).toString(); + const QString defaultDevice = data(index, AbstractModel::role(QByteArrayLiteral("Default"))).toString(); + return defaultDevice + pulseIndex; + } + return AbstractModel::data(index, role); +} + SourceModel::SourceModel(QObject *parent) : AbstractModel(&context()->sources(), parent) { @@ -188,6 +227,17 @@ return context()->server()->defaultSource(); } +QVariant SourceModel::data(const QModelIndex &index, int role) const +{ + if (role == SortByDefaultRole) { + // Workaround QTBUG-1548 + const QString pulseIndex = data(index, AbstractModel::role(QByteArrayLiteral("Index"))).toString(); + const QString defaultDevice = data(index, AbstractModel::role(QByteArrayLiteral("Default"))).toString(); + return defaultDevice + pulseIndex; + } + return AbstractModel::data(index, role); +} + SinkInputModel::SinkInputModel(QObject *parent) : AbstractModel(&context()->sinkInputs(), parent) { diff --git a/src/qml/PulseObjectFilterModel.qml b/src/qml/PulseObjectFilterModel.qml --- a/src/qml/PulseObjectFilterModel.qml +++ b/src/qml/PulseObjectFilterModel.qml @@ -1,8 +1,16 @@ import org.kde.plasma.core 2.1 as PlasmaCore PlasmaCore.SortFilterModel { + property var filters: [] + filterCallback: function(source_row, value) { var idx = sourceModel.index(source_row, 0); - return !sourceModel.data(idx, sourceModel.role("VirtualStream")); + for (var i = 0; i < filters.length; ++i) { + var filter = filters[i]; + if (sourceModel.data(idx, sourceModel.role(filter.role)) != filter.value) { + return false; + } + } + return true; } }