#include "devicespage.h" #include "../model/pulsemodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include 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 cards = m_model->cards(); for ( TQValueList::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 names; int activeIdx = 0, i = 0; for ( TQValueList::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 portWidgetList; int portIdx = 0; for ( TQValueList::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::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 >::Iterator nit = m_profileNames.find( combo ); if ( nit != m_profileNames.end() ) { TQValueList &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 >::Iterator pit = m_cardPorts.find( index ); if ( pit == m_cardPorts.end() ) return; TQValueList &portWidgets = pit.data(); int i = 0; for ( TQValueList::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( const_cast( sender() ) ); if ( !combo ) return; TQMap::Iterator cit = m_comboCard.find( combo ); if ( cit == m_comboCard.end() ) return; uint32_t cardIndex = cit.data(); TQMap >::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"