#include "mixerwindow.h" #include "preferencesdlg.h" #include "../model/pulsemodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tmixtray.h" #include "devicespage.h" #include #include #include // ---- tab helper ------------------------------------------------------------- static MixerWindow::Tab makeTab( KTabWidget *tabs, const TQString &label ) { MixerWindow::Tab t; TQScrollView *sv = new TQScrollView( tabs ); sv->setFrameStyle( TQFrame::NoFrame ); sv->setHScrollBarMode( TQScrollView::Auto ); sv->setVScrollBarMode( TQScrollView::AlwaysOff ); sv->setResizePolicy( TQScrollView::AutoOneFit ); t.page = new TQWidget( sv->viewport() ); sv->addChild( t.page, 0, 0 ); t.layout = new TQHBoxLayout( t.page, 6, 4 ); t.layout->addStretch( 1 ); t.scroll = sv; t.count = 0; tabs->addTab( sv, label ); return t; } MixerWindow::MixerWindow( PulseModel *model, TQWidget *parent ) : TDEMainWindow(parent), m_model(model), m_prefsDlg(0), m_devicesBtn(0), m_devicesPage(0), m_devicesInTabs(false), m_quitting(false) { setPlainCaption( i18n("Volume Control") ); setIcon( TDEGlobal::iconLoader()->loadIcon( "kmix", TDEIcon::Desktop, 48 ) ); // ---- menu bar ----------------------------------------------------------- TDEPopupMenu *fileMenu = new TDEPopupMenu( this ); fileMenu->insertItem( SmallIcon("application-exit"), i18n("&Quit"), this, TQ_SLOT(quit()) ); TDEPopupMenu *settingsMenu = new TDEPopupMenu( this ); settingsMenu->insertItem( SmallIcon("configure"), i18n("&Configure TMix..."), this, TQ_SLOT(showPreferences()) ); TDEPopupMenu *helpMenu = new TDEPopupMenu( this ); helpMenu->insertItem( SmallIcon("help"), i18n("&About TMix"), this, TQ_SLOT(showAbout()) ); menuBar()->insertItem( i18n("&File"), fileMenu ); menuBar()->insertItem( i18n("&Settings"), settingsMenu ); menuBar()->insertItem( i18n("&Help"), helpMenu ); // ---- system tray -------------------------------------------------------- m_tray = new TmixTray( this ); m_tray->setModel( model ); connect( model, TQ_SIGNAL(defaultOutputChanged(AudioDevice*)), this, TQ_SLOT(onDefaultOutputChanged(AudioDevice*)) ); onDefaultOutputChanged( model->defaultOutput() ); TDEConfig *cfg = TDEGlobal::config(); cfg->setGroup("General"); if ( cfg->readBoolEntry("DockInTray", true) ) m_tray->show(); // ---- central widget ----------------------------------------------------- TQWidget *central = new TQWidget( this ); setCentralWidget( central ); setMinimumSize( 300, 200 ); TQVBoxLayout *top = new TQVBoxLayout( central, 0, 0 ); // Outer stack: page 0 = mixer content, page 1 = devices (no-tab mode only) m_outerStack = new TQWidgetStack( central ); top->addWidget( m_outerStack, 1 ); TQWidget *mixerPage = new TQWidget( m_outerStack ); TQVBoxLayout *mixerLayout = new TQVBoxLayout( mixerPage, 0, 0 ); m_outerStack->addWidget( mixerPage, 0 ); m_outerStack->raiseWidget( 0 ); m_stack = new TQWidgetStack( mixerPage ); mixerLayout->addWidget( m_stack ); // Page 0: tabbed view m_tabs = new KTabWidget( m_stack ); m_stack->addWidget( m_tabs, 0 ); m_output = makeTab( m_tabs, i18n("Output") ); m_input = makeTab( m_tabs, i18n("Input") ); m_playback = makeTab( m_tabs, i18n("Playback") ); m_recording = makeTab( m_tabs, i18n("Recording") ); // Page 1: all-in-one flat view (scrollable) { TQScrollView *sv = new TQScrollView( m_stack ); sv->setFrameStyle( TQFrame::NoFrame ); sv->setHScrollBarMode( TQScrollView::Auto ); sv->setVScrollBarMode( TQScrollView::AlwaysOff ); sv->setResizePolicy( TQScrollView::AutoOneFit ); m_allPage = sv; m_allStrip = new TQWidget( sv->viewport() ); sv->addChild( m_allStrip, 0, 0 ); m_allLayout = new TQHBoxLayout( m_allStrip, 2, 4 ); m_allLayout->addStretch( 1 ); m_allCount = 0; m_stack->addWidget( sv, 1 ); } // Floating toggle button — child of central widget so TQWidgetStack page // switches don't hide it; positioned over m_outerStack's bottom-right corner. m_devicesBtn = new TQToolButton( central ); m_devicesBtn->setIconSet( SmallIconSet("configure") ); m_devicesBtn->resize( 24, 24 ); TQToolTip::add( m_devicesBtn, i18n("Device Configuration") ); connect( m_devicesBtn, TQ_SIGNAL(clicked()), this, TQ_SLOT(toggleDevices()) ); central->installEventFilter( this ); // DevicesPage — placement depends on tab/no-tab mode (set below) m_devicesPage = new DevicesPage( model, this ); { TDEConfig *c = TDEGlobal::config(); c->setGroup("View"); bool noTabs = c->readBoolEntry("NoTabs", false); m_stack->raiseWidget( noTabs ? 1 : 0 ); // Place DevicesPage in the right home for the initial mode if ( noTabs ) { m_devicesPage->reparent( m_outerStack, 0, TQPoint(), false ); m_outerStack->addWidget( m_devicesPage, 1 ); m_devicesInTabs = false; } else { m_devicesPage->reparent( m_tabs, 0, TQPoint(), false ); m_tabs->addTab( m_devicesPage, i18n("Devices") ); m_devicesInTabs = true; } } setAutoSaveSettings( "MainWindow" ); // Position button after layout is set; hide if in tab mode (has its own tab) repositionDevicesBtn(); m_devicesBtn->setShown( !m_devicesInTabs ); connect( model, TQ_SIGNAL(deviceAdded(AudioDevice*)), this, TQ_SLOT(onDeviceAdded(AudioDevice*)) ); connect( model, TQ_SIGNAL(deviceRemoved(AudioDevice*)), this, TQ_SLOT(onDeviceRemoved(AudioDevice*)) ); connect( tqApp, TQ_SIGNAL(aboutToQuit()), this, TQ_SLOT(onAppAboutToQuit()) ); // Populate with any devices already known at construction time. AudioDevice::Category cats[] = { AudioDevice::Output, AudioDevice::Input, AudioDevice::Playback, AudioDevice::Recording }; for ( int i = 0; i < 4; i++ ) { TQPtrList devs = model->devices( cats[i] ); for ( TQPtrListIterator it(devs); *it; ++it ) onDeviceAdded( *it ); } } bool MixerWindow::queryClose() { if ( m_quitting || tdeApp->sessionSaving() ) return true; TDEConfig *cfg = TDEGlobal::config(); cfg->setGroup("General"); if ( cfg->readBoolEntry("DockInTray", true) ) { saveMainWindowSettings( TDEGlobal::config(), "MainWindow" ); TDEConfig *c = TDEGlobal::config(); c->setGroup("MainWindow"); c->writeEntry("XPos", x()); c->writeEntry("YPos", y()); c->sync(); hide(); return false; } return true; } void MixerWindow::showAbout() { TDEAboutData about( "tmix", I18N_NOOP("TMix"), "0.1", I18N_NOOP("Volume control for TDE / PulseAudio"), TDEAboutData::License_GPL, "(c) 2026", 0, 0, 0 ); TDEAboutApplication dlg( &about, this ); dlg.exec(); } MixerWindow::Tab &MixerWindow::tabForCategory( AudioDevice::Category cat ) { switch ( cat ) { case AudioDevice::Output: return m_output; case AudioDevice::Input: return m_input; case AudioDevice::Playback: return m_playback; case AudioDevice::Recording: return m_recording; } return m_output; } void MixerWindow::onDeviceAdded( AudioDevice *dev ) { if ( m_stack->id( m_stack->visibleWidget() ) == 1 ) { TQWidget *w = dev->createWidget( m_allStrip ); m_allLayout->insertWidget( m_allCount, w ); m_allCount++; w->show(); m_widgets.insert( dev, w ); } else { Tab &t = tabForCategory( dev->category() ); TQWidget *w = dev->createWidget( t.page ); t.layout->insertWidget( t.count, w ); t.count++; w->show(); m_widgets.insert( dev, w ); } } void MixerWindow::onDefaultOutputChanged( AudioDevice *dev ) { m_tray->setDevice( dev ); } void MixerWindow::onDeviceRemoved( AudioDevice *dev ) { TQWidget *w = m_widgets[dev]; if ( !w ) return; if ( m_stack->id( m_stack->visibleWidget() ) == 1 ) { m_allLayout->remove( w ); m_allCount--; } else { Tab &t = tabForCategory( dev->category() ); t.layout->remove( w ); t.count--; } m_widgets.remove( dev ); delete w; } void MixerWindow::quit() { TDEConfig *cfg = TDEGlobal::config(); cfg->setGroup("General"); if ( cfg->readBoolEntry("ConfirmQuit", false) ) { int r = KMessageBox::questionYesNo( this, i18n("Are you sure you want to quit TMix?"), i18n("Quit TMix"), KStdGuiItem::quit(), KStdGuiItem::cancel() ); if ( r != KMessageBox::Yes ) return; } m_quitting = true; tqApp->quit(); } void MixerWindow::onAppAboutToQuit() { m_quitting = true; } void MixerWindow::showPreferences() { if ( !m_prefsDlg ) { m_prefsDlg = new PreferencesDlg( this ); connect( m_prefsDlg, TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(applySettings()) ); } m_prefsDlg->load(); m_prefsDlg->show(); m_prefsDlg->raise(); m_prefsDlg->setActiveWindow(); } void MixerWindow::rebuildView() { // Tear down all existing device widgets TQMap::iterator it; for ( it = m_widgets.begin(); it != m_widgets.end(); ++it ) delete it.data(); m_widgets.clear(); m_output.count = m_input.count = m_playback.count = m_recording.count = 0; m_allCount = 0; TDEConfig *cfg = TDEGlobal::config(); cfg->setGroup("View"); bool noTabs = cfg->readBoolEntry("NoTabs", false); m_stack->raiseWidget( noTabs ? 1 : 0 ); if ( noTabs && m_devicesInTabs ) { // Move DevicesPage from m_tabs → m_outerStack m_tabs->removePage( m_devicesPage ); m_devicesPage->reparent( m_outerStack, 0, TQPoint(), false ); m_outerStack->addWidget( m_devicesPage, 1 ); m_outerStack->raiseWidget( 0 ); updateDevicesButton(); m_devicesInTabs = false; m_devicesBtn->show(); repositionDevicesBtn(); } else if ( !noTabs && !m_devicesInTabs ) { // Move DevicesPage from m_outerStack → m_tabs m_outerStack->removeWidget( m_devicesPage ); m_devicesPage->reparent( m_tabs, 0, TQPoint(), false ); m_tabs->addTab( m_devicesPage, i18n("Devices") ); m_outerStack->raiseWidget( 0 ); m_devicesInTabs = true; m_devicesBtn->hide(); } // Repopulate into whichever view is now active AudioDevice::Category cats[] = { AudioDevice::Output, AudioDevice::Input, AudioDevice::Playback, AudioDevice::Recording }; for ( int i = 0; i < 4; i++ ) { TQPtrList devs = m_model->devices( cats[i] ); for ( TQPtrListIterator jt(devs); *jt; ++jt ) onDeviceAdded( *jt ); } } void MixerWindow::applySettings() { TDEConfig *cfg = TDEGlobal::config(); cfg->setGroup("General"); if ( cfg->readBoolEntry("DockInTray", true) ) m_tray->show(); else m_tray->hide(); cfg->setGroup("View"); bool noTabs = cfg->readBoolEntry("NoTabs", false); if ( noTabs != ( m_stack->id( m_stack->visibleWidget() ) == 1 ) ) { rebuildView(); } else { // Tabs mode: apply per-tab visibility m_tabs->setTabEnabled( m_output.scroll, cfg->readBoolEntry("ShowOutput", true) ); m_tabs->setTabEnabled( m_input.scroll, cfg->readBoolEntry("ShowInput", true) ); m_tabs->setTabEnabled( m_playback.scroll, cfg->readBoolEntry("ShowPlayback", true) ); m_tabs->setTabEnabled( m_recording.scroll, cfg->readBoolEntry("ShowRecording", true) ); } } void MixerWindow::toggleDevices() { int next = ( m_outerStack->id( m_outerStack->visibleWidget() ) == 0 ) ? 1 : 0; m_outerStack->raiseWidget( next ); updateDevicesButton(); repositionDevicesBtn(); m_devicesBtn->raise(); } void MixerWindow::updateDevicesButton() { if ( m_outerStack->id( m_outerStack->visibleWidget() ) == 0 ) { m_devicesBtn->setIconSet( SmallIconSet("configure") ); TQToolTip::add( m_devicesBtn, i18n("Device Configuration") ); } else { m_devicesBtn->setIconSet( SmallIconSet("kmix") ); TQToolTip::add( m_devicesBtn, i18n("Mixer") ); } } void MixerWindow::repositionDevicesBtn() { int bw = m_devicesBtn->width() ? m_devicesBtn->width() : 24; int bh = m_devicesBtn->height() ? m_devicesBtn->height() : 24; // Position in bottom-right of m_outerStack, mapped to centralWidget coords TQPoint origin = m_outerStack->mapTo( centralWidget(), TQPoint(0, 0) ); int x = origin.x() + m_outerStack->width() - bw - 4; int y = origin.y() + m_outerStack->height() - bh - 4; m_devicesBtn->move( x, y ); m_devicesBtn->raise(); } bool MixerWindow::eventFilter( TQObject *obj, TQEvent *e ) { if ( obj == centralWidget() && e->type() == TQEvent::Resize ) repositionDevicesBtn(); return false; } void MixerWindow::showEvent( TQShowEvent *e ) { TDEMainWindow::showEvent( e ); repositionDevicesBtn(); TDEConfig *c = TDEGlobal::config(); c->setGroup("MainWindow"); if ( c->hasKey("XPos") && c->hasKey("YPos") ) move( c->readNumEntry("XPos"), c->readNumEntry("YPos") ); } #include "mixerwindow.moc"