summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2021-11-04 14:47:38 +0100
committerSimon Rettberg2021-11-04 14:47:38 +0100
commita11c084f904c21607bd6f925b2280e95d5592082 (patch)
treea4393bc7889a5cc81f64cfc4b5dfc9da9f5b002a
parent"Fix" compile on older distros by killing off i18n for now (diff)
downloadpavucontrol-slx-a11c084f904c21607bd6f925b2280e95d5592082.tar.gz
pavucontrol-slx-a11c084f904c21607bd6f925b2280e95d5592082.tar.xz
pavucontrol-slx-a11c084f904c21607bd6f925b2280e95d5592082.zip
Try to group the output configurations in the last tab
This introduces a second combobox that is supposed to list the different output configurations for a selected output. The idea is to list the most resonable configration first (i.e. make it the default) and have the primary combobox only list the different outputs themselves. Otherwise the UI might appear quite overwhelming to a novice user, depending on their hardware. Some platforms for exmaple list 4-7 HDMI ports each with stereo, 5.1 and 7.1 configurations. Having all this in a flat list is confusing and bad UX, as it doesn't even fit on a single screen. Unfortunately the pa-api isn't hierarchical and pretty much enumerates the configurations exactly the way they get displayed by pavucontrol, so we need to employ some fuzzy matching logic for grouping, which might or might not need some refining in the future.
-rw-r--r--src/cardwidget.cc137
-rw-r--r--src/cardwidget.h27
-rw-r--r--src/cardwidget.ui53
-rw-r--r--src/mainwindow.cc21
4 files changed, 162 insertions, 76 deletions
diff --git a/src/cardwidget.cc b/src/cardwidget.cc
index d5b4e63..3b4c8b3 100644
--- a/src/cardwidget.cc
+++ b/src/cardwidget.cc
@@ -24,46 +24,59 @@
#include "cardwidget.h"
+#include <QDebug>
+#include <QHash>
+#include <QSet>
+
/*** CardWidget ***/
CardWidget::CardWidget(QWidget* parent) :
QWidget(parent) {
setupUi(this);
connect(profileList, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &CardWidget::onProfileChange);
- connect(profileCB, &QAbstractButton::toggled, this, &CardWidget::onProfileCheck);
+ connect(profileFlavorList, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &CardWidget::onProfileFlavorChange);
}
void CardWidget::prepareMenu() {
int idx = 0;
- const bool off = activeProfile == noInOutProfile;
profileList->clear();
/* Fill the ComboBox */
- for (const auto & profile : profiles) {
- QByteArray name = profile.first;
- // skip the "off" profile
- if (name == noInOutProfile)
- continue;
- QString desc = QString::fromUtf8(profile.second);
- profileList->addItem(desc, name);
- if (profile.first == activeProfile
- || (off && profile.first == lastActiveProfile)
- )
- {
+ for (auto k : profiles.keys()) {
+ auto & profile = profiles[k];
+ auto desc = profile.getProfileName();
+ profileList->addItem(desc, k);
+ if (profile.containsProfile(activeProfile)) {
profileList->setCurrentIndex(idx);
- lastActiveProfile = profile.first;
+ changeProfile(k);
}
++idx;
}
-
- profileCB->setChecked(!off);
}
-void CardWidget::changeProfile(const QByteArray & name)
+void CardWidget::changeProfile(const QString & name)
{
+ auto &profile = profiles[name];
+ auto old = profileFlavorList->currentText();
+ profileFlavorList->clear();
+ int idx = 0;
+ for (auto & entry : profile.entries) {
+ auto name = entry.getName();
+ profileFlavorList->addItem(name, entry.id);
+ if (name == old || entry.id == activeProfile) {
+ profileFlavorList->setCurrentIndex(idx);
+ }
+ ++idx;
+ }
+ if (profileFlavorList->currentIndex() == -1) {
+ profileFlavorList->setCurrentIndex(0);
+ }
+}
+
+void CardWidget::changeProfileFlavor(const QByteArray & id) {
pa_operation* o;
- if (!(o = pa_context_set_card_profile_by_index(get_context(), index, name.constData(), nullptr, nullptr))) {
+ if (!(o = pa_context_set_card_profile_by_index(get_context(), index, id.constData(), nullptr, nullptr))) {
show_error(tr("pa_context_set_card_profile_by_index() failed").toUtf8().constData());
return;
}
@@ -76,17 +89,91 @@ void CardWidget::onProfileChange(int active) {
return;
if (active != -1)
- changeProfile(profileList->itemData(active).toByteArray());
+ changeProfile(profileList->itemData(active).toString());
}
-void CardWidget::onProfileCheck(bool on)
-{
+void CardWidget::onProfileFlavorChange(int active) {
if (updating)
return;
- if (on)
- onProfileChange(profileList->currentIndex());
- else
- changeProfile(noInOutProfile);
+ if (active != -1)
+ changeProfileFlavor(profileFlavorList->itemData(active).toByteArray());
+}
+
+QString ProfileGroup::getProfileName() {
+ if (!name.isEmpty())
+ return name;
+ // TODO
+ QHash<QString, int> counts;
+ qDebug() << "Entries" << this;
+ for (auto n : entries) {
+ qDebug() << n.tokens;
+ QSet<QString> tmp = n.tokens.toSet(); //(n.tokens.begin(), n.tokens.end());
+ for (auto t : tmp) {
+ counts[t]++;
+ }
+ }
+ qDebug() << counts;
+ QSet<QString> todo = counts.keys().toSet();
+ for (auto &pe : entries) {
+ QMutableListIterator<QString> it(pe.tokens);
+ while (it.hasNext()) {
+ auto s = it.next();
+ if (counts[s] == entries.size()) {
+ it.remove();
+ if (todo.remove(s)) {
+ if (!name.isEmpty()) {
+ name.append(QLatin1Char(' '));
+ }
+ name.append(s);
+ }
+ }
+ }
+ }
+ return name;
+}
+
+void ProfileGroup::addEntry(const char *id, const char *name) {
+ auto *pe = new ProfileEntry;
+ bool paren = false;
+ const char *pos = name, *tokenStart = name;
+ qDebug() << name << "is" << id;
+ while (*pos != '\0') {
+ if (!paren && isspace(*pos)) {
+ if (pos > tokenStart) {
+ qDebug() << tokenStart;
+ pe->tokens.append(QString::fromUtf8(tokenStart, pos - tokenStart));
+ }
+ tokenStart = pos + 1;
+ }
+ if (paren && *pos == ')') {
+ qDebug() << tokenStart;
+ pe->tokens.append(QString::fromUtf8(tokenStart, pos - tokenStart + 1));
+ paren = false;
+ tokenStart = pos + 1;
+ }
+ if (tokenStart == pos && *pos == '(') {
+ paren = true;
+ }
+ pos++;
+ }
+ if (pos > tokenStart) {
+ qDebug() << tokenStart;
+ pe->tokens.append(QString::fromUtf8(tokenStart, pos - tokenStart));
+ }
+ pe->id = id;
+ this->entries.append(*pe);
+ delete pe;
+}
+
+QString ProfileEntry::getName() const {
+ return tokens.join(QLatin1String(" "));
+}
+bool ProfileGroup::containsProfile(const QByteArray &pro) const {
+ for (const auto &e : this->entries) {
+ if (e.id == pro)
+ return true;
+ }
+ return false;
}
diff --git a/src/cardwidget.h b/src/cardwidget.h
index 71f6ebc..7bd87d2 100644
--- a/src/cardwidget.h
+++ b/src/cardwidget.h
@@ -24,6 +24,23 @@
#include "pavucontrol.h"
#include "ui_cardwidget.h"
#include <QWidget>
+#include <QMap>
+#include <QString>
+#include <QList>
+
+struct ProfileEntry {
+ QByteArray id;
+ QStringList tokens;
+ QString getName() const;
+};
+
+struct ProfileGroup {
+ QString name;
+ QList<ProfileEntry> entries;
+ QString getProfileName();
+ void addEntry(const char* id, const char* name);
+ bool containsProfile(const QByteArray &pro) const;
+};
class PortInfo {
public:
@@ -45,20 +62,20 @@ public:
uint32_t index;
bool updating;
- std::vector< std::pair<QByteArray,QByteArray> > profiles;
+
+ QMap<QString, ProfileGroup> profiles;
std::map<QByteArray, PortInfo> ports;
QByteArray activeProfile;
- QByteArray noInOutProfile;
- QByteArray lastActiveProfile;
bool hasSinks;
bool hasSources;
void prepareMenu();
protected:
- void changeProfile(const QByteArray & name);
+ void changeProfile(const QString & name);
+ void changeProfileFlavor(const QByteArray & id);
void onProfileChange(int active);
- void onProfileCheck(bool on);
+ void onProfileFlavorChange(int active);
};
diff --git a/src/cardwidget.ui b/src/cardwidget.ui
index 28ea7ab..a75654b 100644
--- a/src/cardwidget.ui
+++ b/src/cardwidget.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>368</width>
- <height>79</height>
+ <height>106</height>
</rect>
</property>
<property name="windowTitle">
@@ -33,14 +33,7 @@
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="profileHLayout" stretch="0,0,1">
- <item>
- <widget class="QCheckBox" name="profileCB">
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
+ <layout class="QHBoxLayout" name="profileHLayout" stretch="0,1">
<item>
<widget class="QLabel" name="profileLabel">
<property name="text">
@@ -54,6 +47,13 @@
</layout>
</item>
<item>
+ <layout class="QHBoxLayout" name="variantHLayout">
+ <item>
+ <widget class="QComboBox" name="profileFlavorList"/>
+ </item>
+ </layout>
+ </item>
+ <item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -63,38 +63,5 @@
</layout>
</widget>
<resources/>
- <connections>
- <connection>
- <sender>profileCB</sender>
- <signal>toggled(bool)</signal>
- <receiver>profileLabel</receiver>
- <slot>setEnabled(bool)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>137</x>
- <y>47</y>
- </hint>
- <hint type="destinationlabel">
- <x>147</x>
- <y>47</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>profileCB</sender>
- <signal>toggled(bool)</signal>
- <receiver>profileList</receiver>
- <slot>setEnabled(bool)</slot>
- <hints>
- <hint type="sourcelabel">
- <x>137</x>
- <y>47</y>
- </hint>
- <hint type="destinationlabel">
- <x>315</x>
- <y>47</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</ui>
diff --git a/src/mainwindow.cc b/src/mainwindow.cc
index 76e81bf..66c0ac4 100644
--- a/src/mainwindow.cc
+++ b/src/mainwindow.cc
@@ -22,6 +22,7 @@
#include <config.h>
#endif
+#include <QDebug>
#include <set>
#include "mainwindow.h"
@@ -258,9 +259,23 @@ void MainWindow::updateCard(const pa_card_info &info) {
if (!p_profile->available)
desc += tr(" (unavailable)").toUtf8().constData();
- w->profiles.push_back(std::pair<QByteArray,QByteArray>(p_profile->name, desc));
- if (p_profile->n_sinks == 0 && p_profile->n_sources == 0)
- w->noInOutProfile = p_profile->name;
+ QString parseId = QString::fromUtf8(p_profile->name);
+ int plus = parseId.indexOf(QLatin1Char('+'));
+ if (plus != -1) {
+ parseId = parseId.left(plus);
+ }
+ auto list = parseId.split(QRegularExpression(QLatin1String("[:\\-]")));
+ parseId.clear();
+ for (auto p : list) {
+ if (p == QLatin1String("input") || p == QLatin1String("output"))
+ continue;
+ if (parseId.isEmpty() || p.startsWith(QLatin1String("extra"))) {
+ parseId.append(p);
+ }
+ }
+ qDebug() << "ParseID:" << parseId;
+ ProfileGroup &group = w->profiles[parseId];
+ group.addEntry(p_profile->name, desc.constData());
}
w->activeProfile = info.active_profile ? info.active_profile->name : "";