summaryrefslogtreecommitdiff
path: root/src/ui/devicespage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/devicespage.cpp')
-rw-r--r--src/ui/devicespage.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/ui/devicespage.cpp b/src/ui/devicespage.cpp
new file mode 100644
index 0000000..28cce07
--- /dev/null
+++ b/src/ui/devicespage.cpp
@@ -0,0 +1,251 @@
+#include "devicespage.h"
+#include "../model/pulsemodel.h"
+
+#include <tqscrollview.h>
+#include <tqlayout.h>
+#include <tqlabel.h>
+#include <tqcombobox.h>
+#include <tqgroupbox.h>
+#include <tqstringlist.h>
+#include <tqpixmap.h>
+#include <tqbitmap.h>
+#include <tqpainter.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kiconloader.h>
+
+#include <pulse/pulseaudio.h>
+
+static TQString portTypeIcon( uint32_t type )
+{
+ switch ( type ) {
+ case PA_DEVICE_PORT_TYPE_HEADPHONES: return "audio-headphones";
+ case PA_DEVICE_PORT_TYPE_HEADSET: return "audio-headset";
+ case PA_DEVICE_PORT_TYPE_MIC: return "audio-input-microphone";
+ case PA_DEVICE_PORT_TYPE_SPEAKER: return "audio-speakers";
+ case PA_DEVICE_PORT_TYPE_HDMI: return "video-display";
+ case PA_DEVICE_PORT_TYPE_SPDIF: return "audio-card";
+ case PA_DEVICE_PORT_TYPE_LINE: return "audio-card";
+ case PA_DEVICE_PORT_TYPE_USB: return "audio-card";
+ case PA_DEVICE_PORT_TYPE_BLUETOOTH: return "bluetooth";
+ default: return "audio-card";
+ }
+}
+
+static TQPixmap availDot( bool plugged, int size )
+{
+ TQPixmap pm( size, size );
+ TQBitmap mask( size, size, true );
+ {
+ TQPainter mp( &mask );
+ mp.setBrush( Qt::color1 );
+ mp.setPen( Qt::NoPen );
+ mp.drawEllipse( 1, 1, size-2, size-2 );
+ }
+ pm.setMask( mask );
+ pm.fill( plugged ? TQColor(80, 200, 80) : TQColor(160, 160, 160) );
+ {
+ TQPainter p( &pm );
+ p.setBrush( plugged ? TQColor(80, 200, 80) : TQColor(160, 160, 160) );
+ p.setPen( Qt::NoPen );
+ p.drawEllipse( 1, 1, size-2, size-2 );
+ }
+ return pm;
+}
+
+DevicesPage::DevicesPage( PulseModel *model, TQWidget *parent )
+ : TQWidget(parent), m_model(model), m_container(0)
+{
+ TQVBoxLayout *outer = new TQVBoxLayout( this, 0, 0 );
+ m_scroll = new TQScrollView( this );
+ m_scroll->setFrameStyle( TQFrame::NoFrame );
+ m_scroll->setResizePolicy( TQScrollView::AutoOneFit );
+ outer->addWidget( m_scroll );
+
+ rebuild();
+
+ connect( model, TQ_SIGNAL(cardAdded(uint32_t)), this, TQ_SLOT(onCardAdded(uint32_t)) );
+ connect( model, TQ_SIGNAL(cardRemoved(uint32_t)), this, TQ_SLOT(onCardRemoved(uint32_t)) );
+ connect( model, TQ_SIGNAL(cardUpdated(uint32_t)), this, TQ_SLOT(onCardUpdated(uint32_t)) );
+}
+
+void DevicesPage::rebuild()
+{
+ m_comboCard.clear();
+ m_profileNames.clear();
+ m_cardCombo.clear();
+ m_cardPorts.clear();
+
+ delete m_container;
+ m_container = new TQWidget( m_scroll->viewport() );
+ m_scroll->addChild( m_container );
+
+ TQVBoxLayout *vbox = new TQVBoxLayout( m_container, 8, 6 );
+
+ TQValueList<PulseCardInfo> cards = m_model->cards();
+ for ( TQValueList<PulseCardInfo>::Iterator it = cards.begin(); it != cards.end(); ++it ) {
+ PulseCardInfo &info = *it;
+
+ TQGroupBox *grp = new TQGroupBox( info.description, m_container );
+ grp->setColumnLayout( 0, Qt::Vertical );
+ grp->layout()->setSpacing( 4 );
+ grp->layout()->setMargin( 8 );
+ vbox->addWidget( grp );
+
+ TQGridLayout *grid = new TQGridLayout( grp->layout(), 0, 2, 4 );
+ grid->setColStretch( 1, 1 );
+ int gridRow = 0;
+
+ auto addRow = [&]( const TQString &labelText, TQWidget *valueWidget ) {
+ TQLabel *lbl = new TQLabel( labelText, grp );
+ lbl->setAlignment( TQt::AlignRight | TQt::AlignVCenter );
+ grid->addWidget( lbl, gridRow, 0 );
+ grid->addWidget( valueWidget, gridRow, 1 );
+ ++gridRow;
+ };
+ auto addTextRow = [&]( const TQString &labelText, const TQString &valueText ) {
+ addRow( labelText, new TQLabel( valueText, grp ) );
+ };
+
+ if ( !info.vendor.isEmpty() )
+ addTextRow( i18n("Vendor:"), info.vendor );
+ if ( !info.product.isEmpty() )
+ addTextRow( i18n("Product:"), info.product );
+ TQStringList typeparts;
+ if ( !info.formFactor.isEmpty() ) typeparts << info.formFactor;
+ if ( !info.busType.isEmpty() ) typeparts << info.busType;
+ if ( !typeparts.isEmpty() )
+ addTextRow( i18n("Type:"), typeparts.join(" / ") );
+
+ TQComboBox *combo = new TQComboBox( false, grp );
+ combo->setSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Fixed );
+ addRow( i18n("Profile:"), combo );
+
+ TQValueList<TQString> names;
+ int activeIdx = 0, i = 0;
+ for ( TQValueList<PulseCardProfile>::Iterator pit = info.profiles.begin();
+ pit != info.profiles.end(); ++pit, ++i ) {
+ combo->insertItem( pit->description );
+ names.append( pit->name );
+ if ( pit->name == info.activeProfile )
+ activeIdx = i;
+ }
+ if ( info.profiles.count() > 0 )
+ combo->setCurrentItem( activeIdx );
+
+ m_profileNames[combo] = names;
+ m_comboCard[combo] = info.index;
+ m_cardCombo[info.index] = combo;
+
+ connect( combo, TQ_SIGNAL(activated(int)), this, TQ_SLOT(onProfileActivated(int)) );
+
+ // Ports — 2-column grid to keep tall cards compact
+ if ( !info.ports.isEmpty() ) {
+ TQLabel *portsHdr = new TQLabel( i18n("Ports:"), grp );
+ portsHdr->setAlignment( TQt::AlignRight | TQt::AlignTop );
+ TQWidget *portsWidget = new TQWidget( grp );
+ TQGridLayout *portsGrid = new TQGridLayout( portsWidget, 0, 2, 0, 4 );
+ portsGrid->setColStretch( 0, 1 );
+ portsGrid->setColStretch( 1, 1 );
+
+ TQValueList<PortWidgets> portWidgetList;
+ int portIdx = 0;
+ for ( TQValueList<PulseCardPort>::Iterator pit = info.ports.begin();
+ pit != info.ports.end(); ++pit, ++portIdx ) {
+ bool plugged = ( pit->available != PA_PORT_AVAILABLE_NO );
+
+ TQWidget *row = new TQWidget( portsWidget );
+ TQHBoxLayout *hbox = new TQHBoxLayout( row, 0, 4 );
+
+ TQLabel *dot = new TQLabel( row );
+ dot->setPixmap( availDot( plugged, 10 ) );
+ dot->setFixedSize( 10, 10 );
+
+ TQLabel *icon = new TQLabel( row );
+ TQPixmap px = TDEGlobal::iconLoader()->loadIcon(
+ portTypeIcon( pit->type ), TDEIcon::Small, 16,
+ TDEIcon::DefaultState, 0, true );
+ if ( !px.isNull() )
+ icon->setPixmap( px );
+ icon->setFixedSize( 16, 16 );
+
+ TQLabel *name = new TQLabel( pit->description, row );
+
+ hbox->addWidget( dot );
+ hbox->addWidget( icon );
+ hbox->addWidget( name );
+ hbox->addStretch( 1 );
+
+ portsGrid->addWidget( row, portIdx / 2, portIdx % 2 );
+
+ PortWidgets pw;
+ pw.dot = dot;
+ portWidgetList.append( pw );
+ }
+ m_cardPorts[info.index] = portWidgetList;
+
+ grid->addWidget( portsHdr, gridRow, 0, TQt::AlignRight | TQt::AlignTop );
+ grid->addWidget( portsWidget, gridRow, 1 );
+ ++gridRow;
+ }
+ }
+
+ vbox->addStretch( 1 );
+ m_container->show();
+}
+
+void DevicesPage::onCardAdded( uint32_t ) { rebuild(); }
+void DevicesPage::onCardRemoved( uint32_t ) { rebuild(); }
+
+void DevicesPage::onCardUpdated( uint32_t index )
+{
+ TQMap<uint32_t, TQComboBox*>::Iterator cit = m_cardCombo.find( index );
+ if ( cit == m_cardCombo.end() ) return;
+ TQComboBox *combo = cit.data();
+
+ const PulseCardInfo *info = m_model->card( index );
+ if ( !info ) return;
+
+ // Update active profile in combo
+ TQMap<TQComboBox*, TQValueList<TQString> >::Iterator nit = m_profileNames.find( combo );
+ if ( nit != m_profileNames.end() ) {
+ TQValueList<TQString> &names = nit.data();
+ for ( int i = 0; i < (int)names.count(); ++i ) {
+ if ( names[i] == info->activeProfile ) {
+ combo->blockSignals( true );
+ combo->setCurrentItem( i );
+ combo->blockSignals( false );
+ break;
+ }
+ }
+ }
+
+ // Update port availability indicators in-place
+ TQMap<uint32_t, TQValueList<PortWidgets> >::Iterator pit = m_cardPorts.find( index );
+ if ( pit == m_cardPorts.end() ) return;
+ TQValueList<PortWidgets> &portWidgets = pit.data();
+
+ int i = 0;
+ for ( TQValueList<PulseCardPort>::ConstIterator p = info->ports.begin();
+ p != info->ports.end() && i < (int)portWidgets.count(); ++p, ++i ) {
+ bool plugged = ( (*p).available != PA_PORT_AVAILABLE_NO );
+ portWidgets[i].dot->setPixmap( availDot( plugged, 10 ) );
+ }
+}
+
+void DevicesPage::onProfileActivated( int idx )
+{
+ TQComboBox *combo = dynamic_cast<TQComboBox*>( const_cast<TQObject*>( sender() ) );
+ if ( !combo ) return;
+
+ TQMap<TQComboBox*, uint32_t>::Iterator cit = m_comboCard.find( combo );
+ if ( cit == m_comboCard.end() ) return;
+ uint32_t cardIndex = cit.data();
+
+ TQMap<TQComboBox*, TQValueList<TQString> >::Iterator nit = m_profileNames.find( combo );
+ if ( nit == m_profileNames.end() || idx >= (int)nit.data().count() ) return;
+
+ m_model->setCardProfile( cardIndex, nit.data()[idx] );
+}
+
+#include "devicespage.moc"