summaryrefslogtreecommitdiff
path: root/src/ui/mixerwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/mixerwindow.cpp')
-rw-r--r--src/ui/mixerwindow.cpp381
1 files changed, 360 insertions, 21 deletions
diff --git a/src/ui/mixerwindow.cpp b/src/ui/mixerwindow.cpp
index 91cea65..caa6af5 100644
--- a/src/ui/mixerwindow.cpp
+++ b/src/ui/mixerwindow.cpp
@@ -1,44 +1,174 @@
#include "mixerwindow.h"
+#include "preferencesdlg.h"
#include "../model/pulsemodel.h"
#include <tqlayout.h>
#include <tqscrollview.h>
+#include <tqwidgetstack.h>
+#include <tqtoolbutton.h>
+#include <tqtooltip.h>
+#include <tqevent.h>
#include <ktabwidget.h>
#include <tdelocale.h>
+#include <kiconloader.h>
+#include <tdeglobal.h>
+#include <kstandarddirs.h>
+#include <tdeaction.h>
+#include <tdeactioncollection.h>
+#include <tdemenubar.h>
+#include <tdepopupmenu.h>
+#include <tdeaboutapplication.h>
+#include <tdeaboutdata.h>
+#include <tdeapplication.h>
+#include "tmixtray.h"
+#include "devicespage.h"
+#include <tdeconfig.h>
+#include <tdemessagebox.h>
+#include <kstdguiitem.h>
+
+// ---- tab helper -------------------------------------------------------------
static MixerWindow::Tab makeTab( KTabWidget *tabs, const TQString &label )
{
MixerWindow::Tab t;
- TQScrollView *scroll = new TQScrollView( tabs );
- scroll->setResizePolicy( TQScrollView::AutoOneFit );
- scroll->setHScrollBarMode( TQScrollView::Auto );
- scroll->setVScrollBarMode( TQScrollView::AlwaysOff );
- scroll->setFrameStyle( TQFrame::NoFrame );
-
- t.page = new TQWidget( scroll->viewport() );
- scroll->addChild( t.page );
+
+ 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( scroll, label );
+ tabs->addTab( sv, label );
return t;
}
MixerWindow::MixerWindow( PulseModel *model, TQWidget *parent )
- : TQWidget(parent), m_model(model)
+ : TDEMainWindow(parent), m_model(model), m_prefsDlg(0),
+ m_devicesBtn(0), m_devicesPage(0), m_devicesInTabs(false), m_quitting(false)
{
- setCaption( i18n("TMix") );
+ 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 );
- TQVBoxLayout *top = new TQVBoxLayout( this, 4, 0 );
- m_tabs = new KTabWidget( this );
- top->addWidget( m_tabs );
+ // 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[] = {
@@ -52,6 +182,37 @@ MixerWindow::MixerWindow( PulseModel *model, TQWidget *parent )
}
}
+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 ) {
@@ -65,21 +226,199 @@ MixerWindow::Tab &MixerWindow::tabForCategory( AudioDevice::Category cat )
void MixerWindow::onDeviceAdded( AudioDevice *dev )
{
- Tab &t = tabForCategory( dev->category() );
- TQWidget *w = dev->createWidget( t.page );
- t.layout->addWidget( w );
- w->show();
- m_widgets.insert( dev, w );
+ 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;
- Tab &t = tabForCategory( dev->category() );
- t.layout->remove( w );
+ 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<AudioDevice*, TQWidget*>::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<AudioDevice> devs = m_model->devices( cats[i] );
+ for ( TQPtrListIterator<AudioDevice> 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"