diff options
Diffstat (limited to 'src/ui/devicespage.cpp')
| -rw-r--r-- | src/ui/devicespage.cpp | 251 |
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" |
