diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | e2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch) | |
tree | 9047cf9e6b5c43878d5bf82660adae77ceee097a /kmix | |
download | tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kmix')
126 files changed, 14612 insertions, 0 deletions
diff --git a/kmix/AUTHORS b/kmix/AUTHORS new file mode 100644 index 00000000..8f72f47d --- /dev/null +++ b/kmix/AUTHORS @@ -0,0 +1,11 @@ +kmix +Written by Christian Esken (esken@kde.org) +and Stefan Schimanski (1Stein@gmx.de) +SGI port done Paul Kendall (paul@orion.co.nz) +Fixes for FreeBSD Sebestyen Zoltan (szoli@digo.inf.elte.hu) +Solaris fixes Faraut Jean-Louis <jlf@essi.fr> +ALSA port Nick Lopez <kimo_sabe@usa.net> +ALSA 0.9.x port Helio Chissini de Castro <helio@conectiva.com> +HP-UX port Helge Deller <deller@gmx.de> + +The package is maintained by Christian Esken (esken@kde.org) diff --git a/kmix/ChangeLog b/kmix/ChangeLog new file mode 100644 index 00000000..df5588b3 --- /dev/null +++ b/kmix/ChangeLog @@ -0,0 +1,10 @@ +V1.90 +Version shipped with KDE3.1 + +V1.91 +- Multiple soundcards on ALSA are now supported +- Mixer device names are now shown with a vertical label (saves space and looks nice) +- MixerDevice categories. Allows to distribute devices on "New Mixer Tab...". It is used + in the panel applet to show only important devices initally. +- Much nicer "New Mixer Tab..." dialog + diff --git a/kmix/KMixApp.cpp b/kmix/KMixApp.cpp new file mode 100644 index 00000000..74b84f43 --- /dev/null +++ b/kmix/KMixApp.cpp @@ -0,0 +1,71 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org> + * Copyright (C) 2001 Preston Brown <pbrown@kde.org> + * Copyright (C) 2003 Sven Leiber <s.leiber@web.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "KMixApp.h" +#include "kmix.h" +#include <kdebug.h> + + +KMixApp::KMixApp() + : KUniqueApplication(), m_kmix( 0 ) +{ +} + + +KMixApp::~KMixApp() +{ + delete m_kmix; +} + + +int +KMixApp::newInstance() +{ + if ( m_kmix ) + { + m_kmix->show(); + } + else + { + m_kmix = new KMixWindow; + connect(this, SIGNAL(stopUpdatesOnVisibility()), m_kmix, SLOT(stopVisibilityUpdates())); + if ( isRestored() && KMainWindow::canBeRestored(0) ) + { + m_kmix->restore(0, FALSE); + } + } + + return 0; +} + + +void +KMixApp::quitExtended() +{ + // This method is here for quiting from the dock icon: When directly calling + // quit(), the main window will be hidden before saving the configuration. + // isVisible() would return on quit always false (which would be bad). + emit stopUpdatesOnVisibility(); + quit(); +} + +#include "KMixApp.moc" diff --git a/kmix/KMixApp.h b/kmix/KMixApp.h new file mode 100644 index 00000000..79e4fc4e --- /dev/null +++ b/kmix/KMixApp.h @@ -0,0 +1,26 @@ +#ifndef KMixApp_h +#define KMixApp_h + +#include <kuniqueapplication.h> + +class KMixWindow; + +class KMixApp : public KUniqueApplication +{ +Q_OBJECT + public: + KMixApp(); + ~KMixApp(); + int newInstance (); + + public slots: + void quitExtended(); // For a hack on visibility() + + signals: + void stopUpdatesOnVisibility(); + + private: + KMixWindow *m_kmix; +}; + +#endif diff --git a/kmix/Makefile.am b/kmix/Makefile.am new file mode 100644 index 00000000..307c8168 --- /dev/null +++ b/kmix/Makefile.am @@ -0,0 +1,63 @@ +SUBDIRS = pics +INCLUDES= $(all_includes) + +bin_PROGRAMS = +lib_LTLIBRARIES = +kdeinit_LTLIBRARIES = kmix.la kmixctrl.la +kde_module_LTLIBRARIES = kmix_panelapplet.la + + +noinst_HEADERS = kmix.h kmixdockwidget.h kmixprefdlg.h kmixerwidget.h \ + viewbase.h viewoutput.h viewinput.h viewsliders.h viewswitches.h viewsurround.h viewdockareapopup.h viewgrid.h \ + mixdevicewidget.h mdwslider.h mdwswitch.h mdwenum.h \ + mixer.h mixset.h mixdevice.h mixer_backend.h volume.h kledbutton.h version.h kmixtoolbox.h \ + ksmallslider.h kmixapplet.h mixerIface.h verticaltext.h \ + KMixApp.h dialogviewconfiguration.h kmixtoolbox.h mixertoolbox.h dialogselectmaster.h + + +METASOURCES = AUTO + +kmix_la_SOURCES = main.cpp kmix.cpp kmixdockwidget.cpp kmixprefdlg.cpp \ + viewbase.cpp viewoutput.cpp viewinput.cpp viewswitches.cpp viewsurround.cpp viewdockareapopup.cpp \ + viewsliders.cpp viewgrid.cpp \ + mixdevicewidget.cpp mdwslider.cpp mdwswitch.cpp mdwenum.cpp \ + kmixerwidget.cpp mixer.cpp mixset.cpp mixdevice.cpp mixer_backend.cpp ksmallslider.cpp \ + volume.cpp kledbutton.cpp verticaltext.cpp mixerIface.skel \ + kmixtoolbox.cpp mixertoolbox.cpp dialogviewconfiguration.cpp KMixApp.cpp dialogselectmaster.cpp + +kmix_la_LIBADD = $(LIB_KDEUI) $(LIB_KUTILS) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND) +kmix_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -avoid-version + +kmixctrl_la_SOURCES = kmixctrl.cpp mixer.cpp mixset.cpp mixdevice.cpp volume.cpp mixerIface.skel \ + mixertoolbox.cpp mixer_backend.cpp + +kmixctrl_la_LIBADD = $(LIB_KDECORE) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND) +kmixctrl_la_LDFLAGS = $(all_libraries) -module -avoid-version + +kmix_panelapplet_la_SOURCES = kmixapplet.cpp \ + viewbase.cpp viewapplet.cpp \ + mixdevicewidget.cpp mdwslider.cpp \ + mixer.cpp mixset.cpp mixdevice.cpp mixer_backend.cpp ksmallslider.cpp volume.cpp kledbutton.cpp \ + verticaltext.cpp mixerIface.skel colorwidget.ui dialogviewconfiguration.cpp \ + kmixtoolbox.cpp mixertoolbox.cpp dialogselectmaster.cpp + +kmix_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kmix_panelapplet_la_LIBADD = $(LIB_KDEUI) $(LIB_KUTILS) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND) + +xdg_apps_DATA = kmix.desktop + +rcdir = $(kde_datadir)/kmix +rc_DATA = kmixui.rc + +autostart_DATA = restore_kmix_volumes.desktop +autostartdir = $(datadir)/autostart + +lnkdir = $(kde_datadir)/kicker/applets +lnk_DATA = kmixapplet.desktop + +servicesdir = $(kde_servicesdir) +services_DATA = kmixctrl_restore.desktop + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kmix.pot + diff --git a/kmix/TODO b/kmix/TODO new file mode 100644 index 00000000..c48ea54c --- /dev/null +++ b/kmix/TODO @@ -0,0 +1,47 @@ +TODO list + +Bug2 in KMix2.1pre: +- Keys not saved/restored (pending) +- DockApplet shows "show/hide menubar" (DONE) +- "reversed" in panelapplet is broken. Remove it for now (DONE) +- Initial paint of sliders in KMixApplet is broken. (DONE) +- PanelApplet width wrong. (DONE) + +IMPORTANT: +1) Get Switches working +MUTE-LED's : Read: OK, Click: OK , Saved/Restored: no (was not in KMix2.0 and is shifted) +Record-LED's: Read: OK, Click: OK , Saved/Restored: yes +Switches : Read: OK, Click: OK , Saved/Restored: yes +Dockarea : Read: OK, Click: OK , Saved/Restored: n/a (its a view, not HW) + +2a) Splitted sliders and balance +OK +Splitted sliders: Read: OK, Click: OK, Drag: OK, Wheel: OK +Balance slider : OK (works like in KMix2.0) + +3) Mouse wheel +OK + +4) Make Volume Tip work (is currently always "0%") +OK + +5) Switches MUST be restored properly +OK + +6) kmixapplet restoring and working +OK + +7) Keys are not saved +Pending + + +14.6.2004 Christian Esken +I believe Mixer_IRIX is broken. +This is due to the old "writeVolumeToHW" and "readVolumeFromHW" interface. + +14 december 2002 - Helio Chissini de Castro +- Figure out devices like SBLive with external Live Drives and their multiple in/outs. As a sample, using headphone output from live drive, mute switch not work as they must, but we need mutting the headphone lfe and center channel to really mute headphone output. + +- Introduce a new widget to enable route control in pro's ( Turtle ) and pro like ( Audigy, Live ) cards. + + diff --git a/kmix/TestCases b/kmix/TestCases new file mode 100644 index 00000000..edca34e0 --- /dev/null +++ b/kmix/TestCases @@ -0,0 +1,36 @@ +These are the recommended test cases for KMix. +They should be tested before a new KDE version is being shipped. + +01: Basic: Start KMix with existing profile (kmixrc) +02: Basic: Close KMix -> Dock +03: Basic: Close KMix -> Exit +04: Show/Hide Labels +05: Show/Hide Tickmarks +06: New Mixer Tab ("distribute" and "no distribute") +07: System tray volume control +08: Start KMix with no Mixer Hardware/Drivers available +09: Basic: Start KMix with NO existing profile (kmixrc) +10: Basic: KMix KControl module +11: kmixctrl --restore (with existing .kmixctrlrc) must restore volumes +12: kmixctrl --restore (without existing .kmixctrlrc) must NOT change volumes + + +-----------+----+----+----+----+----+----+----+----+----+----+----+----+ +TestCase> | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | +Version | | | | | | | | | | | | | +-----------+----+----+----+----+----+----+----+----+----+----+----+----+ +CVS20030502| OK | OK | OK | OK | OK | OK | OK | | | | | | +-----------+----+----+----+----+----+----+----+ | +----+ | | +CVS20031102| | | | | | na | | | | OK | | | +-----------+----+----+----+----+----+----+----+ | +----+ | | +KDE3.2 | OK | | | | | na | | | | | | | +-----------+----+----+----+----+----+----+----+----+----+----+----+----+ +KDE3.4 | OK | OK | OK | OK | OK | na | OK | OK | OK | na | OK | OK | +-----------+----+----+----+----+----+----+----+----+----+----+----+----+ + +Test case results: +OK : Fully OK + : Not tested +xx : Failure +na : not applicable (Feature discontinued) + diff --git a/kmix/colorwidget.ui b/kmix/colorwidget.ui new file mode 100644 index 00000000..56f1d60e --- /dev/null +++ b/kmix/colorwidget.ui @@ -0,0 +1,276 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>ColorWidget</class> +<author>Stefan Schimanski <1Stein@gmx.de></author> +<widget class="QWidget"> + <property name="name"> + <cstring>ColorWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>272</width> + <height>305</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>customColors</cstring> + </property> + <property name="text"> + <string>&Use custom colors</string> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>activeColors</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Active</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>activeBack</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel3</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Silent:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>activeLow</cstring> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>activeLow</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>activeHigh</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>labelLoad</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Loud:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>activeHigh</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel4</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Background:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>activeBack</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>mutedColors</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Muted</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel6</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Lou&d:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mutedHigh</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel8</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Backgrou&nd:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mutedBack</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel7</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Silen&t:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mutedLow</cstring> + </property> + </widget> + <widget class="KColorButton" row="0" column="1"> + <property name="name"> + <cstring>mutedHigh</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>mutedLow</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>mutedBack</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<connections> + <connection> + <sender>customColors</sender> + <signal>toggled(bool)</signal> + <receiver>activeColors</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>customColors</sender> + <signal>toggled(bool)</signal> + <receiver>mutedColors</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>customColors</tabstop> + <tabstop>activeHigh</tabstop> + <tabstop>activeLow</tabstop> + <tabstop>activeBack</tabstop> + <tabstop>mutedHigh</tabstop> + <tabstop>mutedLow</tabstop> + <tabstop>mutedBack</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in declaration">klocale.h</include> + <include location="global" impldecl="in declaration">kseparator.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kmix/configure.in.in b/kmix/configure.in.in new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/kmix/configure.in.in diff --git a/kmix/dialogselectmaster.cpp b/kmix/dialogselectmaster.cpp new file mode 100644 index 00000000..44f38626 --- /dev/null +++ b/kmix/dialogselectmaster.cpp @@ -0,0 +1,197 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qbuttongroup.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qptrlist.h> +#include <qradiobutton.h> +#include <qscrollview.h> +#include <qtooltip.h> +#include <qvbox.h> + +#include <kcombobox.h> +#include <kdebug.h> +#include <kdialogbase.h> +#include <klocale.h> + +#include "dialogselectmaster.h" +#include "mixdevice.h" +#include "mixer.h" + +DialogSelectMaster::DialogSelectMaster( Mixer *mixer ) + : KDialogBase( Plain, i18n( "Select Master Channel" ), Ok|Cancel, Ok ) +{ + _layout = 0; + m_vboxForScrollView = 0; + createWidgets(mixer); // Open with Mixer Hardware #0 + +} + +DialogSelectMaster::~DialogSelectMaster() +{ + delete _layout; + delete m_vboxForScrollView; +} + +/** + * Create basic widgets of the Dialog. + */ +void DialogSelectMaster::createWidgets(Mixer *ptr_mixer) +{ + QFrame *m_mainFrame = plainPage(); + _layout = new QVBoxLayout(m_mainFrame,0,-1, "_layout" ); + + if ( Mixer::mixers().count() > 1 ) { + //kdDebug(67100) << "DialogSelectMaster::createPage count()>1" << "\n"; + // More than one Mixer => show Combo-Box to select Mixer + // Mixer widget line + QHBoxLayout* mixerNameLayout = new QHBoxLayout( _layout ); + //widgetsLayout->setStretchFactor( mixerNameLayout, 0 ); + //QSizePolicy qsp( QSizePolicy::Ignored, QSizePolicy::Maximum); + //mixerNameLayout->setSizePolicy(qsp); + mixerNameLayout->setSpacing(KDialog::spacingHint()); + + QLabel *qlbl = new QLabel( i18n("Current Mixer"), m_mainFrame ); + mixerNameLayout->addWidget(qlbl); + qlbl->setFixedHeight(qlbl->sizeHint().height()); + + m_cMixer = new KComboBox( FALSE, m_mainFrame, "mixerCombo" ); + m_cMixer->setFixedHeight(m_cMixer->sizeHint().height()); + connect( m_cMixer, SIGNAL( activated( int ) ), this, SLOT( createPageByID( int ) ) ); + + //int id=1; + for ( Mixer *mixer = Mixer::mixers().first(); mixer !=0; mixer = Mixer::mixers().next() ) { + m_cMixer->insertItem( mixer->mixerName() ); + if ( ptr_mixer == mixer ) { + // Make the current Mixer the current item in the ComboBos + m_cMixer->setCurrentItem( m_cMixer->count()-1 ); + } + //id++; + } // end for all_Mixers + + QToolTip::add( m_cMixer, i18n("Current mixer" ) ); + mixerNameLayout->addWidget(m_cMixer); + + } // end if (more_than_1_Mixer) + + QLabel *qlbl = new QLabel( i18n("Select the channel representing the master volume:"), m_mainFrame ); + _layout->addWidget(qlbl); + + m_scrollableChannelSelector = new QScrollView(m_mainFrame, "scrollableChannelSelector"); + m_scrollableChannelSelector->viewport()->setBackgroundMode(Qt::PaletteBackground); + _layout->add(m_scrollableChannelSelector); + + m_buttonGroupForScrollView = new QButtonGroup(this); // invisible QButtonGroup + m_buttonGroupForScrollView->hide(); + + createPage(ptr_mixer); + connect( this, SIGNAL(okClicked()) , this, SLOT(apply()) ); +} + +/** + * Create RadioButton's for the Mixer with number 'mixerId'. + * @par mixerId The Mixer, for which the RadioButton's should be created. + */ +void DialogSelectMaster::createPageByID(int mixerId) +{ + //kdDebug(67100) << "DialogSelectMaster::createPage()" << endl; + Mixer *mixer = Mixer::mixers().at(mixerId); + if ( mixer == 0 ) { + kdError(67100) << "DialogSelectMaster::createPage(): Invalid Mixer (mixerID=" << mixerId << ")" << endl; + return; // can not happen + } + createPage(mixer); +} + +/** + * Create RadioButton's for the Mixer with number 'mixerId'. + * @par mixerId The Mixer, for which the RadioButton's should be created. + */ +void DialogSelectMaster::createPage(Mixer* mixer) +{ + + /** --- Reset page ----------------------------------------------- + * In case the user selected a new Mixer via m_cMixer, we need + * to remove the stuff created on the last call. + */ + // delete the VBox. This should automatically remove all contained QRadioButton's. + delete m_vboxForScrollView; + m_mixerPKs.clear(); + /** Reset page end -------------------------------------------------- */ + + m_vboxForScrollView = new QVBox(m_scrollableChannelSelector->viewport()); + m_scrollableChannelSelector->addChild(m_vboxForScrollView); + + QString masterKey = "----noMaster---"; // Use a non-matching name as default + MixDevice* master = mixer->masterDevice(); + if ( master != 0 ) masterKey = master->getPK(); + + const MixSet& mixset = mixer->getMixSet(); + MixSet& mset = const_cast<MixSet&>(mixset); + for( MixDevice* md = mset.first(); md != 0; md = mset.next() ) + { + // Create a RadioButton for each MixDevice (excluding Enum's) + if ( ! md->isEnum() && ! md->isSwitch() ) { + //kdDebug(67100) << "DialogSelectMaster::createPage() mset append qrb" << endl; + QString mdName = md->name(); + mdName.replace('&', "&&"); // Quoting the '&' needed, to prevent QRadioButton creating an accelerator + QRadioButton* qrb = new QRadioButton( mdName, m_vboxForScrollView); + m_buttonGroupForScrollView->insert(qrb); //(qrb, md->num()); + //_qEnabledCB.append(qrb); + m_mixerPKs.push_back(md->getPK()); + if ( md->getPK() == masterKey ) { + qrb->setChecked(true); // preselect the current master + } + else { + qrb->setChecked(false); + } + } + } + + m_vboxForScrollView->show(); // show() is neccesary starting with the second call to createPage() +} + + +void DialogSelectMaster::apply() +{ + int soundcard_id = 0; + if ( Mixer::mixers().count() > 1 ) { + soundcard_id = m_cMixer->currentItem(); + } + int channel_id = m_buttonGroupForScrollView->selectedId(); + if ( channel_id != -1 ) { + // A channel was selected by the user => emit the "newMasterSelected()" signal + //kdDebug(67100) << "DialogSelectMaster::apply(): card=" << soundcard_id << ", channel=" << channel_id << endl; + Mixer *mixer = Mixer::mixers().at(soundcard_id); + if ( mixer == 0 ) { + kdError(67100) << "DialogSelectMaster::createPage(): Invalid Mixer (mixerID=" << soundcard_id << ")" << endl; + return; // can not happen + } + else { + mixer->setMasterDevice( m_mixerPKs[channel_id] ); + emit newMasterSelected(soundcard_id, m_mixerPKs[channel_id] ); + } + } +} + +#include "dialogselectmaster.moc" + diff --git a/kmix/dialogselectmaster.h b/kmix/dialogselectmaster.h new file mode 100644 index 00000000..912d870d --- /dev/null +++ b/kmix/dialogselectmaster.h @@ -0,0 +1,43 @@ +#ifndef DIALOGSELECTMASTER_H +#define DIALOGSELECTMASTER_H + +class QButtonGroup; +#include <qradiobutton.h> +class QScrollView; +#include <qstringlist.h> +class QVBox; +class QVBoxLayout; + +class KComboBox; +#include <kdialogbase.h> + +class Mixer; + +class DialogSelectMaster : public KDialogBase +{ + Q_OBJECT + public: + DialogSelectMaster(Mixer *); + ~DialogSelectMaster(); + + signals: + void newMasterSelected(int, QString& ); + + public slots: + void apply(); + + private: + void createWidgets(Mixer*); + void createPage(Mixer*); + QVBoxLayout* _layout; + KComboBox* m_cMixer; + QScrollView* m_scrollableChannelSelector; + QVBox *m_vboxForScrollView; + QButtonGroup *m_buttonGroupForScrollView; + QStringList m_mixerPKs; + + private slots: + void createPageByID(int mixerId); +}; + +#endif diff --git a/kmix/dialogviewconfiguration.cpp b/kmix/dialogviewconfiguration.cpp new file mode 100644 index 00000000..1c94ccf7 --- /dev/null +++ b/kmix/dialogviewconfiguration.cpp @@ -0,0 +1,102 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qcheckbox.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qptrlist.h> + +#include <kdebug.h> +#include <kdialogbase.h> +#include <klocale.h> + +#include "dialogviewconfiguration.h" +#include "mixdevicewidget.h" +#include "mixdevice.h" + + +DialogViewConfiguration::DialogViewConfiguration( QWidget*, ViewBase& view) + : KDialogBase( Plain, i18n( "Configure" ), Ok|Cancel, Ok ), + _view(view) +{ + QPtrList<QWidget> &mdws = view._mdws; + _layout = new QVBoxLayout(plainPage(),0,-1, "_layout" ); + + // kdDebug(67100) << "DialogViewConfiguration::DialogViewConfiguration add header" << "\n"; + QLabel* qlb = new QLabel( i18n("Configure"), plainPage() ); + //QLabel* qlb = new QLabel( i18n("Show"), plainPage() ); + _layout->addWidget(qlb); + + for ( QWidget *qw = mdws.first(); qw != 0; qw = mdws.next()) + { + if ( qw->inherits("MixDeviceWidget") ) { + MixDeviceWidget *mdw = static_cast<MixDeviceWidget*>(qw); + QString mdName = mdw->mixDevice()->name(); + mdName.replace('&', "&&"); // Quoting the '&' needed, to prevent QCheckBox creating an accelerator + QCheckBox* cb = new QCheckBox( mdName, plainPage() ); + _qEnabledCB.append(cb); + cb->setChecked( !mdw->isDisabled() ); //mdw->isVisible() ); + _layout->addWidget(cb); + } + } + _layout->activate(); + resize(_layout->sizeHint() ); + connect( this, SIGNAL(okClicked()) , this, SLOT(apply()) ); +} + +DialogViewConfiguration::~DialogViewConfiguration() +{ +} + +void DialogViewConfiguration::apply() +{ + QPtrList<QWidget> &mdws = _view._mdws; + + // --- 2-Step Apply --- + + // --- Step 1: Show and Hide Widgets --- + QCheckBox *cb = _qEnabledCB.first(); + for ( QWidget *qw = mdws.first(); qw != 0; qw = mdws.next()) + { + if ( qw->inherits("MixDeviceWidget") ) { + MixDeviceWidget *mdw = static_cast<MixDeviceWidget*>(qw); + if ( cb->isChecked() ) { + mdw->setDisabled(false); + } + else { + mdw->setDisabled(true); + } + + cb = _qEnabledCB.next(); + } + } + + // --- Step 2: Tell the view, that it has changed (probably it needs some "polishing" --- + _view.configurationUpdate(); +} + +QSize DialogViewConfiguration::sizeHint() const { + // kdDebug(67100) << "DialogViewConfiguration::sizeHint() is (100,500)\n"; + return _layout->sizeHint(); +} + +#include "dialogviewconfiguration.moc" + diff --git a/kmix/dialogviewconfiguration.h b/kmix/dialogviewconfiguration.h new file mode 100644 index 00000000..d63d1031 --- /dev/null +++ b/kmix/dialogviewconfiguration.h @@ -0,0 +1,30 @@ +#ifndef DIALOGVIEWCONFIGURATION_H +#define DIALOGVIEWCONFIGURATION_H + +#include <qcheckbox.h> +#include <qptrlist.h> +class QVBoxLayout; + +#include <kdialogbase.h> + +#include "viewbase.h" + + +class DialogViewConfiguration : public KDialogBase +{ + Q_OBJECT + public: + DialogViewConfiguration(QWidget* parent, ViewBase& view); + ~DialogViewConfiguration(); + + QSize sizeHint() const; + public slots: + void apply(); + + private: + QVBoxLayout* _layout; + ViewBase& _view; + QPtrList<QCheckBox> _qEnabledCB; +}; + +#endif diff --git a/kmix/doc/ControlNames.txt b/kmix/doc/ControlNames.txt new file mode 100644 index 00000000..5b18298e --- /dev/null +++ b/kmix/doc/ControlNames.txt @@ -0,0 +1,84 @@ +This document describes standard names of mixer controls. + +Syntax: SOURCE [DIRECTION] FUNCTION + +DIRECTION: + <nothing> (both directions) + Playback + Capture + Bypass Playback + Bypass Capture + +FUNCTION: + Switch (on/off switch) + Volume + Route (route control, hardware specific) + +SOURCE: + Master + Master Mono + Hardware Master + Headphone + PC Speaker + Phone + Phone Input + Phone Output + Synth + FM + Mic + Line + CD + Video + Zoom Video + Aux + PCM + PCM Front + PCM Rear + PCM Pan + Loopback + Analog Loopback (D/A -> A/D loopback) + Digital Loopback (playback -> capture loopback - without analog path) + Mono + Mono Output + Multi + ADC + Wave + Music + I2S + IEC958 + +Exceptions: + [Digital] Capture Source + [Digital] Capture Switch (aka input gain switch) + [Digital] Capture Volume (aka input gain volume) + [Digital] Playback Switch (aka output gain switch) + [Digital] Playback Volume (aka output gain volume) + Tone Control - Switch + Tone Control - Bass + Tone Control - Treble + 3D Control - Switch + 3D Control - Center + 3D Control - Depth + 3D Control - Wide + 3D Control - Space + 3D Control - Level + Mic Boost [(?dB)] + +PCM interface: + + Sample Clock Source { "Word", "Internal", "AutoSync" } + Clock Sync Status { "Lock", "Sync", "No Lock" } + External Rate /* external capture rate */ + Capture Rate /* capture rate taken from external source */ + +IEC958 (S/PDIF) interface: + + IEC958 [...] [Playback|Capture] Switch /* turn on/off the IEC958 interface */ + IEC958 [...] [Playback|Capture] Volume /* digital volume control */ + IEC958 [...] [Playback|Capture] Default /* default or global value - read/write */ + IEC958 [...] [Playback|Capture] Mask /* consumer and professional mask */ + IEC958 [...] [Playback|Capture] Con Mask /* consumer mask */ + IEC958 [...] [Playback|Capture] Pro Mask /* professional mask */ + IEC958 [...] [Playback|Capture] PCM Stream /* the settings assigned to a PCM stream */ + IEC958 Q-subcode [Playback|Capture] Default /* Q-subcode bits */ + IEC958 Preamble [Playback|Capture] Default /* burst preamble words (4*16bits) */ diff --git a/kmix/kledbutton.cpp b/kmix/kledbutton.cpp new file mode 100644 index 00000000..1cddb148 --- /dev/null +++ b/kmix/kledbutton.cpp @@ -0,0 +1,79 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qsizepolicy.h> + +#include "kledbutton.h" + + +KLedButton::KLedButton(const QColor &col, QWidget *parent, const char *name) + : KLed( col, parent, name ) +{ + // KLed and thus KLedButtung does not do proper positioning in QLayout's. + // Thus I will do a dirty trick here + installEventFilter(parent); +} + +KLedButton::KLedButton(const QColor& col, KLed::State st, KLed::Look look, + KLed::Shape shape, QWidget *parent, const char *name) + : KLed( col, st, look, shape, parent, name ) +{ +} + +KLedButton::~KLedButton() +{ +} + +void KLedButton::mousePressEvent( QMouseEvent *e ) +{ + if (e->button() == LeftButton) + { + toggle(); + emit stateChanged( state() ); + } +} + +bool KLedButton::eventFilter( QObject* /*obj*/ , QEvent* /*ev*/ ) { + // KLed and thus KLedButtung does not do proper positioning in QLayout's. + // Thus I listen to my parents resize events and do it here ... OUCH, that's ugly + /* No, this cannot work ! + if ( ev->type() == QEvent::Resize ) { + QResizeEvent* qre = (QResizeEvent*)ev; + this->move( qre->size().width() - width()/2, + qre->size().height() - height()/2 ); + } + */ + return false; + // KLed::eventFilter(obj, ev); + +} + +QSize KLedButton::sizeHint() const +{ + return size(); +} + +QSizePolicy KLedButton::sizePolicy () const +{ + return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +#include "kledbutton.moc" diff --git a/kmix/kledbutton.h b/kmix/kledbutton.h new file mode 100644 index 00000000..261f829c --- /dev/null +++ b/kmix/kledbutton.h @@ -0,0 +1,53 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KLEDBUTTON_H +#define KLEDBUTTON_H + +#include <qwidget.h> + +#include <kled.h> + +/** + *@author Stefan Schimanski + */ + +class KLedButton : public KLed { + Q_OBJECT + public: + KLedButton(const QColor &col=Qt::green, QWidget *parent=0, const char *name=0); + KLedButton(const QColor& col, KLed::State st, KLed::Look look, KLed::Shape shape, + QWidget *parent=0, const char *name=0); + ~KLedButton(); + + QSize sizeHint () const; + QSizePolicy sizePolicy () const; + signals: + void stateChanged( bool newState ); + + protected: + void mousePressEvent ( QMouseEvent *e ); + + private: + bool eventFilter( QObject*, QEvent* ); +}; + +#endif diff --git a/kmix/kmix-platforms.cpp b/kmix/kmix-platforms.cpp new file mode 100644 index 00000000..7947043b --- /dev/null +++ b/kmix/kmix-platforms.cpp @@ -0,0 +1,149 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2000 Christian Esken + * esken@kde.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This code is being #include'd from mixer.cpp */ + +#include <config.h> + +#include <qstring.h> + +#include "mixer_backend.h" + +#if defined(sun) || defined(__sun__) +#define SUN_MIXER +#endif + +#ifdef sgi +#include <sys/fcntl.h> +#define IRIX_MIXER +#endif + +#ifdef __linux__ + +#ifdef HAVE_LIBASOUND2 +#define ALSA_MIXER +#endif + +#define OSS_MIXER +#endif + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(_UNIXWARE) || defined(__DragonFly__) +#define OSS_MIXER +#endif + +#if defined(hpux) +# if defined(HAVE_ALIB_H) +# define HPUX_MIXER +# else +# warning ** YOU NEED to have libAlib installed to use the HP-UX-Mixer ** +# endif // HAVE_ALIB_H +#endif // hpux + +// PORTING: add #ifdef PLATFORM , commands , #endif, add your new mixer below +#if defined(NAS_MIXER) +#include "mixer_nas.cpp" +#endif + +#if defined(SUN_MIXER) +#include "mixer_sun.cpp" +#endif + +#if defined(IRIX_MIXER) +#include "mixer_irix.cpp" +#endif + +// Alsa API's +#if defined(ALSA_MIXER) +#include "mixer_alsa9.cpp" +#endif + +#if defined(OSS_MIXER) +#include "mixer_oss.cpp" + +#if !defined(__NetBSD__) && !defined(__OpenBSD__) +#include <sys/soundcard.h> +#else +#include <soundcard.h> +#endif +#if SOUND_VERSION >= 0x040000 +#define OSS4_MIXER +#endif + +#endif + +#if defined(OSS4_MIXER) +#include "mixer_oss4.cpp" +#endif + +#if defined(HPUX_MIXER) +#include "mixer_hpux.cpp" +#endif + +/* +#else +// We cannot handle this! I install a dummy mixer instead. +#define NO_MIXER +#include "mixer_none.cpp" +#endif +*/ + +typedef Mixer_Backend *getMixerFunc( int device ); +typedef QString getDriverNameFunc( ); + +struct MixerFactory { + getMixerFunc *getMixer; + getDriverNameFunc *getDriverName; +}; + +MixerFactory g_mixerFactories[] = { + +#if defined(NAS_MIXER) + { NAS_getMixer, 0 }, +#endif + +#if defined(SUN_MIXER) + { SUN_getMixer, SUN_getDriverName }, +#endif + +#if defined(IRIX_MIXER) + { IRIX_getMixer, IRIX_getDriverName }, +#endif + +#if defined(ALSA_MIXER) + { ALSA_getMixer, ALSA_getDriverName }, +#endif + +#if defined(OSS4_MIXER) + { OSS4_getMixer, OSS4_getDriverName }, +#endif + +#if defined(OSS_MIXER) + { OSS_getMixer, OSS_getDriverName }, +#endif + +#if defined(HPUX_MIXER) + { HPUX_getMixer, HPUX_getDriverName }, +#endif + + { 0, 0 } +}; + diff --git a/kmix/kmix.cpp b/kmix/kmix.cpp new file mode 100644 index 00000000..e4cd68be --- /dev/null +++ b/kmix/kmix.cpp @@ -0,0 +1,648 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org> + * Copyright (C) 2001 Preston Brown <pbrown@kde.org> + * Copyright (C) 2003 Sven Leiber <s.leiber@web.de> + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// include files for QT +#include <qmap.h> +#include <qhbox.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qwidgetstack.h> +#include <qlayout.h> +#include <qtooltip.h> + +// include files for KDE +#include <kcombobox.h> +#include <kiconloader.h> +#include <kmessagebox.h> +#include <kmenubar.h> +#include <klineeditdlg.h> +#include <klocale.h> +#include <kconfig.h> +#include <kaction.h> +#include <kapplication.h> +#include <kstdaction.h> +#include <kpanelapplet.h> +#include <kpopupmenu.h> +#include <khelpmenu.h> +#include <kdebug.h> +#include <kaccel.h> +#include <kglobalaccel.h> +#include <kkeydialog.h> +#include <kpopupmenu.h> + +// application specific includes +#include "mixertoolbox.h" +#include "kmix.h" +#include "kmixerwidget.h" +#include "kmixprefdlg.h" +#include "kmixdockwidget.h" +#include "kmixtoolbox.h" + + +/** + * Constructs a mixer window (KMix main window) + */ +KMixWindow::KMixWindow() + : KMainWindow(0, 0, 0, 0), m_showTicks( true ), + m_dockWidget( 0L ) +{ + m_visibilityUpdateAllowed = true; + m_multiDriverMode = false; // -<- I never-ever want the multi-drivermode to be activated by accident + m_surroundView = false; // -<- Also the experimental surround View (3D) + m_gridView = false; // -<- Also the experimental Grid View + // As long as we do not know better, we assume to start hidden. We need + // to initialize this variable here, as we don't trigger a hideEvent(). + m_isVisible = false; + m_mixerWidgets.setAutoDelete(true); + loadConfig(); // Need to load config before initMixer(), due to "MultiDriver" keyword + MixerToolBox::initMixer(Mixer::mixers(), m_multiDriverMode, m_hwInfoString); + initActions(); + initWidgets(); + initMixerWidgets(); + + initPrefDlg(); + updateDocking(); + + if ( m_startVisible ) + { + /* Started visible: We should do probably do: + * m_isVisible = true; + * But as a showEvent() is triggered by show() we don't need it. + */ + show(); + } + else + { + hide(); + } + connect( kapp, SIGNAL( aboutToQuit()), SLOT( saveSettings()) ); +} + + +KMixWindow::~KMixWindow() +{ + MixerToolBox::deinitMixer(); +} + + +void +KMixWindow::initActions() +{ + // file menu + KStdAction::quit( this, SLOT(quit()), actionCollection()); + + // settings menu + KStdAction::showMenubar( this, SLOT(toggleMenuBar()), actionCollection()); + KStdAction::preferences( this, SLOT(showSettings()), actionCollection()); + new KAction( i18n( "Configure &Global Shortcuts..." ), "configure_shortcuts", 0, this, + SLOT( configureGlobalShortcuts() ), actionCollection(), "settings_global" ); + KStdAction::keyBindings( guiFactory(), SLOT(configureShortcuts()), actionCollection()); + + (void) new KAction( i18n( "Hardware &Information" ), 0, this, SLOT( slotHWInfo() ), actionCollection(), "hwinfo" ); + (void) new KAction( i18n( "Hide Mixer Window" ), Key_Escape, this, SLOT(hide()), actionCollection(), "hide_kmixwindow" ); + + m_globalAccel = new KGlobalAccel( this ); + m_globalAccel->insert( "Increase volume", i18n( "Increase Volume of Master Channel"), QString::null, + KShortcut(), KShortcut(), this, SLOT( increaseVolume() ) ); + m_globalAccel->insert( "Decrease volume", i18n( "Decrease Volume of Master Channel"), QString::null, + KShortcut(), KShortcut(), this, SLOT( decreaseVolume() ) ); + m_globalAccel->insert( "Toggle mute", i18n( "Toggle Mute of Master Channel"), QString::null, + KShortcut(), KShortcut(), this, SLOT( toggleMuted() ) ); + m_globalAccel->readSettings(); + m_globalAccel->updateConnections(); + + createGUI( "kmixui.rc" ); +} + + +void +KMixWindow::initPrefDlg() +{ + m_prefDlg = new KMixPrefDlg( this ); + connect( m_prefDlg, SIGNAL(signalApplied(KMixPrefDlg *)), + this, SLOT(applyPrefs(KMixPrefDlg *)) ); +} + + +void +KMixWindow::initWidgets() +{ + // Main widget and layout + setCentralWidget( new QWidget( this, "qt_central_widget" ) ); + + // Widgets layout + widgetsLayout = new QVBoxLayout( centralWidget(), 0, 0, "widgetsLayout" ); + widgetsLayout->setResizeMode(QLayout::Minimum); // works fine + + + // Mixer widget line + mixerNameLayout = new QHBox( centralWidget(), "mixerNameLayout" ); + widgetsLayout->setStretchFactor( mixerNameLayout, 0 ); + QSizePolicy qsp( QSizePolicy::Ignored, QSizePolicy::Maximum); + mixerNameLayout->setSizePolicy(qsp); + mixerNameLayout->setSpacing(KDialog::spacingHint()); + QLabel *qlbl = new QLabel( i18n("Current mixer:"), mixerNameLayout ); + qlbl->setFixedHeight(qlbl->sizeHint().height()); + m_cMixer = new KComboBox( FALSE, mixerNameLayout, "mixerCombo" ); + m_cMixer->setFixedHeight(m_cMixer->sizeHint().height()); + connect( m_cMixer, SIGNAL( activated( int ) ), this, SLOT( showSelectedMixer( int ) ) ); + QToolTip::add( m_cMixer, i18n("Current mixer" ) ); + + // Add first layout to widgets + widgetsLayout->addWidget( mixerNameLayout ); + + m_wsMixers = new QWidgetStack( centralWidget(), "MixerWidgetStack" ); + widgetsLayout->setStretchFactor( m_wsMixers, 10 ); + widgetsLayout->addWidget( m_wsMixers ); + + if ( m_showMenubar ) + menuBar()->show(); + else + menuBar()->hide(); + + widgetsLayout->activate(); +} + + +void +KMixWindow::updateDocking() +{ + // delete old dock widget + if (m_dockWidget) + { + delete m_dockWidget; + m_dockWidget = 0L; + } + + if (m_showDockWidget) + { + + // create dock widget + // !! This should be a View in the future + m_dockWidget = new KMixDockWidget( Mixer::mixers().first(), this, "mainDockWidget", m_volumeWidget ); + +/* Belongs in KMixDockWidget + // create RMB menu + KPopupMenu *menu = m_dockWidget->contextMenu(); + + // !! check this + KAction *a = actionCollection()->action( "dock_mute" ); + if ( a ) a->plug( menu ); +*/ + + /* + * Mail from 31.1.2005: "make sure your features are at least string complete" + * Preparation for fixing Bug #55078 - scheduled for KDE3.4.1 . + * This text will be plugged into the dock-icon popup menu. + */ + QString selectChannel = i18n("Select Channel"); // This text will be used in KDE3.4.1 !!! + + m_dockWidget->show(); + } +} + +void +KMixWindow::saveSettings() +{ + saveConfig(); + saveVolumes(); +} + +void +KMixWindow::saveConfig() +{ + KConfig *config = kapp->config(); + config->setGroup(0); + + // make sure we don't start without any UI + // can happen e.g. when not docked and kmix closed via 'X' button + bool startVisible = m_isVisible; + if ( !m_showDockWidget ) + startVisible = true; + + config->writeEntry( "Size", size() ); + config->writeEntry( "Position", pos() ); + // Cannot use isVisible() here, as in the "aboutToQuit()" case this widget is already hidden. + // (Please note that the problem was only there when quitting via Systray - esken). + config->writeEntry( "Visible", startVisible ); + config->writeEntry( "Menubar", m_showMenubar ); + config->writeEntry( "AllowDocking", m_showDockWidget ); + config->writeEntry( "TrayVolumeControl", m_volumeWidget ); + config->writeEntry( "Tickmarks", m_showTicks ); + config->writeEntry( "Labels", m_showLabels ); + config->writeEntry( "startkdeRestore", m_onLogin ); + Mixer* mixerMasterCard = Mixer::masterCard(); + if ( mixerMasterCard != 0 ) { + config->writeEntry( "MasterMixer", mixerMasterCard->id() ); + } + MixDevice* mdMaster = Mixer::masterCardDevice(); + if ( mdMaster != 0 ) { + config->writeEntry( "MasterMixerDevice", mdMaster->getPK() ); + } + + if ( m_valueStyle == MixDeviceWidget::NABSOLUTE ) + config->writeEntry( "ValueStyle", "Absolute"); + else if ( m_valueStyle == MixDeviceWidget::NRELATIVE ) + config->writeEntry( "ValueStyle", "Relative"); + else + config->writeEntry( "ValueStyle", "None" ); + + if ( m_toplevelOrientation == Qt::Vertical ) + config->writeEntry( "Orientation","Vertical" ); + else + config->writeEntry( "Orientation","Horizontal" ); + + // save mixer widgets + for ( KMixerWidget *mw = m_mixerWidgets.first(); mw != 0; mw = m_mixerWidgets.next() ) + { + if ( mw->mixer()->isOpen() ) + { // protect from unplugged devices (better do *not* save them) + QString grp; + grp.sprintf( "%i", mw->id() ); + mw->saveConfig( config, grp ); + } + } + + config->setGroup(0); +} + +void +KMixWindow::loadConfig() +{ + KConfig *config = kapp->config(); + config->setGroup(0); + + m_showDockWidget = config->readBoolEntry("AllowDocking", true); + m_volumeWidget = config->readBoolEntry("TrayVolumeControl", true); + //hide on close has to stay true for usability. KMixPrefDlg option commented out. nolden + m_hideOnClose = config->readBoolEntry("HideOnClose", true); + m_showTicks = config->readBoolEntry("Tickmarks", true); + m_showLabels = config->readBoolEntry("Labels", true); + const QString& valueStyleString = config->readEntry("ValueStyle", "None"); + m_onLogin = config->readBoolEntry("startkdeRestore", true ); + m_startVisible = config->readBoolEntry("Visible", true); + m_multiDriverMode = config->readBoolEntry("MultiDriver", false); + m_surroundView = config->readBoolEntry("Experimental-ViewSurround", false ); + m_gridView = config->readBoolEntry("Experimental-ViewGrid", false ); + const QString& orientationString = config->readEntry("Orientation", "Horizontal"); + QString mixerMasterCard = config->readEntry( "MasterMixer", "" ); + Mixer::setMasterCard(mixerMasterCard); + QString masterDev = config->readEntry( "MasterMixerDevice", "" ); + Mixer::setMasterCardDevice(masterDev); + + if ( valueStyleString == "Absolute" ) + m_valueStyle = MixDeviceWidget::NABSOLUTE; + else if ( valueStyleString == "Relative" ) + m_valueStyle = MixDeviceWidget::NRELATIVE; + else + m_valueStyle = MixDeviceWidget::NNONE; + + if ( orientationString == "Vertical" ) + m_toplevelOrientation = Qt::Vertical; + else + m_toplevelOrientation = Qt::Horizontal; + + // show/hide menu bar + m_showMenubar = config->readBoolEntry("Menubar", true); + + KToggleAction *a = static_cast<KToggleAction*>(actionCollection()->action("options_show_menubar")); + if (a) a->setChecked( m_showMenubar ); + + // restore window size and position + if ( !kapp->isRestored() ) // done by the session manager otherwise + { + QSize defSize( minimumWidth(), height() ); + QSize size = config->readSizeEntry("Size", &defSize ); + if(!size.isEmpty()) resize(size); + + QPoint defPos = pos(); + QPoint pos = config->readPointEntry("Position", &defPos); + move(pos); + } +} + + +void +KMixWindow::initMixerWidgets() +{ + m_mixerWidgets.clear(); + + int id=0; + Mixer *mixer; + + // Attention!! If Mixer::mixers() is empty, we behave stupid. We don't show nothing and there + // is not even a context menu. + for ( mixer=Mixer::mixers().first(),id=0; mixer!=0; mixer=Mixer::mixers().next(),id++ ) + { + //kdDebug(67100) << "Mixer number: " << id << " Name: " << mixer->mixerName() << endl ; + ViewBase::ViewFlags vflags = ViewBase::HasMenuBar; + if ( m_showMenubar ) { + vflags |= ViewBase::MenuBarVisible; + } + if ( m_surroundView ) { + vflags |= ViewBase::Experimental_SurroundView; + } + if ( m_gridView ) { + vflags |= ViewBase::Experimental_GridView; + } + if ( m_toplevelOrientation == Qt::Vertical ) { + vflags |= ViewBase::Vertical; + } + else { + vflags |= ViewBase::Horizontal; + } + + + KMixerWidget *mw = new KMixerWidget( id, mixer, mixer->mixerName(), + MixDevice::ALL, this, "KMixerWidget", vflags ); + m_mixerWidgets.append( mw ); + + // Add to Combo and Stack + m_cMixer->insertItem( mixer->mixerName() ); + m_wsMixers->addWidget( mw, id ); + + QString grp; + grp.sprintf( "%i", mw->id() ); + mw->loadConfig( kapp->config(), grp ); + + mw->setTicks( m_showTicks ); + mw->setLabels( m_showLabels ); + mw->setValueStyle ( m_valueStyle ); + // !! I am still not sure whether this works 100% reliably - chris + mw->show(); + } + + if (id == 1) + { + // don't show the Current Mixer bit unless we have multiple mixers + mixerNameLayout->hide(); + } +} + + + +bool +KMixWindow::queryClose ( ) +{ + if ( m_showDockWidget && !kapp->sessionSaving() ) + { + hide(); + return false; + } + return true; +} + + +void +KMixWindow::quit() +{ + kapp->quit(); +} + + +void +KMixWindow::showSettings() +{ + if (!m_prefDlg->isVisible()) + { + m_prefDlg->m_dockingChk->setChecked( m_showDockWidget ); + m_prefDlg->m_volumeChk->setChecked(m_volumeWidget); + m_prefDlg->m_showTicks->setChecked( m_showTicks ); + m_prefDlg->m_showLabels->setChecked( m_showLabels ); + m_prefDlg->m_onLogin->setChecked( m_onLogin ); + m_prefDlg->_rbVertical ->setChecked( m_toplevelOrientation == Qt::Vertical ); + m_prefDlg->_rbHorizontal->setChecked( m_toplevelOrientation == Qt::Horizontal ); + m_prefDlg->_rbNone->setChecked( m_valueStyle == MixDeviceWidget::NNONE ); + m_prefDlg->_rbAbsolute->setChecked( m_valueStyle == MixDeviceWidget::NABSOLUTE ); + m_prefDlg->_rbRelative->setChecked( m_valueStyle == MixDeviceWidget::NRELATIVE ); + + m_prefDlg->show(); + } +} + + +void +KMixWindow::showHelp() +{ + actionCollection()->action( "help_contents" )->activate(); +} + + +void +KMixWindow::showAbout() +{ + actionCollection()->action( "help_about_app" )->activate(); +} + +/** + * Loads the volumes of all mixers from kmixctrlrc. + * In other words: + * Restores the default voumes as stored via saveVolumes() or the + * execution of "kmixctrl --save" + */ +/* Currently this is not in use +void +KMixWindow::loadVolumes() +{ + KConfig *cfg = new KConfig( "kmixctrlrc", true ); + for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) + { + mixer->volumeLoad( cfg ); + } + delete cfg; +} +*/ + +/** + * Stores the volumes of all mixers Can be restored via loadVolumes() or + * the kmixctrl application. + */ +void +KMixWindow::saveVolumes() +{ + KConfig *cfg = new KConfig( "kmixctrlrc", false ); + for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) { + //kdDebug(67100) << "KMixWindow::saveConfig()" << endl; + if ( mixer->isOpen() ) { // protect from unplugged devices (better do *not* save them) + mixer->volumeSave( cfg ); + } + } + delete cfg; +} + + +void +KMixWindow::applyPrefs( KMixPrefDlg *prefDlg ) +{ + m_showDockWidget = prefDlg->m_dockingChk->isChecked(); + m_volumeWidget = prefDlg->m_volumeChk->isChecked(); + m_showTicks = prefDlg->m_showTicks->isChecked(); + m_showLabels = prefDlg->m_showLabels->isChecked(); + m_onLogin = prefDlg->m_onLogin->isChecked(); + + if ( prefDlg->_rbNone->isChecked() ) { + m_valueStyle = MixDeviceWidget::NNONE; + } else if ( prefDlg->_rbAbsolute->isChecked() ) { + m_valueStyle = MixDeviceWidget::NABSOLUTE; + } else if ( prefDlg->_rbRelative->isChecked() ) { + m_valueStyle = MixDeviceWidget::NRELATIVE; + } + + bool toplevelOrientationHasChanged = + ( prefDlg->_rbVertical->isChecked() && m_toplevelOrientation == Qt::Horizontal ) + || ( prefDlg->_rbHorizontal->isChecked() && m_toplevelOrientation == Qt::Vertical ); + if ( toplevelOrientationHasChanged ) { + QString msg = i18n("The change of orientation will be adopted on the next start of KMix."); + KMessageBox::information(0,msg); + } + if ( prefDlg->_rbVertical->isChecked() ) { + //QString "For a change of language to take place, quit and restart KDiff3."; + //kdDebug(67100) << "KMix should change to Vertical layout\n"; + m_toplevelOrientation = Qt::Vertical; + } + else if ( prefDlg->_rbHorizontal->isChecked() ) { + //kdDebug(67100) << "KMix should change to Horizontal layout\n"; + m_toplevelOrientation = Qt::Horizontal; + } + + + this->setUpdatesEnabled(false); + updateDocking(); + + for (KMixerWidget *mw=m_mixerWidgets.first(); mw!=0; mw=m_mixerWidgets.next()) + { + mw->setTicks( m_showTicks ); + mw->setLabels( m_showLabels ); + mw->setValueStyle ( m_valueStyle ); + mw->mixer()->readSetFromHWforceUpdate(); // needed, as updateDocking() has reconstructed the DockWidget + } + + this->setUpdatesEnabled(true); + + // avoid invisible and unaccessible main window + if( !m_showDockWidget && !isVisible() ) + { + show(); + } + + this->repaint(); // make KMix look fast (saveConfig() often uses several seconds) + kapp->processEvents(); + saveConfig(); +} + + +void +KMixWindow::toggleMenuBar() +{ + m_showMenubar = !m_showMenubar; + if( m_showMenubar ) + { + menuBar()->show(); + } + else + { + menuBar()->hide(); + } +} + +void +KMixWindow::showEvent( QShowEvent * ) +{ + if ( m_visibilityUpdateAllowed ) + m_isVisible = isVisible(); + // !! could possibly start polling now (idea: use someting like ref() and unref() on Mixer instance +} + +void +KMixWindow::hideEvent( QHideEvent * ) +{ + if ( m_visibilityUpdateAllowed ) + { + m_isVisible = isVisible(); + } + // !! could possibly stop polling now (idea: use someting like ref() and unref() on Mixer instance + // Update: This is a stupid idea, because now the views are responsible for updating. So it will be done in the Views. + // But the dock icon is currently no View, so that must be done first. +} + + +void +KMixWindow::stopVisibilityUpdates() { + m_visibilityUpdateAllowed = false; +} + +void +KMixWindow::slotHWInfo() { + KMessageBox::information( 0, m_hwInfoString, i18n("Mixer Hardware Information") ); +} + +void +KMixWindow::showSelectedMixer( int mixer ) +{ + m_wsMixers->raiseWidget( mixer ); +} + +void +KMixWindow::configureGlobalShortcuts() +{ + KKeyDialog::configure( m_globalAccel, 0, false ) ; + m_globalAccel->writeSettings(); + m_globalAccel->updateConnections(); +} + +void +KMixWindow::toggleMuted() +{ + Mixer* mixerMaster = Mixer::masterCard(); + if ( mixerMaster != 0 ) { + MixDevice* md = mixerMaster->masterDevice(); + if ( md != 0 && md->hasMute() ) { + mixerMaster->toggleMute(md->num()); + } + } +} + +void +KMixWindow::increaseVolume() +{ + Mixer* mixerMaster = Mixer::masterCard(); + if ( mixerMaster != 0 ) { + MixDevice* md = mixerMaster->masterDevice(); + if ( md != 0 ) { + mixerMaster->increaseVolume(md->num()); + } + } +} + +void +KMixWindow::decreaseVolume() +{ + Mixer* mixerMaster = Mixer::masterCard(); + if ( mixerMaster != 0 ) { + MixDevice* md = mixerMaster->masterDevice(); + if ( md != 0 ) { + mixerMaster->decreaseVolume(md->num()); + } + } +} + +#include "kmix.moc" + diff --git a/kmix/kmix.desktop b/kmix/kmix.desktop new file mode 100644 index 00000000..4cb25f7a --- /dev/null +++ b/kmix/kmix.desktop @@ -0,0 +1,92 @@ +[Desktop Entry] +Exec=kmix -caption "%c" %i %m +DocPath=kmix/index.html +OnlyShowIn=KDE; +Path= +Type=Application +MimeType= +Terminal=false +Icon=kmix +GenericName=Sound Mixer +GenericName[af]=Klank Menger +GenericName[ar]=مازج الصوت +GenericName[bg]=Аудио миксер +GenericName[br]=Mesker ar Son +GenericName[bs]=Zvučni mikser +GenericName[ca]=Mesclador de so +GenericName[cs]=Zvukový směšovač +GenericName[cy]=Cymysgydd Sŵn +GenericName[da]=Lydmikser +GenericName[de]=Lautstärkeregler +GenericName[el]=Μείκτης ήχου +GenericName[eo]=Sonormiksilo +GenericName[es]=Un mezclador audio +GenericName[et]=Helimikser +GenericName[eu]=Soinu nahasgailua +GenericName[fa]=مخلوطکن صدا +GenericName[fi]=Äänimikseri +GenericName[fr]=Console de mixage +GenericName[ga]=Meascthóir Fuaime +GenericName[gl]=Mesturador de Son +GenericName[he]=מערבל צליל +GenericName[hi]=ध्वनि मिक्सर +GenericName[hr]=Mikser zvuka +GenericName[hu]=Hangkeverő +GenericName[is]=Hljóðblöndun +GenericName[it]=Mixer audio +GenericName[ja]=サウンドミキサー +GenericName[kk]=Дыбыс микшері +GenericName[km]=កម្មវិធីលាយសំឡេង +GenericName[ko]=소리 믹서 +GenericName[lt]=Garsų maišiklis +GenericName[lv]=Skaņas Mikšeris +GenericName[mk]=Миксета за звук +GenericName[ms]=Pengadun Bunyi +GenericName[nb]=Lydmikser +GenericName[nds]=Klangmischer +GenericName[ne]=ध्वनि मिक्सर +GenericName[nl]=Geluidsmixer +GenericName[nn]=Lydmiksar +GenericName[pa]=ਧੁਨੀ ਮਿਕਸਰ +GenericName[pl]=Ustawienia głośności +GenericName[pt]=Mesa de Mistura de Áudio +GenericName[pt_BR]=Mixagem de som +GenericName[ro]=Mixer de sunet +GenericName[ru]=Звуковой микшер +GenericName[se]=Jietnamixer +GenericName[sk]=Zvukový mixér +GenericName[sl]=Mešalnik zvoka +GenericName[sr]=Звучна миксета +GenericName[sr@Latn]=Zvučna mikseta +GenericName[sv]=Ljudmixer +GenericName[ta]=ஒலி ஒன்றுசேர்ப்பான் +GenericName[tg]=Омехтакунаки Овоз +GenericName[th]=โปรแกรมผสมเสียง +GenericName[tr]=Ses Denetimleri +GenericName[uk]=Аудіомікшер +GenericName[uz]=Audio mikser +GenericName[uz@cyrillic]=Аудио миксер +GenericName[ven]=Tshitanganisi tsha mubvumo +GenericName[wa]=Maxheu d' sons +GenericName[xh]=Umxubi WokuvakalayoU +GenericName[zh_CN]=混音器 +GenericName[zh_HK]=聲音混音器 +GenericName[zh_TW]=音效混音器 +GenericName[zu]=Umxubi Womsindo +Name=KMix +Name[af]=Kmix +Name[bn]=কে-মিক্স +Name[ca]=Kmix +Name[eo]=Miksilo +Name[hi]=के-मिक्स +Name[lv]=KMiks +Name[ne]=केडीई मिक्स +Name[pa]=ਕੇ-ਮਿਕਸ +Name[sv]=Kmix +Name[ta]=கேமிக்ஸ் +Name[tg]=KОмезиш +Name[ven]=U tanganisa ha K +Name[zh_TW]=KMix 混音器 +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Unique +Categories=Qt;KDE;AudioVideo;Audio;Mixer; diff --git a/kmix/kmix.h b/kmix/kmix.h new file mode 100644 index 00000000..4805d4d1 --- /dev/null +++ b/kmix/kmix.h @@ -0,0 +1,135 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KMIX_H +#define KMIX_H + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// include files for Qt +#include <qstring.h> +#include <qmap.h> + +class QHBox; +class QWidgetStack; + +// include files for KDE +#include <kmainwindow.h> + +class KAccel; +class KGlobalAccel; +class KComboBox; +class KMixerWidget; +class KMixerPrefWidget; +class KMixPrefDlg; +class KMixDockWidget; +class KMixWindow; +class Mixer; + +#include "mixer.h" +#include "mixdevicewidget.h" + + +class +KMixWindow : public KMainWindow +{ + Q_OBJECT + + public: + KMixWindow(); + ~KMixWindow(); + + protected slots: + void saveSettings(); + + protected: + void saveConfig(); + void loadConfig(); + + void initPrefDlg(); + void initActions(); + void initWidgets(); + void initMixerWidgets(); + + void updateDocking(); + + bool queryClose(); + void showEvent( QShowEvent * ); + void hideEvent( QHideEvent * ); + + public slots: + void quit(); + void showSettings(); + void showHelp(); + void showAbout(); + void toggleMenuBar(); + //void loadVolumes(); + void saveVolumes(); + virtual void applyPrefs( KMixPrefDlg *prefDlg ); + void stopVisibilityUpdates(); + + private: + KAccel *m_keyAccel; + KGlobalAccel *m_globalAccel; + QPopupMenu *m_fileMenu; + QPopupMenu *m_viewMenu; + QPopupMenu *m_helpMenu; + + bool m_showDockWidget; + bool m_volumeWidget; + bool m_hideOnClose; + bool m_showTicks; + bool m_showLabels; + MixDeviceWidget::ValueStyle m_valueStyle; // No numbers by default + bool m_onLogin; + bool m_startVisible; + bool m_showMenubar; + bool m_isVisible; + bool m_visibilityUpdateAllowed; + bool m_multiDriverMode; // Not officially supported. + bool m_surroundView; // Experimental. Off by defualt + bool m_gridView; // Experimental. Off by default + Qt::Orientation m_toplevelOrientation; + + QPtrList<KMixerWidget> m_mixerWidgets; + + QHBox* mixerNameLayout; + KComboBox *m_cMixer; + QWidgetStack *m_wsMixers; + KMixPrefDlg *m_prefDlg; + KMixDockWidget *m_dockWidget; + QString m_hwInfoString; + QVBoxLayout *widgetsLayout; + + private slots: + //void removeMixerWidget( KMixerWidget *mw ); + void slotHWInfo(); + void showSelectedMixer( int mixer ); + void configureGlobalShortcuts(); + void toggleMuted(); + void increaseVolume(); + void decreaseVolume(); +}; + +#endif // KMIX_H diff --git a/kmix/kmixapplet.cpp b/kmix/kmixapplet.cpp new file mode 100644 index 00000000..7ab8c00b --- /dev/null +++ b/kmix/kmixapplet.cpp @@ -0,0 +1,566 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org> + * Copyright (C) 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// System +#include <stdlib.h> + +// QT +#include <qgroupbox.h> +#include <qcheckbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpixmap.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qwmatrix.h> + + +// KDE +#include <kaboutapplication.h> +#include <kaboutdata.h> +#include <kaction.h> +#include <kapplication.h> +#include <kbugreport.h> +#include <kcolorbutton.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kglobalaccel.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> + +// // KMix +#include "colorwidget.h" +#include "mixertoolbox.h" +#include "kmixapplet.h" +#include "kmixtoolbox.h" +#include "mdwslider.h" +#include "mixdevicewidget.h" +#include "mixer.h" +#include "version.h" +#include "viewapplet.h" + + +extern "C" +{ + KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("kmix"); + return new KMixApplet(configFile, KPanelApplet::Normal, + parent, "kmixapplet"); + } +} + +int KMixApplet::s_instCount = 0; +//<Mixer> KMixApplet::Mixer::mixers(); + +static const QColor highColor = KGlobalSettings::baseColor(); +static const QColor lowColor = KGlobalSettings::highlightColor(); +static const QColor backColor = "#000000"; +static const QColor mutedHighColor = "#FFFFFF"; +static const QColor mutedLowColor = "#808080"; +static const QColor mutedBackColor = "#000000"; + +AppletConfigDialog::AppletConfigDialog( QWidget * parent, const char * name ) + : KDialogBase( KDialogBase::Plain, QString::null, + KDialogBase::Ok | KDialogBase::Apply | KDialogBase::Cancel, + KDialogBase::Ok, parent, name, false, true) +{ + setPlainCaption(i18n("Configure - Mixer Applet")); + QFrame* page = plainPage(); + QVBoxLayout *topLayout = new QVBoxLayout(page); + colorWidget = new ColorWidget(page); + topLayout->addWidget(colorWidget); + setUseCustomColors(false); +} + +void AppletConfigDialog::slotOk() +{ + slotApply(); + KDialogBase::slotOk(); +} + +void AppletConfigDialog::slotApply() +{ + emit applied(); +} + +void AppletConfigDialog::setActiveColors(const QColor& high, const QColor& low, const QColor& back) +{ + colorWidget->activeHigh->setColor(high); + colorWidget->activeLow->setColor(low); + colorWidget->activeBack->setColor(back); +} + +void AppletConfigDialog::activeColors(QColor& high, QColor& low, QColor& back) const +{ + high = colorWidget->activeHigh->color(); + low = colorWidget->activeLow->color(); + back = colorWidget->activeBack->color(); +} + +void AppletConfigDialog::setMutedColors(const QColor& high, const QColor& low, const QColor& back) +{ + colorWidget->mutedHigh->setColor(high); + colorWidget->mutedLow->setColor(low); + colorWidget->mutedBack->setColor(back); +} + +void AppletConfigDialog::mutedColors(QColor& high, QColor& low, QColor& back) const +{ + high = colorWidget->mutedHigh->color(); + low = colorWidget->mutedLow->color(); + back = colorWidget->mutedBack->color(); +} + +void AppletConfigDialog::setUseCustomColors(bool custom) +{ + colorWidget->customColors->setChecked(custom); + colorWidget->activeColors->setEnabled(custom); + colorWidget->mutedColors->setEnabled(custom); +} + +bool AppletConfigDialog::useCustomColors() const +{ + return colorWidget->customColors->isChecked(); +} + + +KMixApplet::KMixApplet( const QString& configFile, Type t, + QWidget *parent, const char *name ) + + : KPanelApplet( configFile, t, KPanelApplet::Preferences | KPanelApplet::ReportBug | KPanelApplet::About, parent, name ), + m_mixerWidget(0), m_errorLabel(0), m_pref(0), + m_aboutData( "kmix", I18N_NOOP("KMix Panel Applet"), + APP_VERSION, "Mini Sound Mixer Applet", KAboutData::License_GPL, + I18N_NOOP( "(c) 1996-2000 Christian Esken\n(c) 2000-2003 Christian Esken, Stefan Schimanski") ) +{ + setBackgroundOrigin(AncestorOrigin); + kdDebug(67100) << "KMixApplet::KMixApplet instancing Applet. Old s_instCount="<< s_instCount << " configfile=" << configFile << endl; + //kdDebug(67100) << "KMixApplet::KMixApplet()" << endl; + _layout = new QHBoxLayout(this); // it will always only be one item in it, so we don't care whether it is HBox or VBox + + // init static vars + if ( s_instCount == 0) { + Mixer::mixers().setAutoDelete( TRUE ); + QString dummyStringHwinfo; + MixerToolBox::initMixer(Mixer::mixers(), false, dummyStringHwinfo); + } + s_instCount++; + kdDebug(67100) << "KMixApplet::KMixApplet instancing Applet, s_instCount="<< s_instCount << endl; + + KGlobal::dirs()->addResourceType( "appicon", KStandardDirs::kde_default("data") + "kmix/pics" ); + + loadConfig(); + + + /********** find out to use which mixer ****************************************/ + _mixer = 0; + for (_mixer= Mixer::mixers().first(); _mixer!=0; _mixer=Mixer::mixers().next()) + { + if ( _mixer->id() == _mixerId ) break; + } + if ( _mixer == 0 ) { + /* Until KMix V3.4-0 the mixerNumber (int) was stored. This was too complicated to handle, so we use an + * unique ID (_mixer->mixerId(). But in case when the user changes soundcards (or when upgrading from + * KMix 3.4-0 to a 3.4-1 or newer), we scan also for the soundcard name */ + for (_mixer= Mixer::mixers().first(); _mixer!=0; _mixer=Mixer::mixers().next()) + { + if ( _mixer->mixerName() == _mixerName ) break; + } + } + + // don't prompt for a mixer if there is just one available + if ( !_mixer && Mixer::mixers().count() == 1 ) { + _mixer = Mixer::mixers().first(); + } + + + + if ( _mixer == 0 ) + { + // No mixer set by user (kmixappletrc_*) and more than one to choose + // We do NOT know which mixer to use => ask the User + m_errorLabel = new QPushButton( i18n("Select Mixer"), this ); + m_errorLabel->setGeometry(0, 0, m_errorLabel->sizeHint().width(), m_errorLabel->sizeHint().height() ); + resize( m_errorLabel->sizeHint() ); + connect( m_errorLabel, SIGNAL(clicked()), this, SLOT(selectMixer()) ); + } + else { + // We know which mixer to use: Call positionChange(), which does all the creating + positionChange(position()); + } + m_aboutData.addCredit( I18N_NOOP( "For detailed credits, please refer to the About information of the KMix program" ) ); +} + +KMixApplet::~KMixApplet() +{ + saveConfig(); + + /* !!! no cleanup for now: I get strange crashes on exiting + // destroy static vars + s_instCount--; + if ( s_instCount == 0) + { + MixerToolBox::deinitMixer(); + } + */ +} + +void KMixApplet::saveConfig() +{ + kdDebug(67100) << "KMixApplet::saveConfig()" << endl; + if ( m_mixerWidget != 0) { + //kdDebug(67100) << "KMixApplet::saveConfig() save" << endl; + KConfig *cfg = this->config(); + //kdDebug(67100) << "KMixApplet::saveConfig() save cfg=" << cfg << endl; + cfg->setGroup( 0 ); + cfg->writeEntry( "Mixer", _mixer->id() ); + cfg->writeEntry( "MixerName", _mixer->mixerName() ); + + cfg->writeEntry( "ColorCustom", _customColors ); + + cfg->writeEntry( "ColorHigh", _colors.high.name() ); + cfg->writeEntry( "ColorLow", _colors.low.name() ); + cfg->writeEntry( "ColorBack", _colors.back.name() ); + + cfg->writeEntry( "ColorMutedHigh", _colors.mutedHigh.name() ); + cfg->writeEntry( "ColorMutedLow", _colors.mutedLow.name() ); + cfg->writeEntry( "ColorMutedBack", _colors.mutedBack.name() ); + + //cfg->writeEntry( "ReversedDirection", reversedDir ); + + saveConfig( cfg, "Widget" ); + cfg->sync(); + } +} + + +void KMixApplet::loadConfig() +{ + kdDebug(67100) << "KMixApplet::loadConfig()" << endl; + KConfig *cfg = this->config(); + cfg->setGroup(0); + + _mixerId = cfg->readEntry( "Mixer", "undef" ); + _mixerName = cfg->readEntry( "MixerName", QString::null ); + + _customColors = cfg->readBoolEntry( "ColorCustom", false ); + + _colors.high = cfg->readColorEntry("ColorHigh", &highColor); + _colors.low = cfg->readColorEntry("ColorLow", &lowColor); + _colors.back = cfg->readColorEntry("ColorBack", &backColor); + + _colors.mutedHigh = cfg->readColorEntry("ColorMutedHigh", &mutedHighColor); + _colors.mutedLow = cfg->readColorEntry("ColorMutedLow", &mutedLowColor); + _colors.mutedBack = cfg->readColorEntry("ColorMutedBack", &mutedBackColor); + + loadConfig( cfg, "Widget"); +} + + +void KMixApplet::loadConfig( KConfig *config, const QString &grp ) +{ + if ( m_mixerWidget ) { + //config->setGroup( grp ); + KMixToolBox::loadConfig(m_mixerWidget->_mdws, config, grp, "PanelApplet" ); + } +} + + +void KMixApplet::saveConfig( KConfig *config, const QString &grp ) +{ + if ( m_mixerWidget ) { + config->setGroup( grp ); + // Write mixer name. It cannot be changed in the Mixer instance, + // it is only saved for diagnostical purposes (analyzing the config file). + config->writeEntry("Mixer_Name_Key", _mixer->mixerName()); + KMixToolBox::saveConfig(m_mixerWidget->_mdws, config, grp, "PanelApplet" ); + } +} + +/** + * Opens a dialog box with all available mixers and let the user choose one. + * If the user selects a mixer, "_mixer" will be set and positionChange() is called. + */ +void KMixApplet::selectMixer() +{ + QStringList lst; + + int n=1; + for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) + { + QString s; + s.sprintf("%i. %s", n, mixer->mixerName().ascii()); + lst << s; + n++; + } + + bool ok = FALSE; + QString res = KInputDialog::getItem( i18n("Mixers"), + i18n("Available mixers:"), + lst, 1, FALSE, &ok, this ); + if ( ok ) + { + Mixer *mixer = Mixer::mixers().at( lst.findIndex( res ) ); + if (!mixer) + KMessageBox::sorry( this, i18n("Invalid mixer entered.") ); + else + { + delete m_errorLabel; + m_errorLabel = 0; + + _mixer = mixer; + // Create the ViewApplet by calling positionChange() ... :) + // To take over reversedDir and (more important) to create the mixer widget + // if necessary! + positionChange(position()); + } + } +} + + +void KMixApplet::about() +{ + KAboutApplication aboutDlg(&m_aboutData); + aboutDlg.exec(); +} + +void KMixApplet::help() +{ +} + + +void KMixApplet::positionChange(Position pos) { + orientationChange( orientation() ); + QResizeEvent e( size(), size() ); // from KPanelApplet::positionChange + resizeEvent( &e ); // from KPanelApplet::positionChange + + if ( m_errorLabel == 0) { + // do this only after we deleted the error label + if (m_mixerWidget) { + saveConfig(); // save the applet before recreating it + _layout->remove(m_mixerWidget); + delete m_mixerWidget; + } + m_mixerWidget = new ViewApplet( this, _mixer->name(), _mixer, 0, pos ); + connect ( m_mixerWidget, SIGNAL(appletContentChanged()), this, SLOT(updateGeometrySlot()) ); + m_mixerWidget->createDeviceWidgets(); + _layout->add(m_mixerWidget); + _layout->activate(); + + loadConfig(); + setColors(); + + const QSize panelAppletConstrainedSize = sizeHint(); + m_mixerWidget->setGeometry( 0, 0, panelAppletConstrainedSize.width(), panelAppletConstrainedSize.height() ); + resize( panelAppletConstrainedSize.width(), panelAppletConstrainedSize.height() ); + //setFixedSize(panelAppletConstrainedSize.width(), panelAppletConstrainedSize.height() ); + //kdDebug(67100) << "KMixApplet::positionChange(). New MDW is at " << panelAppletConstrainedSize << endl; + m_mixerWidget->show(); + //connect( _mixer, SIGNAL(newVolumeLevels()), m_mixerWidget, SLOT(refreshVolumeLevels()) ); + } +} + + +/************* GEOMETRY STUFF START ********************************/ +void KMixApplet::resizeEvent(QResizeEvent *e) +{ + //kdDebug(67100) << "KMixApplet::resizeEvent(). New MDW is at " << e->size() << endl; + + if ( position() == KPanelApplet::pLeft || position() == KPanelApplet::pRight ) { + if ( m_mixerWidget ) m_mixerWidget->resize(e->size().width(),m_mixerWidget->height()); + if ( m_errorLabel ) m_errorLabel ->resize(e->size().width(),m_errorLabel ->height()); + } + else { + if ( m_mixerWidget ) m_mixerWidget->resize(m_mixerWidget->width(), e->size().height()); + if ( m_errorLabel ) m_errorLabel ->resize(m_errorLabel ->width() ,e->size().height()); + } + + + // resizing changes our own sizeHint(), because we must take the new PanelSize in account. + // So updateGeometry() is amust for us. + //kdDebug(67100) << "KMixApplet::resizeEvent(). UPDATE GEOMETRY" << endl; + updateGeometry(); + //kdDebug(67100) << "KMixApplet::resizeEvent(). EMIT UPDATE LAYOUT" << endl; + emit updateLayout(); +} + +void KMixApplet::updateGeometrySlot() { + updateGeometry(); +} + + +QSize KMixApplet::sizeHint() const { + //kdDebug(67100) << "KMixApplet::sizeHint()\n"; + QSize qsz; + if ( m_errorLabel !=0 ) { + qsz = m_errorLabel->sizeHint(); + } + else if ( m_mixerWidget != 0) { + qsz = m_mixerWidget->sizeHint(); + } + else { + // During construction of m_mixerWidget or if something goes wrong: + // Return something that should resemble our former sizeHint(). + qsz = size(); + } + //kdDebug(67100) << "KMixApplet::sizeHint() leftright =" << qsz << "\n"; + return qsz; +} + +/** + We need widthForHeight() and heigthForWidth() only because KPanelApplet::updateLayout does relayouts + using this method. Actually we ignore the passed paramater and just return our preferred size. +*/ +int KMixApplet::widthForHeight(int) const { + //kdDebug(67100) << "KMixApplet::widthForHeight() = " << sizeHint().width() << endl; + return sizeHint().width(); +} +int KMixApplet::heightForWidth(int) const { + //kdDebug(67100) << "KMixApplet::heightForWidth() = " << sizeHint().height() << endl; + return sizeHint().height(); +} + + + + +QSizePolicy KMixApplet::sizePolicy() const { + // return QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + if ( orientation() == Qt::Vertical ) { + //kdDebug(67100) << "KMixApplet::sizePolicy=(Ignored,Fixed)\n"; + return QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + } + else { + //kdDebug(67100) << "KMixApplet::sizePolicy=(Fixed,Ignored)\n"; + return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + } +} + +/************* GEOMETRY STUFF END ********************************/ + + +void KMixApplet::reportBug() +{ + KBugReport bugReportDlg(this, true, &m_aboutData); + bugReportDlg.exec(); +} + + +/******************* COLOR STUFF START ***********************************/ + +void KMixApplet::preferences() +{ + if ( !m_pref ) + { + m_pref = new AppletConfigDialog( this ); + connect(m_pref, SIGNAL(finished()), SLOT(preferencesDone())); + connect( m_pref, SIGNAL(applied()), SLOT(applyPreferences()) ); + + m_pref->setActiveColors(_colors.high , _colors.low , _colors.back); + m_pref->setMutedColors (_colors.mutedHigh, _colors.mutedLow, _colors.mutedBack); + + m_pref->setUseCustomColors( _customColors ); + + } + + m_pref->show(); + m_pref->raise(); +} + + +void KMixApplet::preferencesDone() +{ + m_pref->delayedDestruct(); + m_pref = 0; +} + +void KMixApplet::applyPreferences() +{ + if (!m_pref) + return; + + // copy the colors from the prefs dialog + m_pref->activeColors(_colors.high , _colors.low , _colors.back); + m_pref->mutedColors (_colors.mutedHigh, _colors.mutedLow, _colors.mutedBack); + _customColors = m_pref->useCustomColors(); + if (!m_mixerWidget) + return; + + /* + QSize si = m_mixerWidget->size(); + m_mixerWidget->resize( si ); + */ + setColors(); + saveConfig(); +} + +void KMixApplet::paletteChange ( const QPalette &) { + if ( ! _customColors ) { + // We take over Colors from paletteChange(), if the user has not set custom colors. + // ignore the given QPalette and use the values from KGlobalSettings instead + _colors.high = KGlobalSettings::highlightColor(); + _colors.low = KGlobalSettings::baseColor(); + _colors.back = backColor; + setColors( _colors ); + } +} + +void KMixApplet::setColors() +{ + if ( !_customColors ) { + KMixApplet::Colors cols; + cols.high = highColor; + cols.low = lowColor; + cols.back = backColor; + cols.mutedHigh = mutedHighColor; + cols.mutedLow = mutedLowColor; + cols.mutedBack = mutedBackColor; + + setColors( cols ); + } else + setColors( _colors ); +} + +void KMixApplet::setColors( const Colors &color ) +{ + if ( m_mixerWidget == 0 ) { + // can happen for example after a paletteChange() + return; + } + QPtrList<QWidget> &mdws = m_mixerWidget->_mdws; + for ( QWidget* qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) { + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- temporary check. Later we *know* that it is correct + static_cast<MixDeviceWidget*>(qmdw)->setColors( color.high, color.low, color.back ); + static_cast<MixDeviceWidget*>(qmdw)->setMutedColors( color.mutedHigh, color.mutedLow, color.mutedBack ); + } + } +} + +/******************* COLOR STUFF END ***********************************/ + +#include "kmixapplet.moc" + diff --git a/kmix/kmixapplet.desktop b/kmix/kmixapplet.desktop new file mode 100644 index 00000000..1295ecdd --- /dev/null +++ b/kmix/kmixapplet.desktop @@ -0,0 +1,110 @@ +[Desktop Entry] +Type=Plugin +Name=Sound Mixer +Name[bg]=Аудио миксер +Name[br]=Mesker ar Son +Name[bs]=Zvučni mikser +Name[ca]=Mesclador de so +Name[cs]=Zvukový směšovač +Name[cy]=Cymysgydd Sŵn +Name[da]=Lydmikser +Name[de]=Lautstärkeregler +Name[el]=Μείκτης ήχου +Name[eo]=Sonmiksilo +Name[es]=Mezclador de audio +Name[et]=Helimikser +Name[eu]=Soinu nahasgailua +Name[fa]=مخلوطکن صدا +Name[fi]=Äänimikseri +Name[fr]=Console de mixage +Name[ga]=Meascthóir Fuaime +Name[gl]=Mesturador de Son +Name[he]=מערבל צליל +Name[hi]=ध्वनि मिक्सर +Name[hr]=Mixer zvuka +Name[hu]=Hangkeverő +Name[is]=Hljóðblöndun +Name[it]=Mixer sonoro +Name[ja]=サウンドミキサー +Name[kk]=Дыбыс микшері +Name[km]=កម្មវិធីលាយសំឡេង +Name[ko]=소리 믹서 +Name[mk]=Миксета за звук +Name[ms]=Pengadun Bunyi +Name[nb]=Lydmikser +Name[nds]=Klangmischer +Name[ne]=ध्वनि मिक्सर +Name[nl]=Geluidsmixer +Name[nn]=Lydmiksar +Name[pa]=ਸਾਊਂਡ ਮਿਕਸਰ +Name[pl]=Mikser dźwięku +Name[pt]=Mistura de Áudio +Name[pt_BR]=Mixagem de som +Name[ro]=Mixer de sunet +Name[ru]=Микшер +Name[sk]=Zvukový mixér +Name[sl]=Mešalnik zvoka +Name[sr]=Звучна миксета +Name[sr@Latn]=Zvučna mikseta +Name[sv]=Ljudmixer +Name[ta]=ஒலிக் ஒன்றுசேர்ப்பான் +Name[tg]=Омехтакунаки Овоз +Name[th]=ปรับแต่งผสมเสียง +Name[tr]=Ses Karıştırıcı +Name[uk]=Аудіомікшер +Name[uz]=Audio mikser +Name[uz@cyrillic]=Аудио миксер +Name[wa]=Maxheu d' sons +Name[zh_CN]=混音器 +Name[zh_HK]=聲音混音器 +Name[zh_TW]=音效混音器 + +Comment=Volume and sound channel mixer control +Comment[bg]=Управление на силата на звука и каналите +Comment[br]=Renadur meskañ kanol ha nerzh +Comment[bs]=Kontrola miksera kanala glasnoće i zvuka +Comment[ca]=Control i mesclador de volum i canals de so +Comment[cs]=Ovládání hlasitosti a zvukových kanálů +Comment[da]=Mikserkontrol for lydstyrke og lydkanal +Comment[de]=Kontrolle der Lautstärke +Comment[el]=Στοιχείο μείκτη ήχων και έντασης καναλιών +Comment[eo]=Stirado de volumo kaj sonkanalmiksilo +Comment[es]=Control del volumen y los canales de sonido del mezclador +Comment[et]=Helitugevuse ja helikanalite seadistamine +Comment[eu]=Bolumena eta soinu-kanalen nahasketarako kontrola +Comment[fa]=کنترل مخلوطکن مجرای صدا و حجم صدا +Comment[fi]=Äänenvoimakkuuden ja äänikanavien mikserin hallinta +Comment[fr]=Contrôle du volume et des canaux de la console de mixage +Comment[gl]=Control do volume e da canle de son do mesturador +Comment[he]=שינוי הגדרות עוצמת קול של כרטיס הקול +Comment[hu]=Hangerőbeállító és keverő +Comment[is]=Stjórnun á styrk og hljóðrása blöndun +Comment[it]=Controllo del volume e del mixer sonoro +Comment[ja]=ボリュームおよびサウンドチャンネルミキサー制御 +Comment[kk]=Дыбыс үнділігі мен арналарды басқару +Comment[km]=ឧបករណ៍លាយឆានែលកម្រិតសំឡេង និងសំឡេង +Comment[ko]=음량과 사운드 채널 믹서 조정 +Comment[lt]=Garso ir garso kanalų maišyklės valdymas +Comment[nb]=Miksekontroll for lydnivået og lydkanalene +Comment[nds]=Kuntrull för den Luutstärk- un Klangkanaalmischer +Comment[ne]=भोल्युम र ध्वनि च्यानल मिक्सर नियन्त्रण +Comment[nl]=Bedieningspaneel voor het regelen van het volume en de geluidskanalen +Comment[nn]=Lydstyrke- og kanalmiksarkontroll +Comment[pl]=Sterowanie mikserem dźwięku i kanałów +Comment[pt]=Controlo de volume e canais de som +Comment[pt_BR]=Controle de volume e mixer de canais de som +Comment[ru]=Управление громкостью и звуковыми каналами +Comment[sk]=Riadenie hlasitosti a zvukového kanálu mixéru +Comment[sl]=Nadzor glasnosti in mešalnik zvočnih kanalov +Comment[sr]=Контрола јачине и миксета звучних канала +Comment[sr@Latn]=Kontrola jačine i mikseta zvučnih kanala +Comment[sv]=Volymkontroll och ljudkanalmixer +Comment[th]=โปรแกรมควบคุมช่องเสียงและระดับเสียง +Comment[tr]=Ses düzeyi ve ses kanalı karıştırıcısını kontrol edin +Comment[uk]=Керування гучністю і міксером звукових каналів +Comment[zh_CN]=音量和声道混音器控制 +Comment[zh_HK]=音量與聲道混音控制器 +Comment[zh_TW]=音量與聲道混音器控制 + +Icon=kmix +X-KDE-Library=kmix_panelapplet diff --git a/kmix/kmixapplet.h b/kmix/kmixapplet.h new file mode 100644 index 00000000..11c6225a --- /dev/null +++ b/kmix/kmixapplet.h @@ -0,0 +1,131 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KMIXAPPLET_H +#define KMIXAPPLET_H + +// Qt +#include <qlayout.h> +#include <qptrlist.h> +#include <qwidget.h> + +// KDE +#include <kaboutdata.h> +#include <kdialogbase.h> +#include <kpanelapplet.h> + +//KMix +#include "viewapplet.h" + +class Mixer; +class ColorWidget; +class KMixApplet; + + +class AppletConfigDialog : public KDialogBase +{ + Q_OBJECT + public: + AppletConfigDialog( QWidget * parent=0, const char * name=0 ); + virtual ~AppletConfigDialog() {}; + + void setActiveColors(const QColor& high, const QColor& low, const QColor& back); + void activeColors(QColor& high, QColor& low, QColor& back) const; + + void setMutedColors(const QColor& high, const QColor& low, const QColor& back); + void mutedColors(QColor& high, QColor& low, QColor& back) const; + + void setUseCustomColors(bool); + bool useCustomColors() const; + + protected slots: + virtual void slotOk(); + virtual void slotApply(); + + signals: + void applied(); + private: + ColorWidget* colorWidget; +}; + + +class KMixApplet : public KPanelApplet +{ + Q_OBJECT + +public: + KMixApplet( const QString& configFile, Type t = Normal, + QWidget *parent = 0, const char *name = 0 ); + virtual ~KMixApplet(); + + struct Colors { + QColor high, low, back, mutedHigh, mutedLow, mutedBack; + }; + + void about(); + void help(); + void preferences(); + void reportBug(); + void paletteChange ( const QPalette & oldPalette ); + + QSize sizeHint() const; + QSizePolicy sizePolicy() const; + int widthForHeight(int) const; + int heightForWidth(int) const; + +protected slots: + void selectMixer(); + void applyPreferences(); + void preferencesDone(); + void updateGeometrySlot(); + +protected: + void resizeEvent( QResizeEvent * ); + void saveConfig(); + void saveConfig( KConfig *config, const QString &grp ); + void loadConfig(); + void loadConfig( KConfig *config, const QString &grp ); + +private: + void positionChange(Position); + void setColors(); + void setColors( const Colors &color ); + + ViewApplet *m_mixerWidget; + QPushButton *m_errorLabel; + AppletConfigDialog *m_pref; + + static int s_instCount; + Mixer *_mixer; + + KMixApplet::Colors _colors; + bool _customColors; + + QLayout* _layout; + + QString _mixerId; + QString _mixerName; + + KAboutData m_aboutData; +}; + + +#endif diff --git a/kmix/kmixctrl.cpp b/kmix/kmixctrl.cpp new file mode 100644 index 00000000..3dbf4f4e --- /dev/null +++ b/kmix/kmixctrl.cpp @@ -0,0 +1,90 @@ +/* + * kmixctrl - kmix volume save/restore utility + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "mixertoolbox.h" +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <klocale.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <kdebug.h> +#include <qptrlist.h> + +#include "kmixtoolbox.h" +#include "mixer.h" +#include "version.h" + +static const char description[] = +I18N_NOOP("kmixctrl - kmix volume save/restore utility"); + +static KCmdLineOptions options[] = +{ + { "s", 0, 0 }, + { "save", I18N_NOOP("Save current volumes as default"), 0 }, + { "r", 0, 0 }, + { "restore", I18N_NOOP("Restore default volumes"), 0 }, + KCmdLineLastOption + // INSERT YOUR COMMANDLINE OPTIONS HERE +}; + +extern "C" KDE_EXPORT int kdemain(int argc, char *argv[]) +{ + KLocale::setMainCatalogue("kmix"); + KAboutData aboutData( "kmixctrl", I18N_NOOP("KMixCtrl"), + APP_VERSION, description, KAboutData::License_GPL, + "(c) 2000 by Stefan Schimanski"); + + aboutData.addAuthor("Stefan Schimanski", 0, "1Stein@gmx.de"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + KApplication app( false, false ); + + // get maximum values + KConfig *config= new KConfig("kmixrc", true, false); + config->setGroup("Misc"); + delete config; + + // create mixers + QString dummyStringHwinfo; + MixerToolBox::initMixer(Mixer::mixers(), false, dummyStringHwinfo); + + // load volumes + if ( args->isSet("restore") ) + { + for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) { + mixer->volumeLoad( KGlobal::config() ); + } + } + + // save volumes + if ( args->isSet("save") ) + { + for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) + mixer->volumeSave( KGlobal::config() ); + } + + MixerToolBox::deinitMixer(); + + return 0; +} diff --git a/kmix/kmixctrl_restore.desktop b/kmix/kmixctrl_restore.desktop new file mode 100644 index 00000000..52e25fc8 --- /dev/null +++ b/kmix/kmixctrl_restore.desktop @@ -0,0 +1,70 @@ +[Desktop Entry] +Type=Service +Name=Restore Mixer Settings +Name[af]=Herstel Menger Instellings +Name[az]=Qarışdırıcı Qurğularını Yenidən Yükle +Name[bg]=Възстановяване на настройките на миксера +Name[bn]=মিক্সার সেটিংস পুনঃস্থাপন করে +Name[br]=Assav kefluniadur ar mesker +Name[bs]=Vrati postavke miksera +Name[ca]=Restaura l'arranjament del mesclador +Name[cs]=Obnovit nastavení směšovače +Name[cy]=Adfer Gosodiadau Cymysgydd +Name[da]=Genopret mikseropsætning +Name[de]=Lautstärkeeinstellungen wiederherstellen +Name[el]=Αποκατάσταση ρυθμίσεων μείκτη +Name[eo]=Restarigu Miksilagordon +Name[es]=Restaurar opciones del mezclador +Name[et]=Mikseri seadistuste taastamine +Name[eu]=Nahasgailuaren ezarpenak berreskuratu +Name[fa]=بازگردانی تنظیمات مخلوطکن +Name[fi]=Palauta mikserin asetukset +Name[fr]=Restaurer la configuration du mixage +Name[gl]=Restaura-los Parámetros do Mesturador +Name[he]=שיחזור הגדרות המערבל +Name[hi]=मिक्सर विन्यास बहाल करें +Name[hr]=Vrati postavke miksera +Name[hu]=A hangkeverő beállításainak visszatöltése +Name[id]=Kembalikan seting Mixer +Name[is]=Sækja aftur stillingar hljóðrása +Name[it]=Ripristina le impostazioni del mixer +Name[ja]=ミキサーの設定を復元 +Name[kk]=Микшер баптауларын қалпына келтіру +Name[km]=ស្ដារការកំណត់ឧបករណ៍លាយឡើងវិញ +Name[ko]=믹서 설정 복원 +Name[lt]=Gražinti mikšerio parametrus +Name[lv]=Atjauno Miksera Uzstādījumus +Name[mk]=Враќање на поставувањата на миксетата +Name[ms]=Pulihkan Tetapan Pengadun +Name[mt]=Reġġa' lura setings tal-Mixer +Name[nb]=Gjennopprett mikserinnstillinger +Name[nds]=Mischerinstellen wedderherstellen +Name[ne]=मिक्सर सेटिङ पूर्वावस्थामा ल्याउनुहोस् +Name[nl]=Mixerinstellingen herstellen +Name[nn]=Gjenopprett miksarinnstillingar +Name[pl]=Odtwarzanie ustawień miksera +Name[pt]=Repor a Configuração do Volume +Name[pt_BR]=Restaurar preferências do mixer +Name[ro]=Reface setările mixerului +Name[ru]=Восстанавливает настройки микшера +Name[se]=Máhcat mixerheivehusat +Name[sk]=Obnov Nastavenia Mixéra +Name[sl]=Obnovi nastavitve mešalnika +Name[sr]=Поврати поставке миксете +Name[sr@Latn]=Povrati postavke miksete +Name[sv]=Återställ mixerinställningar +Name[ta]=ஒன்றுசேர்க்கும் அமைப்புகளை மீட்கவும் +Name[tg]=Аз нав захиракунии Гузоришҳои Омехтакунак +Name[th]=เรียกคืนค่าที่ตั้งไว้ของมิกเซอร์ +Name[tr]=Karıştırıcı Ayarlarını Yeniden Yükle +Name[uk]=Відновити параметри мікшера +Name[uz]=Mikserning moslamalarini qayta tiklash +Name[uz@cyrillic]=Миксернинг мосламаларини қайта тиклаш +Name[ven]=Vhuedzedzani vhuvha ha tshitanganisi +Name[wa]=Rimete come divant les apontiaedjes d maxheu d' sons +Name[xh]=Gcina kwakhona izicwangciso zoMxubi +Name[zh_CN]=恢复混音器设置 +Name[zh_HK]=回復混音器設置 +Name[zh_TW]=回復混音器設定 +Name[zu]=Gcina futhi izilungiso zoMxubi +Exec=kmixctrl --restore diff --git a/kmix/kmixdockwidget.cpp b/kmix/kmixdockwidget.cpp new file mode 100644 index 00000000..18e19af0 --- /dev/null +++ b/kmix/kmixdockwidget.cpp @@ -0,0 +1,391 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * Copyright (C) 2001 Preston Brown <pbrown@kde.org> + * Copyright (C) 2003 Sven Leiber <s.leiber@web.de> + * Copyright (C) 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <kaction.h> +#include <klocale.h> +#include <kapplication.h> +#include <kpanelapplet.h> +#include <kpopupmenu.h> +#include <kglobalsettings.h> +#include <kdialog.h> +#include <kaudioplayer.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <kwin.h> + +#include <qapplication.h> +#include <qcursor.h> +#include <qtooltip.h> +#include <X11/Xlib.h> +#include <fixx11h.h> + +#include "dialogselectmaster.h" +#include "mixer.h" +#include "mixdevicewidget.h" +#include "kmixdockwidget.h" +#include "kwin.h" +#include "viewdockareapopup.h" + +KMixDockWidget::KMixDockWidget( Mixer *mixer, QWidget *parent, const char *name, bool volumePopup ) + : KSystemTray( parent, name ), + m_mixer(mixer), + _dockAreaPopup(0L), + _audioPlayer(0L), + _playBeepOnVolumeChange(false), // disabled due to triggering a "Bug" + _oldToolTipValue(-1), + _oldPixmapType('-'), + _volumePopup(volumePopup) +{ + Mixer* preferredMasterMixer = Mixer::masterCard(); + if ( preferredMasterMixer != 0 ) { + m_mixer = preferredMasterMixer; + } + MixDevice* mdMaster = Mixer::masterCardDevice(); + if ( mdMaster != 0 ) { + m_mixer->setMasterDevice(mdMaster->getPK()); // !! using both Mixer::masterCard() and m_mixer->masterDevice() is nonsense !! + } + createActions(); + createMasterVolWidget(); + connect(this, SIGNAL(quitSelected()), kapp, SLOT(quitExtended())); +} + +KMixDockWidget::~KMixDockWidget() +{ + delete _audioPlayer; + delete _dockAreaPopup; +} + +void KMixDockWidget::createActions() +{ + // Put "Mute" selector in context menu + (void)new KToggleAction( i18n( "M&ute" ), 0, this, SLOT( dockMute() ), + actionCollection(), "dock_mute" ); + KAction *a = actionCollection()->action( "dock_mute" ); + KPopupMenu *popupMenu = contextMenu(); + if ( a ) a->plug( popupMenu ); + + // Put "Select Master Channel" dialog in context menu + if ( m_mixer != 0 ) { + (void)new KAction( i18n("Select Master Channel..."), 0, this, SLOT(selectMaster()), + actionCollection(), "select_master"); + KAction *a2 = actionCollection()->action( "select_master" ); + if (a2) a2->plug( popupMenu ); + } + + // Setup volume preview + if ( _playBeepOnVolumeChange ) { + _audioPlayer = new KAudioPlayer("KDE_Beep_Digital_1.ogg"); + } +} + + +void +KMixDockWidget::createMasterVolWidget() +{ + // Reset flags, so that the dock icon will be reconstructed + _oldToolTipValue = -1; + _oldPixmapType = '-'; + + if (m_mixer == 0) { + // In case that there is no mixer installed, there will be no newVolumeLevels() signal's + // Thus we prepare the dock areas manually + setVolumeTip(); + updatePixmap(); + return; + } + // create devices + + _dockAreaPopup = new ViewDockAreaPopup(0, "dockArea", m_mixer, 0, this); + _dockAreaPopup->createDeviceWidgets(); + m_mixer->readSetFromHWforceUpdate(); // after changing the master device, make sure to re-read (otherwise no "changed()" signals might get sent by the Mixer + /* With the recently introduced QSocketNotifier stuff, we can't rely on regular timer updates + any longer. Also the readSetFromHWforceUpdate() won't be enough. As a workaround, we trigger + all "repaints" manually here. + The call to m_mixer->readSetFromHWforceUpdate() is most likely superfluous, even if we don't use QSocketNotifier (e.g. in backends OSS, Solaris, ...) + */ + setVolumeTip(); + updatePixmap(); + /* We are setting up 3 connections: + * Refreshig the _dockAreaPopup (not anymore neccesary, because ViewBase already does it) + * Refreshing the Tooltip + * Refreshing the Icon + * + */ + // connect( m_mixer, SIGNAL(newVolumeLevels()), _dockAreaPopup, SLOT(refreshVolumeLevels()) ); + connect( m_mixer, SIGNAL(newVolumeLevels()), this, SLOT(setVolumeTip() ) ); + connect( m_mixer, SIGNAL(newVolumeLevels()), this, SLOT(updatePixmap() ) ); +} + + +void KMixDockWidget::selectMaster() +{ + DialogSelectMaster* dsm = new DialogSelectMaster(m_mixer); + connect ( dsm, SIGNAL(newMasterSelected(int, QString&)), SLOT( handleNewMaster(int,QString&)) ); + dsm->show(); + // !! The dialog is modal. Does it delete itself? +} + + +void KMixDockWidget::handleNewMaster(int soundcard_id, QString& channel_id) // !! @todo rework parameters +{ + //kdDebug(67100) << "KMixDockWidget::handleNewMaster() soundcard_id=" << soundcard_id << " , channel_id=" << channel_id << endl; + Mixer *mixer = Mixer::mixers().at(soundcard_id); + if ( mixer == 0 ) { + kdError(67100) << "KMixDockWidget::createPage(): Invalid Mixer (soundcard_id=" << soundcard_id << ")" << endl; + return; // can not happen + } + m_mixer = mixer; + Mixer::setMasterCard(mixer->id()); // We must save this information "somewhere". + Mixer::setMasterCardDevice( channel_id ); + createMasterVolWidget(); +} + + +void +KMixDockWidget::setVolumeTip() +{ + MixDevice *md = 0; + if ( _dockAreaPopup != 0 ) { + md = _dockAreaPopup->dockDevice(); + } + QString tip = ""; + + int newToolTipValue = 0; + if ( md == 0 ) + { + tip = i18n("Mixer cannot be found"); // !! text could be reworked + newToolTipValue = -2; + } + else + { + long val = -1; + if ( md->maxVolume() != 0 ) { + val = (md->getVolume().getAvgVolume(Volume::MMAIN)*100 )/( md->maxVolume() ); + } + newToolTipValue = val + 10000*md->isMuted(); + if ( _oldToolTipValue != newToolTipValue ) { + tip = i18n( "Volume at %1%" ).arg( val ); + if ( md->isMuted() ) { + tip += i18n( " (Muted)" ); + } + } + // create a new "virtual" value. With that we see "volume changes" as well as "muted changes" + newToolTipValue = val + 10000*md->isMuted(); + } + + // The actual updating is only done when the "toolTipValue" was changed + if ( newToolTipValue != _oldToolTipValue ) { + // changed (or completely new tooltip) + if ( _oldToolTipValue >= 0 ) { + // there was an old Tooltip: remove it + QToolTip::remove(this); + } + QToolTip::add(this, tip); + } + _oldToolTipValue = newToolTipValue; +} + +void +KMixDockWidget::updatePixmap() +{ + MixDevice *md = 0; + if ( _dockAreaPopup != 0 ) { + md = _dockAreaPopup->dockDevice(); + } + char newPixmapType; + if ( md == 0 ) + { + newPixmapType = 'e'; + } + else if ( md->isMuted() ) + { + newPixmapType = 'm'; + } + else + { + newPixmapType = 'd'; + } + + + if ( newPixmapType != _oldPixmapType ) { + // Pixmap must be changed => do so + switch ( newPixmapType ) { + case 'e': setPixmap( loadIcon( "kmixdocked_error" ) ); break; + case 'm': setPixmap( loadIcon( "kmixdocked_mute" ) ); break; + case 'd': setPixmap( loadIcon( "kmixdocked" ) ); break; + } + } + + _oldPixmapType = newPixmapType; +} + +void +KMixDockWidget::mousePressEvent(QMouseEvent *me) +{ + if ( _dockAreaPopup == 0 ) { + return KSystemTray::mousePressEvent(me); + } + + // esken: Due to overwhelming request, LeftButton shows the ViewDockAreaPopup, if configured + // to do so. Otherwise the main window will be shown. + if ( me->button() == LeftButton ) + { + if ( ! _volumePopup ) { + // Case 1: User wants to show main window => This is the KSystemTray default action + return KSystemTray::mousePressEvent(me); + } + + // Case 2: User wants to show volume popup + if ( _dockAreaPopup->justHidden() ) + return; + + if ( _dockAreaPopup->isVisible() ) + { + _dockAreaPopup->hide(); + return; + } + + int h = _dockAreaPopup->height(); + int x = this->mapToGlobal( QPoint( 0, 0 ) ).x() + this->width()/2 - _dockAreaPopup->width()/2; + int y = this->mapToGlobal( QPoint( 0, 0 ) ).y() - h; + if ( y < 0 ) + y = y + h + this->height(); + + _dockAreaPopup->move(x, y); // so that the mouse is outside of the widget + + // Now handle Multihead displays. And also make sure that the dialog is not + // moved out-of-the screen on the right (see Bug 101742). + QDesktopWidget* vdesktop = QApplication::desktop(); + const QRect& vScreenSize = vdesktop->screenGeometry(_dockAreaPopup); + if ( (x+_dockAreaPopup->width()) > (vScreenSize.width() + vScreenSize.x()) ) { + // move horizontally, so that it is completely visible + _dockAreaPopup->move(vScreenSize.width() + vScreenSize.x() - _dockAreaPopup->width() -1 , y); + } // horizontally out-of bound + else if ( x < vScreenSize.x() ) { + _dockAreaPopup->move(vScreenSize.x(), y); + } + // the above stuff could also be implemented vertically + + _dockAreaPopup->show(); + KWin::setState(_dockAreaPopup->winId(), NET::StaysOnTop | NET::SkipTaskbar | NET::SkipPager ); + + QWidget::mousePressEvent(me); // KSystemTray's shouldn't do the default action for this + return; + } // LeftMouseButton pressed + else if ( me->button() == MidButton ) { + toggleActive(); + return; + } + else { + KSystemTray::mousePressEvent(me); + } // Other MouseButton pressed + +} + +void +KMixDockWidget::mouseReleaseEvent( QMouseEvent *me ) +{ + + KSystemTray::mouseReleaseEvent(me); +} + +void +KMixDockWidget::wheelEvent(QWheelEvent *e) +{ + MixDevice *md = 0; + if ( _dockAreaPopup != 0 ) { + md = _dockAreaPopup->dockDevice(); + } + if ( md != 0 ) + { + Volume vol = md->getVolume(); + int inc = vol.maxVolume() / 20; + + if ( inc == 0 ) inc = 1; + + for ( int i = 0; i < vol.count(); i++ ) { + int newVal = vol[i] + (inc * (e->delta() / 120)); + if( newVal < 0 ) newVal = 0; + vol.setVolume( (Volume::ChannelID)i, newVal < vol.maxVolume() ? newVal : vol.maxVolume() ); + } + + if ( _playBeepOnVolumeChange ) { + _audioPlayer->play(); + } + md->getVolume().setVolume(vol); + m_mixer->commitVolumeChange(md); + // refresh the toolTip (Qt removes it on a MouseWheel event) + // Mhhh, it doesn't work. Qt does not show it again. + setVolumeTip(); + // Simulate a mouse move to make Qt show the tooltip again + QApplication::postEvent( this, new QMouseEvent( QEvent::MouseMove, QCursor::pos(), Qt::NoButton, Qt::NoButton ) ); + + } +} + +void +KMixDockWidget::dockMute() +{ + MixDevice *md = 0; + if ( _dockAreaPopup != 0 ) + { + md = _dockAreaPopup->dockDevice(); + if ( md != 0 ) { + md->setMuted( !md->isMuted() ); + m_mixer->commitVolumeChange( md ); + } + } +} + +void +KMixDockWidget::contextMenuAboutToShow( KPopupMenu* /* menu */ ) +{ + KAction* showAction = actionCollection()->action("minimizeRestore"); + if ( parentWidget() && showAction ) + { + if ( parentWidget()->isVisible() ) + { + showAction->setText( i18n("Hide Mixer Window") ); + } + else + { + showAction->setText( i18n("Show Mixer Window") ); + } + } + + // Enable/Disable "Muted" menu item + MixDevice *md = 0; + if ( _dockAreaPopup != 0 ) + { + md = _dockAreaPopup->dockDevice(); + KToggleAction *dockMuteAction = static_cast<KToggleAction*>(actionCollection()->action("dock_mute")); + //kdDebug(67100) << "---> md=" << md << "dockMuteAction=" << dockMuteAction << "isMuted=" << md->isMuted() << endl; + if ( md != 0 && dockMuteAction != 0 ) { + dockMuteAction->setChecked( md->isMuted() ); + } + } +} + +#include "kmixdockwidget.moc" + diff --git a/kmix/kmixdockwidget.h b/kmix/kmixdockwidget.h new file mode 100644 index 00000000..273e8533 --- /dev/null +++ b/kmix/kmixdockwidget.h @@ -0,0 +1,82 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * Copyright (C) 2003 Sven Leiber <s.leiber@web.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KMIXDOCKWIDGET_H +#define KMIXDOCKWIDGET_H + +class QFrame; +class QString; +#include <qwidget.h> +#include <qvbox.h> + +#include <ksystemtray.h> + +class Mixer; +class KAudioPlayer; +class MixDeviceWidget; +class Mixer; +class ViewDockAreaPopup; +class Volume; + +class KMixDockWidget : public KSystemTray { + Q_OBJECT + + friend class KMixWindow; + + public: + KMixDockWidget(Mixer *, QWidget *parent=0, const char *name=0, bool volumePopup=true); + ~KMixDockWidget(); + + void setErrorPixmap(); + void ignoreNextEvent(); + ViewDockAreaPopup* getDockAreaPopup(); + + Mixer *m_mixer; + ViewDockAreaPopup *_dockAreaPopup; + KAudioPlayer *_audioPlayer; + + public slots: + void setVolumeTip(); + void updatePixmap(); + + protected: + void createMasterVolWidget(); + void createActions(); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void wheelEvent(QWheelEvent *); + void contextMenuAboutToShow( KPopupMenu* menu ); + void toggleMinimizeRestore(); + + private: + bool _playBeepOnVolumeChange; + bool _ignoreNextEvent; + int _oldToolTipValue; + char _oldPixmapType; + bool _volumePopup; + private slots: + void dockMute(); + void selectMaster(); + void handleNewMaster(int soundcard_id, QString& channel_id); +}; + +#endif diff --git a/kmix/kmixerwidget.cpp b/kmix/kmixerwidget.cpp new file mode 100644 index 00000000..cf597fb4 --- /dev/null +++ b/kmix/kmixerwidget.cpp @@ -0,0 +1,266 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * Copyright (C) 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +// Qt +#include <qlabel.h> +#include <qlayout.h> +#include <qslider.h> +#include <qstring.h> +#include <qtooltip.h> +#include <qapplication.h> // for QApplication::revsreseLayout() + +// KDE +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <ktabwidget.h> + +// KMix +#include "mixdevicewidget.h" +#include "kmixerwidget.h" +#include "kmixtoolbox.h" +#include "mixer.h" +#include "viewinput.h" +#include "viewoutput.h" +#include "viewswitches.h" +// KMix experimental +#include "viewgrid.h" +#include "viewsurround.h" + + +/** + This widget is embedded in the KMix Main window. Each Hardware Mixer is visualized by one KMixerWidget. + KMixerWidget contains + (a) a headline where you can change Mixer's (if you got more than one Mixer) + (b) a Tab with 2-4 Tabs (containing View's with sliders, switches and other GUI elements visualizing the Mixer) + (c) A balancing slider + (d) A label containg the mixer name +*/ +KMixerWidget::KMixerWidget( int _id, Mixer *mixer, const QString &mixerName, + MixDevice::DeviceCategory categoryMask, + QWidget * parent, const char * name, ViewBase::ViewFlags vflags ) + : QWidget( parent, name ), _mixer(mixer), m_balanceSlider(0), + m_topLayout(0), + m_id( _id ), + _iconsEnabled( true ), _labelsEnabled( false ), _ticksEnabled( false ), + _valueStyle ( -1 ) // this definitely does not correspond to the 'default value display' style, + // so the style will be set by a later call to setValueStyle() + +{ + m_categoryMask = categoryMask; + + if ( _mixer ) + { + createLayout(vflags); + } + else + { + // No mixer found + // !! Fix this: This is actually never shown! + QBoxLayout *layout = new QHBoxLayout( this ); + QString s = i18n("Invalid mixer"); + if ( !mixerName.isEmpty() ) + s.append(" \"").append(mixerName).append("\""); + QLabel *errorLabel = new QLabel( s, this ); + errorLabel->setAlignment( QLabel::AlignCenter | QLabel::WordBreak ); + layout->addWidget( errorLabel ); + } +} + +KMixerWidget::~KMixerWidget() +{ +} + +/** + * Creates the widgets as described in the KMixerWidget constructor + */ +void KMixerWidget::createLayout(ViewBase::ViewFlags vflags) +{ + // delete old objects + if( m_balanceSlider ) { + delete m_balanceSlider; + } + if( m_topLayout ) { + delete m_topLayout; + } + + // create main layout + m_topLayout = new QVBoxLayout( this, 0, 3, "m_topLayout" ); + + // Create tabs of input + output + [...] + m_ioTab = new KTabWidget( this, "ioTab" ); + m_topLayout->add( m_ioTab ); + + + /******************************************************************* + * Now the main GUI is created. + * 1) Select a (GUI) profile, which defines which controls to show on which Tab + * 2a) Create the Tab's and the corresponding Views + * 2b) Create device widgets + * 2c) Add Views to Tab + ********************************************************************/ + //KMixGUIProfile* prof = MixerToolbox::selectProfile(_mixer); + + + possiblyAddView(new ViewOutput ( m_ioTab, "output", i18n("Output"), _mixer, vflags ) ); + possiblyAddView(new ViewInput( m_ioTab, "input", i18n("Input"), _mixer, vflags ) ); + possiblyAddView(new ViewSwitches( m_ioTab, "switches", i18n("Switches"), _mixer, vflags ) ); + if ( vflags & ViewBase::Experimental_SurroundView ) + possiblyAddView( new ViewSurround( m_ioTab, "surround", i18n("Surround"), _mixer, vflags ) ); + if ( vflags & ViewBase::Experimental_GridView ) + possiblyAddView( new ViewGrid( m_ioTab, "grid", i18n("Grid"), _mixer, vflags ) ); + + + // *** Lower part: Slider and Mixer Name ************************************************ + QHBoxLayout *balanceAndDetail = new QHBoxLayout( m_topLayout, 8, "balanceAndDetail"); + // Create the left-right-slider + m_balanceSlider = new QSlider( -100, 100, 25, 0, QSlider::Horizontal, this, "RightLeft" ); + m_balanceSlider->setTickmarks( QSlider::Below ); + m_balanceSlider->setTickInterval( 25 ); + m_balanceSlider->setMinimumSize( m_balanceSlider->sizeHint() ); + m_balanceSlider->setFixedHeight( m_balanceSlider->sizeHint().height() ); + + QLabel *mixerName = new QLabel(this, "mixerName"); + mixerName->setText( _mixer->mixerName() ); + + balanceAndDetail->addSpacing( 10 ); + + balanceAndDetail->addWidget( m_balanceSlider ); + balanceAndDetail->addWidget( mixerName ); + balanceAndDetail->addSpacing( 10 ); + + connect( m_balanceSlider, SIGNAL(valueChanged(int)), this, SLOT(balanceChanged(int)) ); + QToolTip::add( m_balanceSlider, i18n("Left/Right balancing") ); + + // --- "MenuBar" toggling from the various View's --- + + + + show(); + // kdDebug(67100) << "KMixerWidget::createLayout(): EXIT\n"; +} + +void KMixerWidget::possiblyAddView(ViewBase* vbase) +{ + if ( vbase->count() == 0 ) + delete vbase; + else { + _views.push_back(vbase); + vbase ->createDeviceWidgets(); + m_ioTab->addTab( vbase , vbase->caption() ); + connect( vbase, SIGNAL(toggleMenuBar()), parentWidget(), SLOT(toggleMenuBar()) ); + } +} + +void KMixerWidget::setIcons( bool on ) +{ + for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) { + ViewBase* mixerWidget = *it; + KMixToolBox::setIcons(mixerWidget->_mdws, on); + } // for all tabs +} + +void KMixerWidget::setLabels( bool on ) +{ + if ( _labelsEnabled!=on ) { + // value was changed + _labelsEnabled = on; + for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) { + ViewBase* mixerWidget = *it; + KMixToolBox::setLabels(mixerWidget->_mdws, on); + } // for all tabs + } +} + +void KMixerWidget::setTicks( bool on ) +{ + if ( _ticksEnabled!=on ) { + // value was changed + _ticksEnabled = on; + for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) { + ViewBase* mixerWidget = *it; + KMixToolBox::setTicks(mixerWidget->_mdws, on); + } // for all tabs + } +} + +void KMixerWidget::setValueStyle( int vs ) +{ + if ( _valueStyle!=vs ) { + // value was changed + _valueStyle = vs; + for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) { + ViewBase* mixerWidget = *it; + KMixToolBox::setValueStyle(mixerWidget->_mdws, vs); + } // for all tabs + } +} + + +/** + * @todo : Is the view list already filled, when loadConfig() is called? + */ +void KMixerWidget::loadConfig( KConfig *config, const QString &grp ) +{ + + for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) { + ViewBase* mixerWidget = *it; + QString viewPrefix = "View."; + viewPrefix += mixerWidget->name(); + KMixToolBox::loadConfig(mixerWidget->_mdws, config, grp, viewPrefix ); + mixerWidget->configurationUpdate(); + } // for all tabs +} + + + +void KMixerWidget::saveConfig( KConfig *config, const QString &grp ) +{ + config->setGroup( grp ); + // Write mixer name. It cannot be changed in the Mixer instance, + // it is only saved for diagnostical purposes (analyzing the config file). + config->writeEntry("Mixer_Name_Key", _mixer->mixerName()); + + for ( std::vector<ViewBase*>::iterator it = _views.begin(); it != _views.end(); it++) { + ViewBase* mixerWidget = *it; + QString viewPrefix = "View."; + viewPrefix += mixerWidget->name(); + KMixToolBox::saveConfig(mixerWidget->_mdws, config, grp, viewPrefix ); + } // for all tabs +} + + +void KMixerWidget::toggleMenuBarSlot() { + emit toggleMenuBar(); +} + +// in RTL mode, the slider is reversed, we cannot just connect the signal to setBalance() +// hack arround it before calling _mixer->setBalance() +void KMixerWidget::balanceChanged(int balance) +{ + if (QApplication::reverseLayout()) + balance = -balance; + + _mixer->setBalance( balance ); +} + +#include "kmixerwidget.moc" diff --git a/kmix/kmixerwidget.h b/kmix/kmixerwidget.h new file mode 100644 index 00000000..442bded5 --- /dev/null +++ b/kmix/kmixerwidget.h @@ -0,0 +1,118 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KMIXERWIDGET_H +#define KMIXERWIDGET_H + +#include <vector> + +#include <qwidget.h> +#include <qptrlist.h> +class QString; +class QGridLayout; + +#include <kpanelapplet.h> +class KPopupMenu; + +#include "mixer.h" +#include "mixdevicewidget.h" + +// QT +class QSlider; + + +// KDE +class KActionCollection; +class KActionMenu; +class KConfig; +class KTabWidget; + +// KMix +class Mixer; +#include "viewbase.h" +class ViewInput; +class ViewOutput; +class ViewSwitches; +// KMix experimental +class ViewGrid; +class ViewSurround; + + +class KMixerWidget : public QWidget +{ + Q_OBJECT + + public: + KMixerWidget( int _id, Mixer *mixer, const QString &mixerName, + MixDevice::DeviceCategory categoryMask = MixDevice::ALL , + QWidget *parent=0, const char *name=0, ViewBase::ViewFlags vflags=0 ); + ~KMixerWidget(); + + enum KMixerWidgetIO { OUTPUT=0, INPUT }; + + const Mixer *mixer() const { return _mixer; }; + + int id() const { return m_id; }; + + KActionCollection* getActionCollection() const { return 0; /* m_actions; */ } + + signals: + void masterMuted( bool ); + void newMasterVolume(Volume vol); + void toggleMenuBar(); + + public slots: + void setTicks( bool on ); + void setLabels( bool on ); + void setIcons( bool on ); + void setValueStyle( int vs ); + void toggleMenuBarSlot(); + + void saveConfig( KConfig *config, const QString &grp ); + void loadConfig( KConfig *config, const QString &grp ); + + private slots: + //void updateBalance(); + void balanceChanged(int balance); + + private: + Mixer *_mixer; + QSlider *m_balanceSlider; + QVBoxLayout *m_topLayout; // contains the Card selector, TabWidget and balance slider + + KTabWidget* m_ioTab; + + std::vector<ViewBase*> _views; + int m_id; + + KActionMenu *m_toggleMixerChannels; + + bool _iconsEnabled; + bool _labelsEnabled; + bool _ticksEnabled; + int _valueStyle; + MixDevice::DeviceCategory m_categoryMask; + + void createLayout(ViewBase::ViewFlags vflags); + void possiblyAddView(ViewBase* vbase); +}; + +#endif diff --git a/kmix/kmixprefdlg.cpp b/kmix/kmixprefdlg.cpp new file mode 100644 index 00000000..e2788ceb --- /dev/null +++ b/kmix/kmixprefdlg.cpp @@ -0,0 +1,132 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * Copyright (C) 2001 Preston Brown <pbrown@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qbuttongroup.h> +#include <qlayout.h> +#include <qwhatsthis.h> +#include <qcheckbox.h> +#include <qlabel.h> +#include <qradiobutton.h> + +#include <klocale.h> +// For "kapp" +#include <kapplication.h> + +#include "kmix.h" +#include "kmixprefdlg.h" +#include "kmixerwidget.h" + + +KMixPrefDlg::KMixPrefDlg( QWidget *parent ) + : KDialogBase( Plain, i18n( "Configure" ), + Ok|Cancel|Apply, Ok, parent ) +{ + // general buttons + m_generalTab = plainPage( /* i18n("&General") */ ); + + QBoxLayout *layout = new QVBoxLayout( m_generalTab ); + layout->setSpacing( KDialog::spacingHint() ); + + m_dockingChk = new QCheckBox( i18n("&Dock into panel"), m_generalTab ); + layout->addWidget( m_dockingChk ); + QWhatsThis::add(m_dockingChk, i18n("Docks the mixer into the KDE panel")); + + m_volumeChk = new QCheckBox(i18n("Enable system tray &volume control"), + m_generalTab); + layout->addWidget(m_volumeChk); + + m_showTicks = new QCheckBox( i18n("Show &tickmarks"), m_generalTab ); + layout->addWidget( m_showTicks ); + QWhatsThis::add(m_showTicks, + i18n("Enable/disable tickmark scales on the sliders")); + + m_showLabels = new QCheckBox( i18n("Show &labels"), m_generalTab ); + layout->addWidget( m_showLabels ); + QWhatsThis::add(m_showLabels, + i18n("Enables/disables description labels above the sliders")); + + + m_onLogin = new QCheckBox( i18n("Restore volumes on login"), m_generalTab ); + layout->addWidget( m_onLogin ); + + QBoxLayout *numbersLayout = new QHBoxLayout( layout ); + QButtonGroup *numbersGroup = new QButtonGroup( 3, Qt::Horizontal, i18n("Numbers"), m_generalTab ); + numbersGroup->setRadioButtonExclusive(true); + QLabel* qlbl = new QLabel( i18n("Volume Values: "), m_generalTab ); + _rbNone = new QRadioButton( i18n("&None"), m_generalTab ); + _rbAbsolute = new QRadioButton( i18n("A&bsolute"), m_generalTab ); + _rbRelative = new QRadioButton( i18n("&Relative"), m_generalTab ); + numbersGroup->insert(_rbNone); + numbersGroup->insert(_rbAbsolute); + numbersGroup->insert(_rbRelative); + numbersGroup->hide(); + + numbersLayout->add(qlbl); + numbersLayout->add(_rbNone); + numbersLayout->add(_rbAbsolute); + numbersLayout->add(_rbRelative); + numbersLayout->addStretch(); + + QBoxLayout *orientationLayout = new QHBoxLayout( layout ); + QButtonGroup* orientationGroup = new QButtonGroup( 2, Qt::Horizontal, i18n("Orientation"), m_generalTab ); + //orientationLayout->add(orientationGroup); + orientationGroup->setRadioButtonExclusive(true); + QLabel* qlb = new QLabel( i18n("Slider Orientation: "), m_generalTab ); + _rbHorizontal = new QRadioButton(i18n("&Horizontal"), m_generalTab ); + _rbVertical = new QRadioButton(i18n("&Vertical" ), m_generalTab ); + orientationGroup->insert(_rbHorizontal); + orientationGroup->insert(_rbVertical); + orientationGroup->hide(); + //orientationLayout->add(qlb); + //orientationLayout->add(orientationGroup); + + orientationLayout->add(qlb); + orientationLayout->add(_rbHorizontal); + orientationLayout->add(_rbVertical); + + orientationLayout->addStretch(); + layout->addStretch(); + enableButtonSeparator(true); + + connect( this, SIGNAL(applyClicked()), this, SLOT(apply()) ); + connect( this, SIGNAL(okClicked()), this, SLOT(apply()) ); +} + +KMixPrefDlg::~KMixPrefDlg() +{ +} + +void KMixPrefDlg::apply() +{ + // disabling buttons => users sees that we are working + enableButtonOK(false); + enableButtonCancel(false); + enableButtonApply(false); + kapp->processEvents(); + emit signalApplied( this ); + // re-enable (in case of "Apply") + enableButtonOK(true); + enableButtonCancel(true); + enableButtonApply(true); +} + +#include "kmixprefdlg.moc" diff --git a/kmix/kmixprefdlg.h b/kmix/kmixprefdlg.h new file mode 100644 index 00000000..3519d045 --- /dev/null +++ b/kmix/kmixprefdlg.h @@ -0,0 +1,67 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KPREFDLG_H +#define KPREFDLG_H + +#include <kdialogbase.h> + +class KMixPrefWidget; +class KMixApp; +class QCheckBox; +class QRadioButton; + +class +KMixPrefDlg : public KDialogBase +{ + Q_OBJECT + + friend class KMixWindow; + + public: + KMixPrefDlg( QWidget *parent ); + ~KMixPrefDlg(); + + signals: + void signalApplied( KMixPrefDlg *prefDlg ); + + private slots: + void apply(); + + private: + QFrame *m_generalTab; + KMixApp *m_mixApp; + KMixPrefWidget *m_mixPrefTab; + + QCheckBox *m_dockingChk; + QCheckBox *m_volumeChk; + QCheckBox *m_hideOnCloseChk; + QCheckBox *m_showTicks; + QCheckBox *m_showLabels; + QCheckBox *m_onLogin; + QRadioButton *_rbVertical; + QRadioButton *_rbHorizontal; + QRadioButton *_rbNone; + QRadioButton *_rbAbsolute; + QRadioButton *_rbRelative; +}; + +#endif diff --git a/kmix/kmixtoolbox.cpp b/kmix/kmixtoolbox.cpp new file mode 100644 index 00000000..06a6ed36 --- /dev/null +++ b/kmix/kmixtoolbox.cpp @@ -0,0 +1,215 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "qcolor.h" +#include "qwidget.h" +#include "qstring.h" + +//#include <kdebug.h> +#include <kglobalaccel.h> +#include <klocale.h> + +#include "mdwslider.h" +#include "mdwswitch.h" +#include "mixdevicewidget.h" +#include "mixdevice.h" +#include "mixer.h" + +#include "kmixtoolbox.h" + +/*********************************************************************************** + KMixToolbox contains several GUI relevant methods that are shared between the + KMix Main Program, and the KMix Applet. + kmixctrl - as not non-GUI application - does NOT link to KMixToolBox. + + This means: Shared GUI stuff goes into the KMixToolBox class , non-GUI stuff goes + into the MixerToolBox class. + ***********************************************************************************/ +void KMixToolBox::setIcons(QPtrList<QWidget> &mdws, bool on ) { + for ( QWidget *qmdw=mdws.first(); qmdw!=0; qmdw=mdws.next() ) { + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here + static_cast<MixDeviceWidget*>(qmdw)->setIcons( on ); + } + } +} + +void KMixToolBox::setLabels(QPtrList<QWidget> &mdws, bool on ) { + QWidget *qmdw; + for ( qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) { + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here + static_cast<MixDeviceWidget*>(qmdw)->setLabeled( on ); + } + } +} + +void KMixToolBox::setTicks(QPtrList<QWidget> &mdws, bool on ) { + QWidget *qmdw; + for ( qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) { + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider + static_cast<MixDeviceWidget*>(qmdw)->setTicks( on ); + } + } +} + +void KMixToolBox::setValueStyle(QPtrList<QWidget> &mdws, int vs ) { + QWidget *qmdw; + for ( qmdw=mdws.first(); qmdw != 0; qmdw=mdws.next() ) { + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider + static_cast<MixDeviceWidget*>(qmdw)->setValueStyle( (MixDeviceWidget::ValueStyle) vs ); + } + } +} + +void KMixToolBox::loadConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix) { + int n = 0; + config->setGroup( grp ); + int num = config->readNumEntry( viewPrefix + ".Devs", 0); + + for ( QWidget *qmdw=mdws.first(); qmdw!=0 && n<num; qmdw=mdws.next() ) { + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here + MixDeviceWidget* mdw = static_cast<MixDeviceWidget*>(qmdw); + QString devgrp; + + /* + * Compatibility config loader! We use the old config group only, if the + * new one does not exist. + * The new group system has been introduced, because it accounts much + * better for soundcard driver updates (if numbering changes, or semantics + * of an ID changes like ALSA changing from "Disable Amplifier" to "External Amplifier"). + */ + // !!! check + devgrp.sprintf( "%s.%s.Dev%s", viewPrefix.ascii(), grp.ascii(), mdw->mixDevice()->getPK().ascii() ); + + /** + Find an appropriate group name for capture GUI elements. + We try devgrp.append(".Capture") + If it doesn't exist, we fall back to devgrp. + This is the second compatibility measure, and was introduced for KDE3.5.2. + */ + if ( mdw->mixDevice()->getVolume().isCapture() ) { + /* A "capture" GUI element must save its own state. Otherwise playback and capture + properties would be written twice under the same name. This would mean, when + restoring, both would get the same value. This is bad, because hidden sliders will re-appear + after restart of KMix, and a lot of other nasty GUI-related problems. + So we add ".Capture" to the group name. + See bug 121451 "KMix panel applet shows broken duplicates of bass, treble sliders" + + The name should have been set in the backend class, but we REALLY cannot do this for KDE3.5.x. !! + This issue will be fixed in KDE4 by the great config cleanup. + */ + QString devgrpTmp(devgrp); + devgrpTmp.append(".Capture"); + if ( config->hasGroup(devgrpTmp) ) { + // Group for capture device exists => take over the name + devgrp = devgrpTmp; + } + else { + // do nothing => keep old name (devgrp). + // Saving wil autmatically create the group 'devgrp.append(".Capture")' + kdDebug(67100) << "KMixToolBox::loadConfig() capture fallback activcated. Fallback group is " << devgrp << endl; + } + } // isCapture() + if ( ! config->hasGroup(devgrp) ) { + // fall back to old-Style configuration (KMix2.1 and earlier) + devgrp.sprintf( "%s.%s.Dev%i", viewPrefix.ascii(), grp.ascii(), n ); + // this configuration group will be deleted when config is saved + } + config->setGroup( devgrp ); + + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider + // only sliders have the ability to split apart in mutliple channels + bool splitChannels = config->readBoolEntry("Split", false); + mdw->setStereoLinked( !splitChannels ); + } + mdw->setDisabled( !config->readBoolEntry("Show", true) ); + + KGlobalAccel *keys=mdw->keys(); + if ( keys ) + { + QString devgrpkeys; + devgrpkeys.sprintf( "%s.%s.Dev%i.keys", viewPrefix.ascii(), grp.ascii(), n ); + //kdDebug(67100) << "KMixToolBox::loadConfig() load Keys " << devgrpkeys << endl; + + // please see KMixToolBox::saveConfig() for some rambling about saving/loading Keys + keys->setConfigGroup(devgrpkeys); + keys->readSettings(config); + keys->updateConnections(); + } + + n++; + } // if it is a MixDeviceWidget + } // for all widgets +} + + +void KMixToolBox::saveConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix) { + config->setGroup( grp ); + config->writeEntry( viewPrefix + ".Devs", mdws.count() ); + + int n=0; + for ( QWidget *qmdw=mdws.first(); qmdw!=0; qmdw=mdws.next() ) { + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- play safe here + MixDeviceWidget* mdw = static_cast<MixDeviceWidget*>(qmdw); + + QString devgrp; + devgrp.sprintf( "%s.%s.Dev%i", viewPrefix.ascii(), grp.ascii(), n ); + if ( ! config->hasGroup(devgrp) ) { + // old-Style configuration (KMix2.1 and earlier => remove now unused group + config->deleteGroup(devgrp); + } + devgrp.sprintf( "%s.%s.Dev%s", viewPrefix.ascii(), grp.ascii(), mdw->mixDevice()->getPK().ascii() ); + //devgrp.sprintf( "%s.%s.Dev%i", viewPrefix.ascii(), grp.ascii(), n ); + + if ( mdw->mixDevice()->getVolume().isCapture() ) { + /* see loadConfig() for the rationale of having an own name for capture devices. */ + devgrp.append(".Capture"); + } // isCapture() + + config->setGroup( devgrp ); + + if ( qmdw->inherits("MixDeviceWidget") ) { // -<- in reality it is only in MDWSlider + // only sliders have the ability to split apart in mutliple channels + config->writeEntry( "Split", ! mdw->isStereoLinked() ); + } + config->writeEntry( "Show" , ! mdw->isDisabled() ); + + // Save key bindings + /* + Implementation hint: Conceptually keys SHOULD be bound to the actual hardware, and not + to one GUI representation. Both work, but it COULD confuse users, if we have multiple + GUI representations (e.g. "Dock Icon" and "Main Window"). + If you think about this aspect more deeply, you will find out that this is the case already + today with "kmixapplet" and "kmix main application". It would really nice to rework this. + */ + KGlobalAccel *keys=mdw->keys(); + if (keys) { + QString devgrpkeys; + devgrpkeys.sprintf( "%s.%s.Dev%i.keys", viewPrefix.ascii(), grp.ascii(), n ); + //kdDebug(67100) << "KMixToolBox::saveConfig() save Keys " << devgrpkeys << endl; + keys->setConfigGroup(devgrpkeys); + keys->writeSettings(config); + } + n++; + } // if it is a MixDeviceWidget + } // for all widgets +} + diff --git a/kmix/kmixtoolbox.h b/kmix/kmixtoolbox.h new file mode 100644 index 00000000..4cb5dd8f --- /dev/null +++ b/kmix/kmixtoolbox.h @@ -0,0 +1,28 @@ +#ifndef KMIXTOOLBOX_H +#define KMIXTOOLBOX_H + +#include "qptrlist.h" +#include "qwidget.h" + +class Mixer; + +class KConfig; + +/** + * This toolbox contains various static methods that are shared throughout KMix. + * The reason, why it is not put in a common base class is, that the classes are + * very different and cannot be changed (e.g. KPanelApplet) without major headache. + */ + +class KMixToolBox { + public: + static void setIcons (QPtrList<QWidget> &mdws, bool on ); + static void setLabels (QPtrList<QWidget> &mdws, bool on ); + static void setTicks (QPtrList<QWidget> &mdws, bool on ); + static void setValueStyle (QPtrList<QWidget> &mdws, int vs ); + static void loadConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix ); + static void saveConfig(QPtrList<QWidget> &mdws, KConfig *config, const QString &grp, const QString &viewPrefix ); +}; + + +#endif diff --git a/kmix/kmixui.rc b/kmix/kmixui.rc new file mode 100644 index 00000000..aada37d8 --- /dev/null +++ b/kmix/kmixui.rc @@ -0,0 +1,21 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kmix" version="3"> + +<ActionProperties> + <Action name="hide_kmixwindow" icon="window"/> +</ActionProperties> + +<MenuBar> + <Menu name="file"><text>&File</text> + <Action name="file_new_tab"/> + <Action name="file_close_tab"/> + </Menu> + <Menu name="settings"> + <Action name="settings_global" append="configure_merge"/> + </Menu> + <Menu name="help" append="about_merge"><text>&Help</text> + <Action name="hwinfo"/> + </Menu> +</MenuBar> + +</kpartgui> diff --git a/kmix/ksmallslider.cpp b/kmix/ksmallslider.cpp new file mode 100644 index 00000000..38e43639 --- /dev/null +++ b/kmix/ksmallslider.cpp @@ -0,0 +1,516 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <kdebug.h> + +#include <qwidget.h> +#include <qpainter.h> +#include <qcolor.h> +#include <qbrush.h> +#include <qstyle.h> + +#include "kglobalsettings.h" +#include "ksmallslider.h" + +/* +static const QColor mutedHighColor2 = "#FFFFFF"; +static const QColor mutedLowColor2 = "#808080"; +static const QColor backColor2 = "#000000"; +*/ + +KSmallSlider::KSmallSlider( QWidget *parent, const char *name ) + : QWidget( parent, name ), _orientation( Qt::Vertical ) +{ + init(); +} + +KSmallSlider::KSmallSlider( Qt::Orientation orientation, QWidget *parent, const char *name ) + : QWidget( parent, name ), _orientation( orientation ) +{ + init(); +} + +KSmallSlider::KSmallSlider( int minValue, int maxValue, int pageStep, + int value, Qt::Orientation orientation, + QWidget *parent, const char *name ) + : QWidget( parent, name ), + QRangeControl( minValue, maxValue, 1, pageStep, value ), _orientation( orientation) +{ + init(); + // sliderVal = value; +} + +void KSmallSlider::init() +{ + // !! the following 2 values must be -1, to make sure the values are not the real values. + // Otherwise some code below could determine that no change has happened and to send + // no signals or to do no initial paint. + // sliderPos = -1; + // state = Idle; + //track = TRUE; + //setMouseTracking(true); + grayed = false; + setFocusPolicy( TabFocus ); + + colHigh = QColor(0,255,0); + colLow = QColor(255,0,0); + colBack = QColor(0,0,0); + + grayHigh = QColor(255,255,255); + grayLow = QColor(128,128,128); + grayBack = QColor(0,0,0); +} +/* +void KSmallSlider::setTracking( bool enable ) +{ + track = enable; +} +*/ +int KSmallSlider::positionFromValue( int v ) const +{ + return QRangeControl::positionFromValue( v, available() ); +} + +int KSmallSlider::valueFromPosition( int p ) const +{ + if ( _orientation == Qt::Vertical ) { + // Coordiante System starts at TopLeft, but the slider values increase from Bottom to Top + // Thus "revert" the position + int avail = available(); + return QRangeControl::valueFromPosition( avail - p, avail ); + } + else { + // Horizontal everything is fine. Slider values match with Coordinate System + return QRangeControl::valueFromPosition( p, available() ); + } +} + +void KSmallSlider::rangeChange() +{ + /* + int newPos = positionFromValue( QRangeControl::value() ); + if ( newPos != sliderPos ) { + sliderPos = newPos; + } + */ + update(); +} + +void KSmallSlider::valueChange() +{ + //kdDebug(67100) << "KSmallSlider::valueChange() value=" << value() << "\n"; + update(); + emit valueChanged(value()); + /* + if ( sliderVal != QRangeControl::value() ) { + //int newPos = positionFromValue( QRangeControl::value() ); + //sliderPos = newPos; + sliderVal = QRangeControl::value(); + update(); + emit valueChanged(value()); + } + */ +} + +void KSmallSlider::resizeEvent( QResizeEvent * ) +{ + update(); + //QWidget::resizeEvent( ev ); +} + +// Returns the really available space for the slider. If there is no space, 0 is returned; +int KSmallSlider::available() const +{ + int available = 0; + if ( _orientation == Qt::Vertical) { + available = height(); + } + else { + available = width(); + } + if ( available > 1 ) { + available -= 2; + } + else { + available = 0; + } + return available; +} + + + +namespace +{ + +void gradient( QPainter &p, bool hor, const QRect &rect, const QColor &ca, const QColor &cb, int /*ncols*/) +{ + int rDiff, gDiff, bDiff; + int rca, gca, bca, rcb, gcb, bcb; + + register int x, y; + + if ((rect.width()<=0) || (rect.height()<=0)) return; + + rDiff = (rcb = cb.red()) - (rca = ca.red()); + gDiff = (gcb = cb.green()) - (gca = ca.green()); + bDiff = (bcb = cb.blue()) - (bca = ca.blue()); + + register int rl = rca << 16; + register int gl = gca << 16; + register int bl = bca << 16; + + int rcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * rDiff; + int gcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * gDiff; + int bcdelta = ((1<<16) / ((!hor) ? rect.height() : rect.width())) * bDiff; + + // these for-loops could be merged, but the if's in the inner loop + // would make it slow + if (!hor) + { + for ( y = rect.top(); y <= rect.bottom(); y++ ) { + rl += rcdelta; + gl += gcdelta; + bl += bcdelta; + + p.setPen(QColor(rl>>16, gl>>16, bl>>16)); + p.drawLine(rect.left(), y, rect.right(), y); + } + } else + { + for( x = rect.left(); x <= rect.right(); x++) { + rl += rcdelta; + gl += gcdelta; + bl += bcdelta; + + p.setPen(QColor(rl>>16, gl>>16, bl>>16)); + p.drawLine(x, rect.top(), x, rect.bottom()); + } + } +} + +QColor interpolate( QColor low, QColor high, int percent ) { + if ( percent<=0 ) return low; else + if ( percent>=100 ) return high; else + return QColor( + low.red() + (high.red()-low.red()) * percent/100, + low.green() + (high.green()-low.green()) * percent/100, + low.blue() + (high.blue()-low.blue()) * percent/100 ); +} + +} + +void KSmallSlider::paintEvent( QPaintEvent * ) +{ +// kdDebug(67100) << "KSmallSlider::paintEvent: width() = " << width() << ", height() = " << height() << endl; + QPainter p( this ); + + int sliderPos = positionFromValue( QRangeControl::value() ); + + // ------------------------ draw 3d border --------------------------------------------- + style().drawPrimitive ( QStyle::PE_Panel, &p, QRect( 0, 0, width(), height() ), colorGroup(), TRUE ); + + + // ------------------------ draw lower/left part ---------------------------------------- + if ( width()>2 && height()>2 ) + { + if ( _orientation == Qt::Horizontal ) { + QRect outer = QRect( 1, 1, sliderPos, height() - 2 ); +// kdDebug(67100) << "KSmallSlider::paintEvent: outer = " << outer << endl; + + if ( grayed ) + gradient( p, true, outer, grayLow, + interpolate( grayLow, grayHigh, 100*sliderPos/(width()-2) ), + 32 ); + else + gradient( p, true, outer, colLow, + interpolate( colLow, colHigh, 100*sliderPos/(width()-2) ), + 32 ); + } + else { + QRect outer = QRect( 1, height()-sliderPos-1, width() - 2, sliderPos-1 ); +/* + kdDebug(67100) << "KSmallSlider::paintEvent: sliderPos=" << sliderPos + << "height()=" << height() + << "width()=" << width() + << "outer = " << outer << endl; +*/ + if ( grayed ) + gradient( p, false, outer, + interpolate( grayLow, grayHigh, 100*sliderPos/(height()-2) ), + grayLow, 32 ); + else + gradient( p, false, outer, + interpolate( colLow, colHigh, 100*sliderPos/(height()-2) ), + colLow, 32 ); + } + + // -------- draw upper/right part -------------------------------------------------- + QRect inner; + if ( _orientation == Qt::Vertical ) { + inner = QRect( 1, 1, width() - 2, height() - 2 -sliderPos ); + } + else { + inner = QRect( sliderPos + 1, 1, width() - 2 - sliderPos, height() - 2 ); + } + + if ( grayed ) { + p.setBrush( grayBack ); + p.setPen( grayBack ); + } else { + p.setBrush( colBack ); + p.setPen( colBack ); + } + p.drawRect( inner ); + } +} + +void KSmallSlider::mousePressEvent( QMouseEvent *e ) +{ + //resetState(); + + if ( e->button() == RightButton ) { + return; + } + + // state = Dragging; + //emit sliderPressed(); + + int pos = goodPart( e->pos() ); + moveSlider( pos ); +} + +void KSmallSlider::mouseMoveEvent( QMouseEvent *e ) +{ + /* + if ( state != Dragging ) + return; + */ + int pos = goodPart( e->pos() ); + moveSlider( pos ); +} + +void KSmallSlider::wheelEvent( QWheelEvent * e) +{ +// kdDebug(67100) << "KSmallslider::wheelEvent()" << endl; + /* Unfortunately KSmallSlider is no MixDeviceWidget, so we don't have access to + * the MixDevice. + */ + int inc = ( maxValue() - minValue() ) / 20; + if ( inc < 1) + inc = 1; + + //kdDebug(67100) << "KSmallslider::wheelEvent() inc=" << inc << "delta=" << e->delta() << endl; + if ( e->delta() > 0 ) { + QRangeControl::setValue( QRangeControl::value() + inc ); + } + else { + QRangeControl::setValue( QRangeControl::value() - inc ); + } + e->accept(); // Accept the event + + // Hint: Qt autmatically triggers a valueChange() when we do setValue() +} + +void KSmallSlider::mouseReleaseEvent( QMouseEvent * ) +{ + //resetState(); +} + +/* + * Moves slider to a dedicated position. If the value has changed + */ +void KSmallSlider::moveSlider( int pos ) +{ + int a = available(); + int newPos = QMIN( a, QMAX( 0, pos ) ); // keep it inside the available bounds of the slider + int newVal = valueFromPosition( newPos ); + + if ( newVal != QRangeControl::value() ) { + //QRangeControl::directSetValue( sliderVal ); + QRangeControl::setValue( newVal ); + emit valueChanged( value() ); // Only for external use + } + update(); +} + +/* +void KSmallSlider::resetState() +{ + switch ( state ) { + case Dragging: { + QRangeControl::setValue( valueFromPosition( sliderPos ) ); + emit sliderReleased(); + break; + } + case Idle: + break; + + default: + qWarning("KSmallSlider: (%s) in wrong state", name( "unnamed" ) ); + } + state = Idle; +} +*/ + +void KSmallSlider::setValue( int value ) +{ + QRangeControl::setValue( value ); +} + +void KSmallSlider::addStep() +{ + addPage(); +} + +void KSmallSlider::subtractStep() +{ + subtractPage(); +} + +int KSmallSlider::goodPart( const QPoint &p ) const +{ + if ( _orientation == Qt::Vertical ) { + return p.y() - 1; + } + else { + return p.x() - 1; + } +} + +/***************** SIZE STUFF START ***************/ +QSize KSmallSlider::sizeHint() const +{ + //constPolish(); + const int length = 25; + const int thick = 10; + + if ( _orientation == Qt::Vertical ) + return QSize( thick, length ); + else + return QSize( length, thick ); +} + + +QSize KSmallSlider::minimumSizeHint() const +{ + QSize s(10,10); + return s; +} + + +QSizePolicy KSmallSlider::sizePolicy() const +{ + + if ( _orientation == Qt::Vertical ) { + //kdDebug(67100) << "KSmallSlider::sizePolicy() vertical value=(Fixed,MinimumExpanding)\n"; + return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); + } + else { + //kdDebug(67100) << "KSmallSlider::sizePolicy() horizontal value=(MinimumExpanding,Fixed)\n"; + return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + } +} +/***************** SIZE STUFF END ***************/ + + +int KSmallSlider::minValue() const +{ + return QRangeControl::minValue(); +} + +int KSmallSlider::maxValue() const +{ + return QRangeControl::maxValue(); +} + +int KSmallSlider::lineStep() const +{ + return QRangeControl::lineStep(); +} + +int KSmallSlider::pageStep() const +{ + return QRangeControl::pageStep(); +} + +void KSmallSlider::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +void KSmallSlider::setPageStep( int i ) +{ + setSteps( lineStep(), i ); +} + +// Only for external acces. You MUST use QRangeControl::value() internally. +int KSmallSlider::value() const +{ + return QRangeControl::value(); +} + +/* +void KSmallSlider::paletteChange ( const QPalette &) { + if ( grayed ) { + setColors(mutedLowColor2, mutedHighColor2, backColor2 ); + } + else { + // ignore the QPalette and use the values from KGlobalSettings instead + //const QColorGroup& qcg = palette().active(); + setColors(KGlobalSettings::baseColor(), KGlobalSettings::highlightColor(), backColor2 ); + } +} +*/ + +void KSmallSlider::setGray( bool value ) +{ + if ( grayed!=value ) + { + grayed = value; + update(); + //repaint(); + } +} + +bool KSmallSlider::gray() const +{ + return grayed; +} + +void KSmallSlider::setColors( QColor high, QColor low, QColor back ) +{ + colHigh = high; + colLow = low; + colBack = back; + update(); + //repaint(); +} + +void KSmallSlider::setGrayColors( QColor high, QColor low, QColor back ) +{ + grayHigh = high; + grayLow = low; + grayBack = back; + update(); + //repaint(); +} + +#include "ksmallslider.moc" diff --git a/kmix/ksmallslider.h b/kmix/ksmallslider.h new file mode 100644 index 00000000..398d7728 --- /dev/null +++ b/kmix/ksmallslider.h @@ -0,0 +1,119 @@ +//-*-C++-*- +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KSMALLSLIDER_H +#define KSMALLSLIDER_H + +#include <kpanelapplet.h> + +#include <qwidget.h> +#include <qpixmap.h> +#include <qrangecontrol.h> + +class KSmallSlider : public QWidget, public QRangeControl +{ + Q_OBJECT + + public: + KSmallSlider( QWidget *parent, const char *name=0 ); + KSmallSlider( Qt::Orientation, QWidget *parent, const char *name=0 ); + KSmallSlider( int minValue, int maxValue, int pageStep, int value, + Qt::Orientation, QWidget *parent, const char *name=0 ); + + //virtual void setTracking( bool enable ); + //bool tracking() const; + QSize sizeHint() const; + QSizePolicy sizePolicy() const; + QSize minimumSizeHint() const; + + int minValue() const; + int maxValue() const; + void setMinValue( int ); // Don't use these unless you make versions + void setMaxValue( int ); // that work. -esigra + int lineStep() const; + int pageStep() const; + void setLineStep( int ); + void setPageStep( int ); + int value() const; + + //void paletteChange ( const QPalette & oldPalette ); + bool gray() const; + +public slots: + virtual void setValue( int ); + void addStep(); + void subtractStep(); + + void setGray( bool value ); + void setColors( QColor high, QColor low, QColor back ); + void setGrayColors( QColor high, QColor low, QColor back ); + + signals: + void valueChanged( int value ); + void sliderPressed(); + void sliderMoved( int value ); + void sliderReleased(); + + protected: + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void wheelEvent( QWheelEvent * ); + + void valueChange(); + void rangeChange(); + + private: + //enum State { Idle, Dragging }; + + void init(); + int positionFromValue( int ) const; + int valueFromPosition( int ) const; + void moveSlider( int ); + //void resetState(); + + // int slideLength() const; + int available() const; + int goodPart( const QPoint& ) const; + //void initTicks(); + + //QCOORD sliderPos; + //int sliderVal; + //State state; + //bool track; + bool grayed; + Qt::Orientation _orientation; + QColor colHigh, colLow, colBack; + QColor grayHigh, grayLow, grayBack; + +}; + +/* +inline bool KSmallSlider::tracking() const +{ + return track; +} +*/ +#endif diff --git a/kmix/main.cpp b/kmix/main.cpp new file mode 100644 index 00000000..3842ecaa --- /dev/null +++ b/kmix/main.cpp @@ -0,0 +1,69 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <klocale.h> +#include <kglobal.h> +#include <kstandarddirs.h> + +#include "KMixApp.h" +#include "version.h" + +static const char description[] = +I18N_NOOP("KMix - KDE's full featured mini mixer"); + +static KCmdLineOptions options[] = +{ + KCmdLineLastOption + // INSERT YOUR COMMANDLINE OPTIONS HERE +}; + +extern "C" KDE_EXPORT int kdemain(int argc, char *argv[]) +{ + KAboutData aboutData( "kmix", I18N_NOOP("KMix"), + APP_VERSION, description, KAboutData::License_GPL, + I18N_NOOP("(c) 1996-2000 Christian Esken\n(c) 2000-2003 Christian Esken, Stefan Schimanski\n(c) 2002-2005 Christian Esken, Helio Chissini de Castro")); + + aboutData.addAuthor("Christian Esken", "Current maintainer", "esken@kde.org"); + aboutData.addAuthor("Helio Chissini de Castro", I18N_NOOP("Current redesign and co-maintainer, Alsa 0.9x port"), "helio@kde.org" ); + aboutData.addAuthor("Stefan Schimanski", 0, "schimmi@kde.org"); + aboutData.addAuthor("Sven Leiber", 0, "s.leiber@web.de"); + aboutData.addAuthor("Brian Hanson", I18N_NOOP("Solaris port"), "bhanson@hotmail.com"); + aboutData.addAuthor("Paul Kendall", I18N_NOOP("SGI Port"), "paul@orion.co.nz"); + aboutData.addAuthor("Sebestyen Zoltan", I18N_NOOP("*BSD fixes"), "szoli@digo.inf.elte.hu"); + aboutData.addAuthor("Lennart Augustsson", I18N_NOOP("*BSD fixes"), "augustss@cs.chalmers.se"); + aboutData.addAuthor("Nick Lopez", I18N_NOOP("ALSA port"), "kimo_sabe@usa.net"); + aboutData.addAuthor("Helge Deller", I18N_NOOP("HP/UX port"), "deller@gmx.de"); + aboutData.addAuthor("Jean Labrousse", I18N_NOOP("NAS port"), "jean.labrousse@alcatel.com" ); + aboutData.addCredit("Nadeem Hasan", I18N_NOOP("Mute and volume preview, other fixes"), "nhasan@kde.org"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + if (!KMixApp::start()) + return 0; + + KMixApp *app = new KMixApp(); + int ret = app->exec(); + delete app; + return ret; +} diff --git a/kmix/mdwenum.cpp b/kmix/mdwenum.cpp new file mode 100644 index 00000000..91220902 --- /dev/null +++ b/kmix/mdwenum.cpp @@ -0,0 +1,206 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qcursor.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qobject.h> +#include <qtooltip.h> + +#include <klocale.h> +#include <kconfig.h> +#include <kcombobox.h> +#include <kaction.h> +#include <kpopupmenu.h> + +#include <kglobalaccel.h> +#include <kkeydialog.h> + +#include <kdebug.h> + +#include "mdwenum.h" +#include "mixer.h" +#include "viewbase.h" + +/** + * Class that represents an Enum element (a select one-from-many selector) + * The orientation (horizontal, vertical) is ignored + */ +MDWEnum::MDWEnum(Mixer *mixer, MixDevice* md, + Qt::Orientation orientation, + QWidget* parent, ViewBase* mw, const char* name) : + MixDeviceWidget(mixer,md,false,orientation,parent,mw,name), + _label(0), _enumCombo(0), _layout(0) +{ + // create actions (on _mdwActions, see MixDeviceWidget) + + // KStdAction::showMenubar() is in MixDeviceWidget now + new KToggleAction( i18n("&Hide"), 0, this, SLOT(setDisabled()), _mdwActions, "hide" ); + new KAction( i18n("C&onfigure Shortcuts..."), 0, this, SLOT(defineKeys()), _mdwActions, "keys" ); + + // create widgets + createWidgets(); + + /* !!! remove this for production version */ + m_keys->insert( "Next Value", i18n( "Next Value" ), QString::null, + KShortcut(), KShortcut(), this, SLOT( nextEnumId() ) ); + + installEventFilter( this ); // filter for popup +} + +MDWEnum::~MDWEnum() +{ +} + + +void MDWEnum::createWidgets() +{ + if ( _orientation == Qt::Vertical ) { + _layout = new QVBoxLayout( this ); + _layout->setAlignment(Qt::AlignHCenter); + } + else { + _layout = new QHBoxLayout( this ); + _layout->setAlignment(Qt::AlignVCenter); + } + QToolTip::add( this, m_mixdevice->name() ); + + //this->setStretchFactor( _layout, 0 ); + //QSizePolicy qsp( QSizePolicy::Ignored, QSizePolicy::Maximum); + //_layout->setSizePolicy(qsp); + //_layout->setSpacing(KDialog::spacingHint()); + _label = new QLabel( m_mixdevice->name(), this); + _layout->addWidget(_label); + _label->setFixedHeight(_label->sizeHint().height()); + _enumCombo = new KComboBox( FALSE, this, "mixerCombo" ); + // ------------ fill ComboBox start ------------ + int maxEnumId= m_mixdevice->enumValues().count(); + for (int i=0; i<maxEnumId; i++ ) { + _enumCombo->insertItem( *(m_mixdevice->enumValues().at(i)),i); + } + // ------------ fill ComboBox end -------------- + _layout->addWidget(_enumCombo); + _enumCombo->setFixedHeight(_enumCombo->sizeHint().height()); + connect( _enumCombo, SIGNAL( activated( int ) ), this, SLOT( setEnumId( int ) ) ); + QToolTip::add( _enumCombo, m_mixdevice->name() ); + + //_layout->addSpacing( 4 ); +} + +void MDWEnum::update() +{ + if ( m_mixdevice->isEnum() ) { + //kdDebug(67100) << "MDWEnum::update() enumID=" << m_mixdevice->enumId() << endl; + _enumCombo->setCurrentItem( m_mixdevice->enumId() ); + } + else { + // !!! print warning message + } +} + +void MDWEnum::showContextMenu() +{ + if( m_mixerwidget == NULL ) + return; + + KPopupMenu *menu = m_mixerwidget->getPopup(); + + QPoint pos = QCursor::pos(); + menu->popup( pos ); +} + +QSize MDWEnum::sizeHint() const { + if ( _layout != 0 ) { + return _layout->sizeHint(); + } + else { + // layout not (yet) created + return QWidget::sizeHint(); + } +} + + +/** + This slot is called, when a user has clicked the mute button. Also it is called by any other + associated KAction like the context menu. +*/ +void MDWEnum::nextEnumId() { + if( m_mixdevice->isEnum() ) { + int curEnum = enumId(); + if ( (unsigned int)curEnum < m_mixdevice->enumValues().count() ) { + // next enum value + setEnumId(curEnum+1); + } + else { + // wrap around + setEnumId(0); + } + } // isEnum +} + +void MDWEnum::setEnumId(int value) +{ + if ( m_mixdevice->isEnum() ) { + m_mixdevice->setEnumId( value ); + m_mixer->commitVolumeChange( m_mixdevice ); + } +} + +int MDWEnum::enumId() +{ + if ( m_mixdevice->isEnum() ) { + return m_mixdevice->enumId(); + } + else { + return 0; + } +} + +void MDWEnum::setDisabled() +{ + setDisabled( true ); +} + +void MDWEnum::setDisabled( bool value ) { + if ( m_disabled!=value) + { + value ? hide() : show(); + m_disabled = value; + } +} + +/** + * An event filter for the various QWidgets. We watch for Mouse press Events, so + * that we can popup the context menu. + */ +bool MDWEnum::eventFilter( QObject* obj, QEvent* e ) +{ + if (e->type() == QEvent::MouseButtonPress) { + QMouseEvent *qme = static_cast<QMouseEvent*>(e); + if (qme->button() == Qt::RightButton) { + showContextMenu(); + return true; + } + } + return QWidget::eventFilter(obj,e); +} + +#include "mdwenum.moc" diff --git a/kmix/mdwenum.h b/kmix/mdwenum.h new file mode 100644 index 00000000..09b2d0a4 --- /dev/null +++ b/kmix/mdwenum.h @@ -0,0 +1,77 @@ +//-*-C++-*- +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2004 Chrisitan Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MDWENUM_H +#define MDWENUM_H + +#include <qwidget.h> +#include "volume.h" + +class QBoxLayout; + +class KAction; +class KActionCollection; +class KComboBox; +class KGlobalAccel; + +class MixDevice; +class Mixer; +class ViewBase; + +#include "mixdevicewidget.h" + +class MDWEnum : public MixDeviceWidget +{ + Q_OBJECT + +public: + MDWEnum( Mixer *mixer, MixDevice* md, + Qt::Orientation orientation, + QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0); + ~MDWEnum(); + + void addActionToPopup( KAction *action ); + QSize sizeHint() const; + bool eventFilter( QObject* obj, QEvent* e ); + +public slots: + // GUI hide and show + void setDisabled(); + void setDisabled(bool); + + // Enum handling: next and selecting + void nextEnumId(); + int enumId(); + void setEnumId(int value); + + void update(); + virtual void showContextMenu(); + +private: + void createWidgets(); + + QLabel *_label; + KComboBox *_enumCombo; + QBoxLayout *_layout; +}; + +#endif diff --git a/kmix/mdwslider.cpp b/kmix/mdwslider.cpp new file mode 100644 index 00000000..6ee8166c --- /dev/null +++ b/kmix/mdwslider.cpp @@ -0,0 +1,974 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <klocale.h> +#include <kled.h> +#include <kiconloader.h> +#include <kconfig.h> +#include <kaction.h> +#include <kpopupmenu.h> +#include <kglobalaccel.h> +#include <kkeydialog.h> +#include <kdebug.h> + +#include <qobject.h> +#include <qcursor.h> +#include <qslider.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpixmap.h> +#include <qtooltip.h> +#include <qwmatrix.h> + +#include "mdwslider.h" +#include "mixer.h" +#include "viewbase.h" +#include "kledbutton.h" +#include "ksmallslider.h" +#include "verticaltext.h" + +/** + * MixDeviceWidget that represents a single mix device, inlcuding PopUp, muteLED, ... + * + * Used in KMix main window and DockWidget and PanelApplet. + * It can be configured to include or exclude the recordLED and the muteLED. + * The direction (horizontal, vertical) can be configured and whether it should + * be "small" (uses KSmallSlider instead of QSlider then). + * + * Due to the many options, this is the most complicated MixDeviceWidget subclass. + */ +MDWSlider::MDWSlider(Mixer *mixer, MixDevice* md, + bool showMuteLED, bool showRecordLED, + bool small, Qt::Orientation orientation, + QWidget* parent, ViewBase* mw, const char* name) : + MixDeviceWidget(mixer,md,small,orientation,parent,mw,name), + m_linked(true), m_valueStyle( NNONE), m_iconLabel( 0 ), m_muteLED( 0 ), m_recordLED( 0 ), m_label( 0 ), _layout(0) +{ + // create actions (on _mdwActions, see MixDeviceWidget) + + new KToggleAction( i18n("&Split Channels"), 0, this, SLOT(toggleStereoLinked()), + _mdwActions, "stereo" ); + new KToggleAction( i18n("&Hide"), 0, this, SLOT(setDisabled()), _mdwActions, "hide" ); + + KToggleAction *a = new KToggleAction(i18n("&Muted"), 0, 0, 0, _mdwActions, "mute" ); + connect( a, SIGNAL(toggled(bool)), SLOT(toggleMuted()) ); + + if( m_mixdevice->isRecordable() ) { + a = new KToggleAction( i18n("Set &Record Source"), 0, 0, 0, _mdwActions, "recsrc" ); + connect( a, SIGNAL(toggled(bool)), SLOT( toggleRecsrc()) ); + } + + new KAction( i18n("C&onfigure Global Shortcuts..."), 0, this, SLOT(defineKeys()), _mdwActions, "keys" ); + + // create widgets + createWidgets( showMuteLED, showRecordLED ); + + m_keys->insert( "Increase volume", i18n( "Increase Volume of '%1'" ).arg(m_mixdevice->name().utf8().data()), QString::null, + KShortcut(), KShortcut(), this, SLOT( increaseVolume() ) ); + m_keys->insert( "Decrease volume", i18n( "Decrease Volume of '%1'" ).arg(m_mixdevice->name().utf8().data()), QString::null, + KShortcut(), KShortcut(), this, SLOT( decreaseVolume() ) ); + m_keys->insert( "Toggle mute", i18n( "Toggle Mute of '%1'" ).arg(m_mixdevice->name().utf8().data()), QString::null, + KShortcut(), KShortcut(), this, SLOT( toggleMuted() ) ); + + installEventFilter( this ); // filter for popup + + update(); +} + + +QSizePolicy MDWSlider::sizePolicy() const +{ + if ( _orientation == Qt::Vertical ) { + return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); + } + else { + return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + } +} + + +/** + * Creates up to 4 widgets - Icon, Mute-Button, Slider and Record-Button. + * + * Those widgets are placed into + +*/ +void MDWSlider::createWidgets( bool showMuteLED, bool showRecordLED ) +{ + if ( _orientation == Qt::Vertical ) { + _layout = new QVBoxLayout( this ); + _layout->setAlignment(Qt::AlignCenter); + } + else { + _layout = new QHBoxLayout( this ); + _layout->setAlignment(Qt::AlignCenter); + } + + // -- MAIN SLIDERS LAYOUT --- + QBoxLayout *slidersLayout; + if ( _orientation == Qt::Vertical ) { + slidersLayout = new QHBoxLayout( _layout ); + slidersLayout->setAlignment(Qt::AlignVCenter); + } + else { + slidersLayout = new QVBoxLayout( _layout ); + slidersLayout->setAlignment(Qt::AlignHCenter); + } + + /* cesken: This is inconsistent. Why should vertical and horizontal layout differ? + * Also it eats too much space - especially when you don't show sliders at all. + * Even more on the vertical panel applet (see Bug #97667) + if ( _orientation == Qt::Horizontal ) + slidersLayout->addSpacing( 10 ); + */ + + + // -- LABEL LAYOUT TO POSITION + QBoxLayout *labelLayout; + if ( _orientation == Qt::Vertical ) { + labelLayout = new QVBoxLayout( slidersLayout ); + labelLayout->setAlignment(Qt::AlignHCenter); + } + else { + labelLayout = new QHBoxLayout( slidersLayout ); + labelLayout->setAlignment(Qt::AlignVCenter); + } + if ( _orientation == Qt::Vertical ) { + m_label = new VerticalText( this, m_mixdevice->name().utf8().data() ); + QToolTip::add( m_label, m_mixdevice->name() ); + + } + else { + m_label = new QLabel(this); + static_cast<QLabel*>(m_label) ->setText(m_mixdevice->name()); + QToolTip::add( m_label, m_mixdevice->name() ); + } + + m_label->hide(); + +/* This addSpacing() looks VERY bizarre => removing it (cesken, 21.2.2006). + Also horizontal and vertical spacing differs. This doesn't look sensible. + if ( _orientation == Qt::Horizontal ) + labelLayout->addSpacing( 36 ); +*/ + labelLayout->addWidget( m_label ); + m_label->installEventFilter( this ); + +/* This addSpacing() looks VERY bizarre => removing it (cesken, 21.2.2006) + Also horizontal and vertical spacing differs. This doesn't look sensible. + if ( _orientation == Qt::Vertical ) { + labelLayout->addSpacing( 18 ); + } +*/ + + // -- SLIDERS, LEDS AND ICON + QBoxLayout *sliLayout; + if ( _orientation == Qt::Vertical ) { + sliLayout = new QVBoxLayout( slidersLayout ); + sliLayout->setAlignment(Qt::AlignHCenter); + } + else { + sliLayout = new QHBoxLayout( slidersLayout ); + sliLayout->setAlignment(Qt::AlignVCenter); + } + + // --- ICON ---------------------------- + QBoxLayout *iconLayout; + if ( _orientation == Qt::Vertical ) { + iconLayout = new QHBoxLayout( sliLayout ); + iconLayout->setAlignment(Qt::AlignVCenter); + } + else { + iconLayout = new QVBoxLayout( sliLayout ); + iconLayout->setAlignment(Qt::AlignHCenter); + } + + m_iconLabel = 0L; + setIcon( m_mixdevice->type() ); + iconLayout->addStretch(); + iconLayout->addWidget( m_iconLabel ); + iconLayout->addStretch(); + m_iconLabel->installEventFilter( this ); + + sliLayout->addSpacing( 5 ); + + + // --- MUTE LED + if ( showMuteLED ) { + QBoxLayout *ledlayout; + if ( _orientation == Qt::Vertical ) { + ledlayout = new QHBoxLayout( sliLayout ); + ledlayout->setAlignment(Qt::AlignVCenter); + } + else { + ledlayout = new QVBoxLayout( sliLayout ); + ledlayout->setAlignment(Qt::AlignHCenter); + } + + if( m_mixdevice->hasMute() ) + { + ledlayout->addStretch(); + // create mute LED + m_muteLED = new KLedButton( Qt::green, KLed::On, KLed::Sunken, + KLed::Circular, this, "MuteLED" ); + m_muteLED->setFixedSize( QSize(16, 16) ); + m_muteLED->resize( QSize(16, 16) ); + ledlayout->addWidget( m_muteLED ); + QToolTip::add( m_muteLED, i18n( "Mute" ) ); + connect( m_muteLED, SIGNAL(stateChanged(bool)), this, SLOT(toggleMuted()) ); + m_muteLED->installEventFilter( this ); + ledlayout->addStretch(); + } // has Mute LED + else { + // we don't have a MUTE LED. We create a dummy widget + // !! possibly not neccesary any more (we are layouted) + QWidget *qw = new QWidget(this, "Spacer"); + qw->setFixedSize( QSize(16, 16) ); + ledlayout->addWidget(qw); + qw->installEventFilter( this ); + } // has no Mute LED + + sliLayout->addSpacing( 3 ); + } // showMuteLED + + // --- SLIDERS --------------------------- + QBoxLayout *volLayout; + if ( _orientation == Qt::Vertical ) { + volLayout = new QHBoxLayout( sliLayout ); + volLayout->setAlignment(Qt::AlignVCenter); + } + else { + volLayout = new QVBoxLayout( sliLayout ); + volLayout->setAlignment(Qt::AlignHCenter); + } + + // Sliders and volume number indication + QBoxLayout *slinumLayout; + for( int i = 0; i < m_mixdevice->getVolume().count(); i++ ) + { + Volume::ChannelID chid = Volume::ChannelID(i); + // @todo !! Normally the mixdevicewidget SHOULD know, which slider represents which channel. + // We should look up the mapping here, but for now, we simply assume "chid == i". + + int maxvol = m_mixdevice->getVolume().maxVolume(); + int minvol = m_mixdevice->getVolume().minVolume(); + + if ( _orientation == Qt::Vertical ) { + slinumLayout = new QVBoxLayout( volLayout ); + slinumLayout->setAlignment(Qt::AlignHCenter); + } + else { + slinumLayout = new QHBoxLayout( volLayout ); + slinumLayout->setAlignment(Qt::AlignVCenter); + } + + // create labels to hold volume values (taken from qamix/kamix) + QLabel *number = new QLabel( "100", this ); + slinumLayout->addWidget( number ); + number->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + number->setLineWidth( 2 ); + number->setMinimumWidth( number->sizeHint().width() ); + number->setPaletteBackgroundColor( QColor(190, 250, 190) ); + // don't show the value by default + number->hide(); + updateValue( number, chid ); + _numbers.append( number ); + + QWidget* slider; + if ( m_small ) { + slider = new KSmallSlider( minvol, maxvol, maxvol/10, + m_mixdevice->getVolume( chid ), _orientation, + this, m_mixdevice->name().ascii() ); + } + else { + slider = new QSlider( 0, maxvol, maxvol/10, + maxvol - m_mixdevice->getVolume( chid ), _orientation, + this, m_mixdevice->name().ascii() ); + slider->setMinimumSize( slider->sizeHint() ); + } + + slider->setBackgroundOrigin(AncestorOrigin); + slider->installEventFilter( this ); + QToolTip::add( slider, m_mixdevice->name() ); + + if( i>0 && isStereoLinked() ) { + // show only one (the first) slider, when the user wants it so + slider->hide(); + number->hide(); + } + slinumLayout->addWidget( slider ); // add to layout + m_sliders.append ( slider ); // add to list + _slidersChids.append(chid); // Remember slider-chid association + connect( slider, SIGNAL(valueChanged(int)), SLOT(volumeChange(int)) ); + } // for all channels of this device + + + // --- RECORD SOURCE LED -------------------------- + if ( showRecordLED ) + { + sliLayout->addSpacing( 5 ); + + // --- LED LAYOUT TO CENTER --- + QBoxLayout *reclayout; + if ( _orientation == Qt::Vertical ) { + reclayout = new QHBoxLayout( sliLayout ); + reclayout->setAlignment(Qt::AlignVCenter); + } + else { + reclayout = new QVBoxLayout( sliLayout ); + reclayout->setAlignment(Qt::AlignHCenter); + } + + if( m_mixdevice->isRecordable() ) { + reclayout->addStretch(); + m_recordLED = new KLedButton( Qt::red, + m_mixdevice->isRecSource()?KLed::On:KLed::Off, + KLed::Sunken, KLed::Circular, this, "RecordLED" ); + m_recordLED->setFixedSize( QSize(16, 16) ); + reclayout->addWidget( m_recordLED ); + connect(m_recordLED, SIGNAL(stateChanged(bool)), this, SLOT(setRecsrc(bool))); + m_recordLED->installEventFilter( this ); + QToolTip::add( m_recordLED, i18n( "Record" ) ); + reclayout->addStretch(); + } + else + { + // we don't have a RECORD LED. We create a dummy widget + // !! possibly not neccesary any more (we are layouted) + QWidget *qw = new QWidget(this, "Spacer"); + qw->setFixedSize( QSize(16, 16) ); + reclayout->addWidget(qw); + qw->installEventFilter( this ); + } // has no Record LED + } // showRecordLED + + layout()->activate(); +} + + +QPixmap +MDWSlider::icon( int icontype ) +{ + QPixmap miniDevPM; + switch (icontype) { + case MixDevice::AUDIO: + miniDevPM = UserIcon("mix_audio"); break; + case MixDevice::BASS: + case MixDevice::SURROUND_LFE: // "LFE" SHOULD have an own icon + miniDevPM = UserIcon("mix_bass"); break; + case MixDevice::CD: + miniDevPM = UserIcon("mix_cd"); break; + case MixDevice::EXTERNAL: + miniDevPM = UserIcon("mix_ext"); break; + case MixDevice::MICROPHONE: + miniDevPM = UserIcon("mix_microphone");break; + case MixDevice::MIDI: + miniDevPM = UserIcon("mix_midi"); break; + case MixDevice::RECMONITOR: + miniDevPM = UserIcon("mix_recmon"); break; + case MixDevice::TREBLE: + miniDevPM = UserIcon("mix_treble"); break; + case MixDevice::UNKNOWN: + miniDevPM = UserIcon("mix_unknown"); break; + case MixDevice::VOLUME: + miniDevPM = UserIcon("mix_volume"); break; + case MixDevice::VIDEO: + miniDevPM = UserIcon("mix_video"); break; + case MixDevice::SURROUND: + case MixDevice::SURROUND_BACK: + case MixDevice::SURROUND_CENTERFRONT: + case MixDevice::SURROUND_CENTERBACK: + miniDevPM = UserIcon("mix_surround"); break; + case MixDevice::HEADPHONE: + miniDevPM = UserIcon( "mix_headphone" ); break; + case MixDevice::DIGITAL: + miniDevPM = UserIcon( "mix_digital" ); break; + case MixDevice::AC97: + miniDevPM = UserIcon( "mix_ac97" ); break; + default: + miniDevPM = UserIcon("mix_unknown"); break; + } + + return miniDevPM; +} + +void +MDWSlider::setIcon( int icontype ) +{ + if( !m_iconLabel ) + { + m_iconLabel = new QLabel(this); + m_iconLabel->setBackgroundOrigin(AncestorOrigin); + installEventFilter( m_iconLabel ); + } + + QPixmap miniDevPM = icon( icontype ); + if ( !miniDevPM.isNull() ) + { + if ( m_small ) + { + // scale icon + QWMatrix t; + t = t.scale( 10.0/miniDevPM.width(), 10.0/miniDevPM.height() ); + m_iconLabel->setPixmap( miniDevPM.xForm( t ) ); + m_iconLabel->resize( 10, 10 ); + } else + m_iconLabel->setPixmap( miniDevPM ); + m_iconLabel->setAlignment( Qt::AlignCenter ); + } else + { + kdError(67100) << "Pixmap missing." << endl; + } + + layout()->activate(); +} + +bool +MDWSlider::isLabeled() const +{ + if ( m_label == 0 ) + return false; + else + return !m_label->isHidden(); +} + +void +MDWSlider::toggleStereoLinked() +{ + setStereoLinked( !isStereoLinked() ); +} + +void +MDWSlider::setStereoLinked(bool value) +{ + m_linked = value; + + QWidget *slider = m_sliders.first(); + QLabel *number = _numbers.first(); + QString qs = number->text(); + + /*********************************************************** + Remember value of first slider, so that it can be copied + to the other sliders. + ***********************************************************/ + int firstSliderValue = 0; + bool firstSliderValueValid = false; + if (slider->isA("QSlider") ) { + QSlider *sld = static_cast<QSlider*>(slider); + firstSliderValue = sld->value(); + firstSliderValueValid = true; + } + else if ( slider->isA("KSmallSlider") ) { + KSmallSlider *sld = static_cast<KSmallSlider*>(slider); + firstSliderValue = sld->value(); + firstSliderValueValid = true; + } + + for( slider=m_sliders.next(), number=_numbers.next(); slider!=0 && number!=0; slider=m_sliders.next(), number=_numbers.next() ) { + if ( m_linked ) { + slider->hide(); + number->hide(); + } + else { + // When splitting, make the next sliders show the same value as the first. + // This might not be entirely true, but better than showing the random value + // that was used to be shown before hot-fixing this. !! must be revised + if ( firstSliderValueValid ) { + // Remark: firstSlider== 0 could happen, if the static_cast<QRangeControl*> above fails. + // It's a safety measure, if we got other Slider types in the future. + if (slider->isA("QSlider") ) { + QSlider *sld = static_cast<QSlider*>(slider); + sld->setValue( firstSliderValue ); + } + if (slider->isA("KSmallSlider") ) { + KSmallSlider *sld = static_cast<KSmallSlider*>(slider); + sld->setValue( firstSliderValue ); + } + } + slider->show(); + number->setText(qs); + if (m_valueStyle != NNONE) + number->show(); + } + } + + slider = m_sliders.last(); + if( slider && static_cast<QSlider *>(slider)->tickmarks() ) + setTicks( true ); + + layout()->activate(); +} + + +void +MDWSlider::setLabeled(bool value) +{ + if ( m_label == 0 ) + return; + + if (value ) + m_label->show(); + else + m_label->hide(); + + layout()->activate(); +} + +void +MDWSlider::setTicks( bool ticks ) +{ + QWidget* slider; + + slider = m_sliders.first(); + + if ( slider->inherits( "QSlider" ) ) + { + if( ticks ) + if( isStereoLinked() ) + static_cast<QSlider *>(slider)->setTickmarks( QSlider::Right ); + else + { + static_cast<QSlider *>(slider)->setTickmarks( QSlider::NoMarks ); + slider = m_sliders.last(); + static_cast<QSlider *>(slider)->setTickmarks( QSlider::Left ); + } + else + { + static_cast<QSlider *>(slider)->setTickmarks( QSlider::NoMarks ); + slider = m_sliders.last(); + static_cast<QSlider *>(slider)->setTickmarks( QSlider::NoMarks ); + } + } + + layout()->activate(); +} + +void +MDWSlider::setValueStyle( ValueStyle valueStyle ) +{ + m_valueStyle = valueStyle; + + int i = 0; + QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin(); + for(QLabel *number = _numbers.first(); number!=0; number = _numbers.next(), ++i, ++it) { + Volume::ChannelID chid = *it; + switch ( m_valueStyle ) { + case NNONE: number->hide(); break; + default: + if ( !isStereoLinked() || (i == 0)) { + updateValue( number, chid ); + number->show(); + } + } + } + layout()->activate(); +} + +void +MDWSlider::setIcons(bool value) +{ + if ( m_iconLabel != 0 ) { + if ( ( !m_iconLabel->isHidden()) !=value ) { + if (value) + m_iconLabel->show(); + else + m_iconLabel->hide(); + + layout()->activate(); + } + } // if it has an icon +} + +void +MDWSlider::setColors( QColor high, QColor low, QColor back ) +{ + for( QWidget *slider=m_sliders.first(); slider!=0; slider=m_sliders.next() ) { + KSmallSlider *smallSlider = dynamic_cast<KSmallSlider*>(slider); + if ( smallSlider ) smallSlider->setColors( high, low, back ); + } +} + +void +MDWSlider::setMutedColors( QColor high, QColor low, QColor back ) +{ + for( QWidget *slider=m_sliders.first(); slider!=0; slider=m_sliders.next() ) { + KSmallSlider *smallSlider = dynamic_cast<KSmallSlider*>(slider); + if ( smallSlider ) smallSlider->setGrayColors( high, low, back ); + } +} + +void +MDWSlider::updateValue( QLabel *value, Volume::ChannelID chid ) { + QString qs; + Volume& vol = m_mixdevice->getVolume(); + + if (m_valueStyle == NABSOLUTE ) + qs.sprintf("%3d", (int) vol.getVolume( chid ) ); + else + qs.sprintf("%3d", (int)( vol.getVolume( chid ) / (double)vol.maxVolume() * 100 ) ); + value->setText(qs); +} + + +/** This slot is called, when a user has changed the volume via the KMix Slider */ +void MDWSlider::volumeChange( int ) +{ + // --- Step 1: Get a REFERENCE of the volume Object --- + Volume& vol = m_mixdevice->getVolume(); + + // --- Step 2: Change the volumes directly in the Volume object to reflect the Sliders --- + if ( isStereoLinked() ) + { + QWidget *slider = m_sliders.first(); + Volume::ChannelID chid = _slidersChids.first(); + + int sliderValue = 0; + if ( slider->inherits( "KSmallSlider" ) ) + { + KSmallSlider *slider = dynamic_cast<KSmallSlider *>(m_sliders.first()); + if (slider) { + sliderValue= slider->value(); + } + } + else { + QSlider *slider = dynamic_cast<QSlider *>(m_sliders.first()); + if (slider) { + if ( _orientation == Qt::Vertical ) + sliderValue= slider->maxValue() - slider->value(); + else + sliderValue= slider->value(); + + } + } + + // With balance proper working, we must change relative volumes, + // not absolute, which leads a make some difference calc related + // to new sliders position against the top volume on channels + long volumeDif = sliderValue - vol.getTopStereoVolume( Volume::MMAIN ); + + if ( chid == Volume::LEFT ) { + vol.setVolume( Volume::LEFT , vol.getVolume( Volume::LEFT ) + volumeDif ); + vol.setVolume( Volume::RIGHT, vol.getVolume( Volume::RIGHT ) + volumeDif ); + } + else { + kdDebug(67100) << "MDWSlider::volumeChange(), unknown chid " << chid << endl; + } + + updateValue( _numbers.first(), Volume::LEFT ); + } // joined + else { + int n = 0; + QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin(); + QLabel *number = _numbers.first(); + for( QWidget *slider=m_sliders.first(); slider!=0 && number!=0; slider=m_sliders.next(), number=_numbers.next(), ++it ) + { + Volume::ChannelID chid = *it; + if ( slider->inherits( "KSmallSlider" ) ) + { + KSmallSlider *smallSlider = dynamic_cast<KSmallSlider *>(slider); + if (smallSlider) + vol.setVolume( chid, smallSlider->value() ); + } + else + { + QSlider *bigSlider = dynamic_cast<QSlider *>(slider); + if (bigSlider) + if ( _orientation == Qt::Vertical ) + vol.setVolume( chid, bigSlider->maxValue() - bigSlider->value() ); + else + vol.setVolume( chid, bigSlider->value() ); + } + updateValue( number, chid ); + n++; + } + } + + // --- Step 3: Write back the new volumes to the HW --- + m_mixer->commitVolumeChange(m_mixdevice); +} + + +/** + This slot is called, when a user has clicked the recsrc button. Also it is called by any other + associated KAction like the context menu. +*/ +void MDWSlider::toggleRecsrc() { + setRecsrc( !m_mixdevice->isRecSource() ); +} + + +void MDWSlider::setRecsrc(bool value ) +{ + if ( m_mixdevice->isRecordable() ) { + m_mixer->setRecordSource( m_mixdevice->num(), value ); + } +} + + +/** + This slot is called, when a user has clicked the mute button. Also it is called by any other + associated KAction like the context menu. +*/ +void MDWSlider::toggleMuted() { + setMuted( !m_mixdevice->isMuted() ); +} + +void MDWSlider::setMuted(bool value) +{ + if ( m_mixdevice->hasMute() ) { + m_mixdevice->setMuted( value ); + m_mixer->commitVolumeChange(m_mixdevice); + } +} + + +void MDWSlider::setDisabled() +{ + setDisabled( true ); +} + +void MDWSlider::setDisabled( bool value ) +{ + if ( m_disabled!=value) { + value ? hide() : show(); + m_disabled = value; + } +} + + +/** + This slot is called on a MouseWheel event. Also it is called by any other + associated KAction like the context menu. +*/ +void MDWSlider::increaseVolume() +{ + Volume vol = m_mixdevice->getVolume(); + long inc = vol.maxVolume() / 20; + if ( inc == 0 ) + inc = 1; + for ( int i = 0; i < vol.count(); i++ ) { + long newVal = (vol[i]) + inc; + m_mixdevice->setVolume( i, newVal < vol.maxVolume() ? newVal : vol.maxVolume() ); + } + m_mixer->commitVolumeChange(m_mixdevice); +} + +/** + This slot is called on a MouseWheel event. Also it is called by any other + associated KAction like the context menu. +*/ +void MDWSlider::decreaseVolume() +{ + Volume vol = m_mixdevice->getVolume(); + long inc = vol.maxVolume() / 20; + if ( inc == 0 ) + inc = 1; + for ( int i = 0; i < vol.count(); i++ ) { + long newVal = (vol[i]) - inc; + m_mixdevice->setVolume( i, newVal > 0 ? newVal : 0 ); + } + m_mixer->commitVolumeChange(m_mixdevice); +} + + +/** + This is called whenever there are volume updates pending from the hardware for this MDW. + At the moment it is called regulary via a QTimer (implicitely). +*/ +void MDWSlider::update() +{ + // update volumes + Volume vol = m_mixdevice->getVolume(); + if( isStereoLinked() ) + { + QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin(); + + long avgVol = vol.getAvgVolume( Volume::MMAIN ); + + QWidget *slider = m_sliders.first(); + if ( slider == 0 ) { + return; // !!! Development version, check this !!! + } + slider->blockSignals( true ); + if ( slider->inherits( "KSmallSlider" ) ) + { + KSmallSlider *smallSlider = dynamic_cast<KSmallSlider *>(slider); + if (smallSlider) { + smallSlider->setValue( avgVol ); // !! inverted ?!? + smallSlider->setGray( m_mixdevice->isMuted() ); + } + } // small slider + else { + QSlider *bigSlider = dynamic_cast<QSlider *>(slider); + if (bigSlider) + { + // In case of stereo linked and single slider, slider must + // show the top of both volumes, and not strangely low down + // the main volume by half + + if ( _orientation == Qt::Vertical ) + bigSlider->setValue( vol.maxVolume() - vol.getTopStereoVolume( Volume::MMAIN ) ); + else + bigSlider->setValue( vol.getTopStereoVolume( Volume::MMAIN ) ); + } + } // big slider + + updateValue( _numbers.first(), Volume::LEFT ); + slider->blockSignals( false ); + } // only 1 slider (stereo-linked) + else { + QValueList<Volume::ChannelID>::Iterator it = _slidersChids.begin(); + for( int i=0; i<vol.count(); i++, ++it ) { + QWidget *slider = m_sliders.at( i ); + Volume::ChannelID chid = *it; + if (slider == 0) { + // !!! not implemented !!! + // not implemented: happens if there are record and playback + // sliders in the same device. Or if you only show + // the right slider (or any other fancy occasion) + continue; + } + slider->blockSignals( true ); + + if ( slider->inherits( "KSmallSlider" ) ) + { + KSmallSlider *smallSlider = dynamic_cast<KSmallSlider *>(slider); + if (smallSlider) { + smallSlider->setValue( vol[chid] ); + smallSlider->setGray( m_mixdevice->isMuted() ); + } + } + else + { + QSlider *bigSlider = dynamic_cast<QSlider *>(slider); + if (bigSlider) + if ( _orientation == Qt::Vertical ) { + bigSlider->setValue( vol.maxVolume() - vol[i] ); + } + else { + bigSlider->setValue( vol[i] ); + } + } + + updateValue( _numbers.at ( i ), chid ); + + slider->blockSignals( false ); + } // for all sliders + } // more than 1 slider + + // update mute led + if ( m_muteLED ) { + m_muteLED->blockSignals( true ); + m_muteLED->setState( m_mixdevice->isMuted() ? KLed::Off : KLed::On ); + m_muteLED->blockSignals( false ); + } + + // update recsrc + if( m_recordLED ) { + m_recordLED->blockSignals( true ); + m_recordLED->setState( m_mixdevice->isRecSource() ? KLed::On : KLed::Off ); + m_recordLED->blockSignals( false ); + } +} + +void MDWSlider::showContextMenu() +{ + if( m_mixerwidget == NULL ) + return; + + KPopupMenu *menu = m_mixerwidget->getPopup(); + menu->insertTitle( SmallIcon( "kmix" ), m_mixdevice->name() ); + + if ( m_sliders.count()>1 ) { + KToggleAction *stereo = (KToggleAction *)_mdwActions->action( "stereo" ); + if ( stereo ) { + stereo->setChecked( !isStereoLinked() ); + stereo->plug( menu ); + } + } + + KToggleAction *ta = (KToggleAction *)_mdwActions->action( "recsrc" ); + if ( ta ) { + ta->setChecked( m_mixdevice->isRecSource() ); + ta->plug( menu ); + } + + if ( m_mixdevice->hasMute() ) { + ta = ( KToggleAction* )_mdwActions->action( "mute" ); + if ( ta ) { + ta->setChecked( m_mixdevice->isMuted() ); + ta->plug( menu ); + } + } + + KAction *a = _mdwActions->action( "hide" ); + if ( a ) + a->plug( menu ); + + a = _mdwActions->action( "keys" ); + if ( a && m_keys ) { + KActionSeparator sep( this ); + sep.plug( menu ); + a->plug( menu ); + } + + QPoint pos = QCursor::pos(); + menu->popup( pos ); +} + +QSize MDWSlider::sizeHint() const { + if ( _layout != 0 ) { + return _layout->sizeHint(); + } + else { + // layout not (yet) created + return QWidget::sizeHint(); + } +} + +/** + * An event filter for the various QWidgets. We watch for Mouse press Events, so + * that we can popup the context menu. + */ +bool MDWSlider::eventFilter( QObject* obj, QEvent* e ) +{ + if (e->type() == QEvent::MouseButtonPress) { + QMouseEvent *qme = static_cast<QMouseEvent*>(e); + if (qme->button() == Qt::RightButton) { + showContextMenu(); + return true; + } + } + // Attention: We don't filter WheelEvents for KSmallSlider, because it handles WheelEvents itself + else if ( (e->type() == QEvent::Wheel) && !obj->isA("KSmallSlider") ) { + QWheelEvent *qwe = static_cast<QWheelEvent*>(e); + if (qwe->delta() > 0) { + increaseVolume(); + } + else { + decreaseVolume(); + } + return true; + } + return QWidget::eventFilter(obj,e); +} + +#include "mdwslider.moc" diff --git a/kmix/mdwslider.h b/kmix/mdwslider.h new file mode 100644 index 00000000..2083c9e4 --- /dev/null +++ b/kmix/mdwslider.h @@ -0,0 +1,128 @@ +//-*-C++-*- +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2004 Chrisitan Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MDWSLIDER_H +#define MDWSLIDER_H + +#include <kpanelapplet.h> + +#include <qvaluelist.h> +#include <qwidget.h> +#include <qlabel.h> +#include <qptrlist.h> +#include <qpixmap.h> +#include <qrangecontrol.h> + +class QBoxLayout; +class QLabel; +class QPopupMenu; +class QSlider; + +class KLed; +class KLedButton; +class KAction; +class KActionCollection; +class KSmallSlider; +class KGlobalAccel; + +class MixDevice; +class VerticalText; +class Mixer; +class ViewBase; + +#include "mixdevicewidget.h" +#include "volume.h" + + +class MDWSlider : public MixDeviceWidget +{ + Q_OBJECT + +public: + MDWSlider( Mixer *mixer, MixDevice* md, + bool showMuteLED, bool showRecordLED, + bool small, Qt::Orientation, + QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0); + ~MDWSlider() {} + + void addActionToPopup( KAction *action ); + + bool isStereoLinked() const { return m_linked; }; + bool isLabeled() const; + + void setStereoLinked( bool value ); + void setLabeled( bool value ); + void setTicks( bool ticks ); + void setIcons( bool value ); + void setValueStyle( ValueStyle valueStyle ); + void setColors( QColor high, QColor low, QColor back ); + void setMutedColors( QColor high, QColor low, QColor back ); + QSize sizeHint() const; + bool eventFilter( QObject* obj, QEvent* e ); + QSizePolicy sizePolicy() const; + +public slots: + void toggleRecsrc(); + void toggleMuted(); + void toggleStereoLinked(); + + void setDisabled(); + void setDisabled( bool value ); + void update(); + virtual void showContextMenu(); + + +signals: + void newVolume( int num, Volume volume ); + void newMasterVolume( Volume volume ); + void masterMuted( bool ); + void newRecsrc( int num, bool on ); + void toggleMenuBar(bool value); + +private slots: + void setRecsrc( bool value ); + void setMuted(bool value); + void volumeChange( int ); + + void increaseVolume(); + void decreaseVolume(); + +private: + QPixmap icon( int icontype ); + void setIcon( int icontype ); + void createWidgets( bool showMuteLED, bool showRecordLED ); + void updateValue( QLabel *value, Volume::ChannelID chid ); + + bool m_linked; + ValueStyle m_valueStyle; + QLabel *m_iconLabel; + KLedButton *m_muteLED; + KLedButton *m_recordLED; + QWidget *m_label; // is either QLabel or VerticalText + QBoxLayout *_layout; + QPtrList<QWidget> m_sliders; + QValueList<Volume::ChannelID> _slidersChids; + QPtrList<QLabel> _numbers; + // QValueList<Volume::ChannelID> _numbersChids; +}; + +#endif diff --git a/kmix/mdwswitch.cpp b/kmix/mdwswitch.cpp new file mode 100644 index 00000000..54f6def1 --- /dev/null +++ b/kmix/mdwswitch.cpp @@ -0,0 +1,231 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qcursor.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qobject.h> +#include <qslider.h> +#include <qtooltip.h> + +#include <klocale.h> +#include <kconfig.h> +#include <kaction.h> +#include <kpopupmenu.h> +#include <kglobalaccel.h> +#include <kkeydialog.h> +#include <kdebug.h> + +#include "kledbutton.h" +#include "mdwswitch.h" +#include "mixer.h" +#include "viewbase.h" +#include "verticaltext.h" + +/** + * Class that represents a single Switch + * The orientation (horizontal, vertical) can be configured and whether it should + * be "small" (uses KSmallSlider instead of QSlider then). + */ +MDWSwitch::MDWSwitch(Mixer *mixer, MixDevice* md, + bool small, Qt::Orientation orientation, + QWidget* parent, ViewBase* mw, const char* name) : + MixDeviceWidget(mixer,md,small,orientation,parent,mw,name), + _label(0) , _labelV(0) , _switchLED(0), _layout(0) +{ + // create actions (on _mdwActions, see MixDeviceWidget) + + // KStdAction::showMenubar() is in MixDeviceWidget now + new KToggleAction( i18n("&Hide"), 0, this, SLOT(setDisabled()), _mdwActions, "hide" ); + new KAction( i18n("C&onfigure Shortcuts..."), 0, this, SLOT(defineKeys()), _mdwActions, "keys" ); + + // create widgets + createWidgets(); + + m_keys->insert( "Toggle switch", i18n( "Toggle Switch" ), QString::null, + KShortcut(), KShortcut(), this, SLOT( toggleSwitch() ) ); + + // The keys are loaded in KMixerWidget::loadConfig, see kmixerwidget.cpp (now: kmixtoolbox.cpp) + //m_keys->readSettings(); + //m_keys->updateConnections(); + + installEventFilter( this ); // filter for popup +} + +MDWSwitch::~MDWSwitch() +{ +} + + +void MDWSwitch::createWidgets() +{ + if ( _orientation == Qt::Vertical ) { + _layout = new QVBoxLayout( this ); + _layout->setAlignment(Qt::AlignHCenter); + } + else { + _layout = new QHBoxLayout( this ); + _layout->setAlignment(Qt::AlignVCenter); + } + QToolTip::add( this, m_mixdevice->name() ); + + + _layout->addSpacing( 4 ); + // --- LEDS -------------------------- + if ( _orientation == Qt::Vertical ) { + if( m_mixdevice->isRecordable() ) + _switchLED = new KLedButton( Qt::red, + m_mixdevice->isRecSource()?KLed::On:KLed::Off, + KLed::Sunken, KLed::Circular, this, "RecordLED" ); + else + _switchLED = new KLedButton( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" ); + _switchLED->setFixedSize(16,16); + _labelV = new VerticalText( this, m_mixdevice->name().utf8().data() ); + + _layout->addWidget( _switchLED ); + _layout->addSpacing( 2 ); + _layout->addWidget( _labelV ); + + _switchLED->installEventFilter( this ); + _labelV->installEventFilter( this ); + } + else + { + if( m_mixdevice->isRecordable() ) + _switchLED = new KLedButton( Qt::red, + m_mixdevice->isRecSource()?KLed::On:KLed::Off, + KLed::Sunken, KLed::Circular, this, "RecordLED" ); + else + _switchLED = new KLedButton( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" ); + _switchLED->setFixedSize(16,16); + _label = new QLabel(m_mixdevice->name(), this, "SwitchName"); + + _layout->addWidget( _switchLED ); + _layout->addSpacing( 1 ); + _layout->addWidget( _label ); + _switchLED->installEventFilter( this ); + _label->installEventFilter( this ); + } + connect( _switchLED, SIGNAL(stateChanged(bool)), this, SLOT(toggleSwitch()) ); + _layout->addSpacing( 4 ); +} + +void MDWSwitch::update() +{ + if ( _switchLED != 0 ) { + _switchLED->blockSignals( true ); + if( m_mixdevice->isRecordable() ) + _switchLED->setState( m_mixdevice->isRecSource() ? KLed::On : KLed::Off ); + else + _switchLED->setState( m_mixdevice->isMuted() ? KLed::Off : KLed::On ); + + _switchLED->blockSignals( false ); + } +} + +void MDWSwitch::setBackgroundMode(BackgroundMode m) +{ + if ( _label != 0 ){ + _label->setBackgroundMode(m); + } + if ( _labelV != 0 ){ + _labelV->setBackgroundMode(m); + } + _switchLED->setBackgroundMode(m); + MixDeviceWidget::setBackgroundMode(m); +} + +void MDWSwitch::showContextMenu() +{ + if( m_mixerwidget == NULL ) + return; + + KPopupMenu *menu = m_mixerwidget->getPopup(); + + QPoint pos = QCursor::pos(); + menu->popup( pos ); +} + +QSize MDWSwitch::sizeHint() const { + if ( _layout != 0 ) { + return _layout->sizeHint(); + } + else { + // layout not (yet) created + return QWidget::sizeHint(); + } +} + + +/** + This slot is called, when a user has clicked the mute button. Also it is called by any other + associated KAction like the context menu. +*/ +void MDWSwitch::toggleSwitch() { + if( m_mixdevice->isRecordable() ) + setSwitch( !m_mixdevice->isRecSource() ); + else + setSwitch( !m_mixdevice->isMuted() ); +} + +void MDWSwitch::setSwitch(bool value) +{ + if ( m_mixdevice->isSwitch() ) { + if ( m_mixdevice->isRecordable() ) { + m_mixer->setRecordSource( m_mixdevice->num(), value ); + } + else { + m_mixdevice->setMuted( value ); + m_mixer->commitVolumeChange( m_mixdevice ); + } + } +} + +void MDWSwitch::setDisabled() +{ + setDisabled( true ); +} + +void MDWSwitch::setDisabled( bool value ) { + if ( m_disabled!=value) + { + value ? hide() : show(); + m_disabled = value; + } +} + +/** + * An event filter for the various QWidgets. We watch for Mouse press Events, so + * that we can popup the context menu. + */ +bool MDWSwitch::eventFilter( QObject* obj, QEvent* e ) +{ + if (e->type() == QEvent::MouseButtonPress) { + QMouseEvent *qme = static_cast<QMouseEvent*>(e); + if (qme->button() == Qt::RightButton) { + showContextMenu(); + return true; + } + } + return QWidget::eventFilter(obj,e); +} + +#include "mdwswitch.moc" diff --git a/kmix/mdwswitch.h b/kmix/mdwswitch.h new file mode 100644 index 00000000..fd145623 --- /dev/null +++ b/kmix/mdwswitch.h @@ -0,0 +1,88 @@ +//-*-C++-*- +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2004 Chrisitan Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MDWSWITCH_H +#define MDWSWITCH_H + +#include <kpanelapplet.h> + +#include <qwidget.h> +#include "volume.h" +#include <qptrlist.h> +#include <qpixmap.h> +#include <qrangecontrol.h> + +class QBoxLayout; +class QLabel; +class QPopupMenu; +class QSlider; + +class KLedButton; +class KAction; +class KActionCollection; +class KSmallSlider; +class KGlobalAccel; + +class MixDevice; +class VerticalText; +class Mixer; +class ViewBase; + +#include "mixdevicewidget.h" + +class MDWSwitch : public MixDeviceWidget +{ + Q_OBJECT + +public: + MDWSwitch( Mixer *mixer, MixDevice* md, + bool small, Qt::Orientation orientation, + QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0); + ~MDWSwitch(); + + void addActionToPopup( KAction *action ); + QSize sizeHint() const; + void setBackgroundMode(BackgroundMode m); + bool eventFilter( QObject* obj, QEvent* e ); + +public slots: + // GUI hide and show + void setDisabled(); + void setDisabled(bool); + + // Switch on/off + void toggleSwitch(); + void setSwitch(bool value); + + void update(); + virtual void showContextMenu(); + +private: + void createWidgets(); + + QLabel *_label; + VerticalText *_labelV; + KLedButton *_switchLED; + QBoxLayout *_layout; +}; + +#endif diff --git a/kmix/mixdevice.cpp b/kmix/mixdevice.cpp new file mode 100644 index 00000000..16984ceb --- /dev/null +++ b/kmix/mixdevice.cpp @@ -0,0 +1,221 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <kdebug.h> +#include <klocale.h> + +#include "mixdevice.h" + + +/** + * Constructs a MixDevice. A MixDevice represents one channel or control of + * the mixer hardware. A MixDevice has a type (e.g. PCM), a descriptive name + * (for example "Master" or "Headphone" or "IEC 958 Output"), + * can have a volume level (2 when stereo), can be recordable and muted. + * The category tells which kind of control the MixDevice is. + * + * Hints: Meaning of "category" has changed. In future the MixDevice might contain two + * Volume objects, one for Output (Playback volume) and one for Input (Record volume). + */ +MixDevice::MixDevice( int num, Volume &vol, bool recordable, bool mute, + QString name, ChannelType type, DeviceCategory category ) : + _volume( vol ), _type( type ), _num( num ), _recordable( recordable ), + _mute( mute ), _category( category ) +{ + // Hint: "_volume" gets COPIED from "vol" due to the fact that the copy-constructor actually copies the volume levels. + _switch = false; + _recSource = false; + if( name.isEmpty() ) + _name = i18n("unknown"); + else + _name = name; + + _pk.setNum(num); + + + if( category == MixDevice::SWITCH ) + _switch = true; +} + +MixDevice::MixDevice(const MixDevice &md) : QObject() +{ + _name = md._name; + _volume = md._volume; + _type = md._type; + _num = md._num; + _pk = md._pk; + _recordable = md._recordable; + _recSource = md._recSource; + _category = md._category; + _switch = md._switch; + _mute = md._mute; + _enumValues = md._enumValues; +} + +MixDevice::~MixDevice() { + // Clear MixDevices enum Strings (switch on auto-delete, so the QString's inside will be cleared) + _enumValues.setAutoDelete(true); + _enumValues.clear(); +} + +Volume& MixDevice::getVolume() +{ + return _volume; +} + +long MixDevice::getVolume(Volume::ChannelID chid) { + return _volume.getVolume(chid); +} + +long MixDevice::maxVolume() { + return _volume.maxVolume(); +} + +long MixDevice::minVolume() { + return _volume.minVolume(); +} + +void MixDevice::setEnumId(int enumId) +{ + if ( enumId < _enumValues.count() ) { + _enumCurrentId = enumId; + } +} + +unsigned int MixDevice::enumId() +{ + return _enumCurrentId; +} + +QPtrList<QString>& MixDevice::enumValues() { + return _enumValues; +} + + +// @todo Used only at mixdevicewidget.cpp:625 . Replace that ASAP !!! +void MixDevice::setVolume( int channel, int volume ) +{ + _volume.setVolume( (Volume::ChannelID)channel /* ARGH! */, volume ); +} + +QString& MixDevice::getPK() { + return _pk; +} + +void MixDevice::setPK(QString &PK) { + _pk = PK; + // The key is used in the config file. It should not contain spaces + _pk.replace(' ', '_'); +} + +/** + * This mehtod is currently only called on "kmixctrl --restore" + * + * Normally we have a working _volume object already, which is very important, + * because we need to read the minimum and maximum volume levels. + * (Another solutien would be to "equip" volFromConfig with maxInt and minInt values). + */ +void MixDevice::read( KConfig *config, const QString& grp ) +{ + QString devgrp; + devgrp.sprintf( "%s.Dev%i", grp.ascii(), _num ); + config->setGroup( devgrp ); + //kdDebug(67100) << "MixDevice::read() of group devgrp=" << devgrp << endl; + + char *nameLeftVolume, *nameRightVolume; + if ( _volume.isCapture() ) { + nameLeftVolume = "volumeLCapture"; + nameRightVolume = "volumeRCapture"; + } else { + nameLeftVolume = "volumeL"; + nameRightVolume = "volumeR"; + } + Volume::ChannelMask chMask = Volume::MNONE; + int vl = config->readNumEntry(nameLeftVolume, -1); + if (vl!=-1) { + chMask = (Volume::ChannelMask)(chMask | Volume::MLEFT); + } + int vr = config->readNumEntry(nameRightVolume, -1); + if (vr!=-1) { + chMask = (Volume::ChannelMask)(chMask | Volume::MRIGHT); + } + + /* + * Now start construction a temporary Volume object. + * We take the maxvol and minvol values from _volume, which is already constructed. + * Otherwise we would have to wildly guess those values + */ + Volume *volFromConfig = new Volume(chMask, _volume._maxVolume, _volume._minVolume); + if (vl!=-1) { + volFromConfig->setVolume(Volume::LEFT , vl); + } + if (vr!=-1) { + volFromConfig->setVolume(Volume::RIGHT, vr); + } + // commit the read config + _volume.setVolume(*volFromConfig); + delete volFromConfig; + + int mute = config->readNumEntry("is_muted", -1); + if ( mute!=-1 ) { + _volume.setMuted( mute!=0 ); + } + + int recsrc = config->readNumEntry("is_recsrc", -1); + if ( recsrc!=-1 ) { + setRecSource( recsrc!=0 ); + } + + int enumId = config->readNumEntry("enum_id", -1); + if ( enumId != -1 ) { + setEnumId( enumId ); + } +} + +/** + * called on "kmixctrl --save" and from the GUI's (currently only on exit) + */ +void MixDevice::write( KConfig *config, const QString& grp ) +{ + QString devgrp; + devgrp.sprintf( "%s.Dev%i", grp.ascii(), _num ); + config->setGroup(devgrp); + // kdDebug(67100) << "MixDevice::write() of group devgrp=" << devgrp << endl; + char *nameLeftVolume, *nameRightVolume; + if ( _volume.isCapture() ) { + nameLeftVolume = "volumeLCapture"; + nameRightVolume = "volumeRCapture"; + } else { + nameLeftVolume = "volumeL"; + nameRightVolume = "volumeR"; + } + config->writeEntry(nameLeftVolume, getVolume( Volume::LEFT ) ); + config->writeEntry(nameRightVolume, getVolume( Volume::RIGHT ) ); + config->writeEntry("is_muted", (int)_volume.isMuted() ); + config->writeEntry("is_recsrc", (int)isRecSource() ); + config->writeEntry("name", _name); + if ( isEnum() ) { + config->writeEntry("enum_id", enumId() ); + } +} + +#include "mixdevice.moc" + diff --git a/kmix/mixdevice.h b/kmix/mixdevice.h new file mode 100644 index 00000000..89ec052c --- /dev/null +++ b/kmix/mixdevice.h @@ -0,0 +1,114 @@ +#ifndef MixDevice_h +#define MixDevice_h + +#include "volume.h" +#include <qstring.h> +#include <kconfig.h> +#include <qobject.h> +#include <qptrlist.h> + +// ! @todo : CONSIDER MERGING OF MixDevice and Volume classes: +// Not easy possible, because Volume is used in the driver backends + +/* !! @todo : Add 2 fields: + * bool update_from_Hardware; + * bool update_from_UI; + * They will show whether there are pending changes from both sides. + * Updates will be faster and more reliable by this. + */ +class MixDevice : public QObject +{ + Q_OBJECT + + public: + // For each ChannelType a special icon exists + enum ChannelType {AUDIO = 1, BASS, CD, EXTERNAL, MICROPHONE, + MIDI, RECMONITOR, TREBLE, UNKNOWN, VOLUME, + VIDEO, SURROUND, HEADPHONE, DIGITAL, AC97, + SURROUND_BACK, SURROUND_LFE, SURROUND_CENTERFRONT, SURROUND_CENTERBACK + }; + + + // The DeviceCategory tells the type of the device + // It is used in bitmasks, so you must use values of 2^n . + enum DeviceCategory { UNDEFINED= 0x00, SLIDER=0x01, SWITCH=0x02, ENUM=0x04, ALL=0xff }; + + + MixDevice(int num, Volume &vol, bool recordable, bool mute, + QString name, ChannelType type = UNKNOWN, DeviceCategory category = +SLIDER ); + MixDevice(const MixDevice &md); + ~MixDevice(); + + int num() { return _num; }; + QString name() { return _name; }; + /** + * Returns an unique ID of this MixDevice. By default the number + * 'num' from the constructor is returned. It is recommended that + * a better ID is set directly after constructing the MixDevice using + * the setUniqueID(). + */ + QString& getPK(); + /** + * Set a suitable PK for this MixDevice. It is used in looking up + * the keys in kmixrc. It is advised to set a nice name, like + * 'PCM_2', which would mean "2nd PCM device of the sound card". + */ + void setPK(QString &id); + bool isRecordable() { return _recordable; }; + bool isRecSource() { return _recSource; }; + bool isSwitch() { return _switch; } // !! change to _category == MixDevice::SWITCH + bool isEnum() { return _category == MixDevice::ENUM; } + bool isMuted() { return _volume.isMuted(); }; + bool hasMute() { return _mute; } + + void setMuted(bool value) { _volume.setMuted( value ); }; + void setVolume( int channel, int volume ); + void setRecSource( bool rec ) { _recSource = rec; } + long getVolume(Volume::ChannelID chid); + Volume& getVolume(); + long maxVolume(); + long minVolume(); + + void setEnumId(int); + unsigned int enumId(); + QPtrList<QString>& enumValues(); + + void read( KConfig *config, const QString& grp ); + void write( KConfig *config, const QString& grp ); + + void setType( ChannelType channeltype ) { _type = channeltype; }; + ChannelType type() { return _type; }; + + DeviceCategory category() { return _category; }; + + signals: + void newVolume( int num, Volume volume ); + + protected: + Volume _volume; + ChannelType _type; + // The DeviceCategory tells, how "important" a MixDevice is. + // The driver (e.g. mixer_oss.cpp) must set this value. It is + // used for deciding what Sliders to show and for distributing + // the sliders. It is advised to use the following categories: + // BASIC: Master, PCM + // PRIMARY: CD, Headphone, Microphone, Line + // SECONDARY: All others + // SWITCH: All devices which only have a On/Off-Switch + int _num; // ioctl() device number of mixer + bool _recordable; // Can it be recorded? + bool _switch; // On/Off switch // !! remove + bool _mute; // Available mute option + bool _recSource; // Current rec status + DeviceCategory _category; // category + QString _name; // Ascii channel name + QString _pk; // Primary key, used as part in config file keys + // A MixDevice, that is an ENUM, has these _enumValues + QPtrList<QString> _enumValues; + int _enumCurrentId; + +}; + +#endif + diff --git a/kmix/mixdevicewidget.cpp b/kmix/mixdevicewidget.cpp new file mode 100644 index 00000000..e032f361 --- /dev/null +++ b/kmix/mixdevicewidget.cpp @@ -0,0 +1,120 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <klocale.h> +#include <kled.h> +#include <kiconloader.h> +#include <kconfig.h> +#include <kaction.h> +#include <kpopupmenu.h> +#include <kglobalaccel.h> +#include <kkeydialog.h> +#include <kdebug.h> + +#include <qobject.h> +#include <qcursor.h> +#include <qslider.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpixmap.h> +#include <qtooltip.h> +#include <qwmatrix.h> + +#include "mixer.h" +#include "mixdevicewidget.h" +#include "viewbase.h" +#include "kledbutton.h" +#include "ksmallslider.h" +#include "verticaltext.h" + +/** + * Class that represents a single mix device, inlcuding PopUp, muteLED, ... + * Used in KMix main window and DockWidget and PanelApplet. + * It can be configured to include or exclude the recordLED and the muteLED. + * The direction (horizontal, vertical) can be configured and whether it should + * be "small" (uses KSmallSlider instead of QSlider then). + */ +MixDeviceWidget::MixDeviceWidget(Mixer *mixer, MixDevice* md, + bool small, Qt::Orientation orientation, + QWidget* parent, ViewBase* mw, const char* name) : + QWidget( parent, name ), m_mixer(mixer), m_mixdevice( md ), m_mixerwidget( mw ), + m_disabled( false ), _orientation( orientation ), m_small( small ) +{ + _mdwActions = new KActionCollection( this ); + m_keys = new KGlobalAccel( this, "Keys" ); +} + +MixDeviceWidget::~MixDeviceWidget() +{ +} + + +void MixDeviceWidget::addActionToPopup( KAction *action ) +{ + _mdwActions->insert( action ); +} + + +bool MixDeviceWidget::isDisabled() const +{ + return m_disabled; +} + + +KGlobalAccel *MixDeviceWidget::keys( void ) +{ + return m_keys; +} + +void MixDeviceWidget::defineKeys() +{ + if (m_keys) { + KKeyDialog::configure(m_keys, 0, false); + // The keys are saved in KMixerWidget::saveConfig, see kmixerwidget.cpp + m_keys->updateConnections(); + } +} + +void MixDeviceWidget::volumeChange( int ) { /* is virtual */ } +void MixDeviceWidget::setDisabled( bool ) { /* is virtual */ } +void MixDeviceWidget::setVolume( int /*channel*/, int /*vol*/ ) { /* is virtual */ } +void MixDeviceWidget::setVolume( Volume /*vol*/ ) { /* is virtual */ } +void MixDeviceWidget::update() { /* is virtual */ } +void MixDeviceWidget::showContextMenu() { /* is virtual */ } +void MixDeviceWidget::setColors( QColor , QColor , QColor ) { /* is virtual */ } +void MixDeviceWidget::setIcons( bool ) { /* is virtual */ } +void MixDeviceWidget::setLabeled( bool ) { /* is virtual */ } +void MixDeviceWidget::setMutedColors( QColor , QColor , QColor ) { /* is virtual */ } + + + + +void MixDeviceWidget::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button()==RightButton ) + showContextMenu(); + else { + QWidget::mousePressEvent(e); + } +} + + +#include "mixdevicewidget.moc" diff --git a/kmix/mixdevicewidget.h b/kmix/mixdevicewidget.h new file mode 100644 index 00000000..96242e73 --- /dev/null +++ b/kmix/mixdevicewidget.h @@ -0,0 +1,115 @@ +//-*-C++-*- +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * 1996-2000 Christian Esken <esken@kde.org> + * Sven Fischer <herpes@kawo2.rwth-aachen.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MIXDEVICEWIDGET_H +#define MIXDEVICEWIDGET_H + +#include <kpanelapplet.h> + +#include <qwidget.h> +#include "volume.h" +#include <qptrlist.h> +#include <qpixmap.h> +#include <qrangecontrol.h> + +class QBoxLayout; +class QLabel; +class QPopupMenu; +class QSlider; + +class KLed; +class KLedButton; +class KAction; +class KActionCollection; +class KSmallSlider; +class KGlobalAccel; + +class MixDevice; +class VerticalText; +class Mixer; +class ViewBase; + +class MixDeviceWidget + : public QWidget +{ + Q_OBJECT + +public: + enum ValueStyle { NNONE = 0, NABSOLUTE = 1, NRELATIVE = 2 } ; + + MixDeviceWidget( Mixer *mixer, MixDevice* md, + bool small, Qt::Orientation orientation, + QWidget* parent = 0, ViewBase* mw = 0, const char* name = 0); + ~MixDeviceWidget(); + + void addActionToPopup( KAction *action ); + + virtual bool isDisabled() const; + MixDevice* mixDevice() { return m_mixdevice; }; + + virtual void setColors( QColor high, QColor low, QColor back ); + virtual void setIcons( bool value ); + virtual void setMutedColors( QColor high, QColor low, QColor back ); + + virtual bool isStereoLinked() const { return false; }; + //virtual bool isLabeled() const { return false; }; + virtual void setStereoLinked( bool ) {}; + virtual void setLabeled( bool ); + virtual void setTicks( bool ) {}; + virtual void setValueStyle( ValueStyle ) {}; + + virtual KGlobalAccel *keys(void); + +public slots: + virtual void setDisabled( bool value ); + virtual void defineKeys(); + virtual void update(); + virtual void showContextMenu(); + +signals: + void newVolume( int num, Volume volume ); + void newMasterVolume( Volume volume ); + void masterMuted( bool ); + void newRecsrc( int num, bool on ); + +protected slots: + void volumeChange( int ); + virtual void setVolume( int channel, int volume ); + virtual void setVolume( Volume volume ); + +protected: + Mixer* m_mixer; + MixDevice* m_mixdevice; + KActionCollection* _mdwActions; + KGlobalAccel* m_keys; + ViewBase* m_mixerwidget; + bool m_disabled; + Qt::Orientation _orientation; + bool m_small; + +private: + void mousePressEvent( QMouseEvent *e ); +}; + +#endif diff --git a/kmix/mixer.cpp b/kmix/mixer.cpp new file mode 100644 index 00000000..316625e0 --- /dev/null +++ b/kmix/mixer.cpp @@ -0,0 +1,753 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken - esken@kde.org + * 2002 Helio Chissini de Castro - helio@conectiva.com.br + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <qtimer.h> + +#include <klocale.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <dcopobject.h> + +#include "mixer.h" +#include "mixer_backend.h" +#include "kmix-platforms.cpp" +#include "volume.h" + +//#define MIXER_MASTER_DEBUG + +#ifdef MIXER_MASTER_DEBUG +#warning MIXER_MASTER_DEBUG is enabled. DO NOT SHIP KMIX LIKE THIS !!! +#endif + +/** + * Some general design hints. Hierachy is Mixer->MixDevice->Volume + */ + +// !! Warning: Don't commit with "KMIX_DCOP_OBJID_TEST" #define'd (cesken) +#undef KMIX_DCOP_OBJID_TEST +int Mixer::_dcopID = 0; + +QPtrList<Mixer> Mixer::s_mixers; +QString Mixer::_masterCard; +QString Mixer::_masterCardDevice; + +int Mixer::numDrivers() +{ + MixerFactory *factory = g_mixerFactories; + int num = 0; + while( factory->getMixer!=0 ) + { + num++; + factory++; + } + + return num; +} + +/* + * Returns a reference of the current mixer list. + */ +QPtrList<Mixer>& Mixer::mixers() +{ + return s_mixers; +} + + +Mixer::Mixer( int driver, int device ) : DCOPObject( "Mixer" ) +{ + _pollingTimer = 0; + + _mixerBackend = 0; + getMixerFunc *f = g_mixerFactories[driver].getMixer; + if( f!=0 ) { + _mixerBackend = f( device ); + } + + readSetFromHWforceUpdate(); // enforce an initial update on first readSetFromHW() + + m_balance = 0; + m_profiles.setAutoDelete( true ); + + _pollingTimer = new QTimer(); // will be started on open() and stopped on close() + connect( _pollingTimer, SIGNAL(timeout()), this, SLOT(readSetFromHW())); + + QCString objid; +#ifndef KMIX_DCOP_OBJID_TEST + objid.setNum(_mixerBackend->m_devnum); +#else +// should use a nice name like the Unique-Card-ID instead !! + objid.setNum(Mixer::_dcopID); + Mixer::_dcopID ++; +#endif + objid.prepend("Mixer"); + DCOPObject::setObjId( objid ); + +} + +Mixer::~Mixer() { + // Close the mixer. This might also free memory, depending on the called backend method + close(); + delete _pollingTimer; +} + +void Mixer::volumeSave( KConfig *config ) +{ + // kdDebug(67100) << "Mixer::volumeSave()" << endl; + readSetFromHW(); + QString grp("Mixer"); + grp.append(mixerName()); + _mixerBackend->m_mixDevices.write( config, grp ); +} + +void Mixer::volumeLoad( KConfig *config ) +{ + QString grp("Mixer"); + grp.append(mixerName()); + if ( ! config->hasGroup(grp) ) { + // no such group. Volumes (of this mixer) were never saved beforehand. + // Thus don't restore anything (also see Bug #69320 for understanding the real reason) + return; // make sure to bail out immediately + } + + // else restore the volumes + _mixerBackend->m_mixDevices.read( config, grp ); + + // set new settings + QPtrListIterator<MixDevice> it( _mixerBackend->m_mixDevices ); + for(MixDevice *md=it.toFirst(); md!=0; md=++it ) + { + // kdDebug(67100) << "Mixer::volumeLoad() writeVolumeToHW(" << md->num() << ", "<< md->getVolume() << ")" << endl; + // !! @todo Restore record source + //setRecordSource( md->num(), md->isRecSource() ); + _mixerBackend->setRecsrcHW( md->num(), md->isRecSource() ); + _mixerBackend->writeVolumeToHW( md->num(), md->getVolume() ); + if ( md->isEnum() ) _mixerBackend->setEnumIdHW( md->num(), md->enumId() ); + } +} + + +/** + * Opens the mixer. + * Also, starts the polling timer, for polling the Volumes from the Mixer. + * + * @return 0, if OK. An Mixer::ERR_ error code otherwise + */ +int Mixer::open() +{ + int err = _mixerBackend->open(); + // A better ID is now calculated in mixertoolbox.cpp, and set via setID(), + // but we want a somhow usable fallback just in case. + _id = mixerName(); + + if( err == ERR_INCOMPATIBLESET ) // !!! When does this happen ?!? + { + // Clear the mixdevices list + _mixerBackend->m_mixDevices.clear(); + // try again with fresh set + err = _mixerBackend->open(); + } + + MixDevice* recommendedMaster = _mixerBackend->recommendedMaster(); + if ( recommendedMaster != 0 ) { + setMasterDevice(recommendedMaster->getPK() ); + } + else { + kdError(67100) << "Mixer::open() no master detected." << endl; + QString noMaster = "---no-master-detected---"; + setMasterDevice(noMaster); // no master + } + /* + // --------- Copy the hardware values to the MixDevice ------------------- + MixSet &mset = _mixerBackend->m_mixDevices; + if( !mset.isEmpty() ) { + // Copy the initial mix set + // kdDebug(67100) << "Mixer::setupMixer() copy Set" << endl; + MixDevice* md; + for( md = mset.first(); md != 0; md = mset.next() ) + { + MixDevice* mdCopy = _mixerBackend->m_mixDevices.first(); + while( mdCopy!=0 && mdCopy->num() != md->num() ) { + mdCopy = _mixerBackend->m_mixDevices.next(); + } + if ( mdCopy != 0 ) { + // The "mdCopy != 0" was not checked before, but its safer to do so + setRecordSource( md->num(), md->isRecSource() ); + Volume &vol = mdCopy->getVolume(); + vol.setVolume( md->getVolume() ); + mdCopy->setMuted( md->isMuted() ); + + // !! might need writeVolumeToHW( mdCopy->num(), mdCopy->getVolume() ); + } + } + } + */ + if ( _mixerBackend->needsPolling() ) { + _pollingTimer->start(50); + } + else { + _mixerBackend->prepareSignalling(this); + // poll once to give the GUI a chance to rebuild it's info + QTimer::singleShot( 50, this, SLOT( readSetFromHW() ) ); + } + return err; +} + + +/** + * Closes the mixer. + * Also, stops the polling timer. + * + * @return 0 (always) + */ +int Mixer::close() +{ + _pollingTimer->stop(); + return _mixerBackend->close(); +} + + +/* ------- WRAPPER METHODS. START ------------------------------ */ +unsigned int Mixer::size() const +{ + return _mixerBackend->m_mixDevices.count(); +} + +MixDevice* Mixer::operator[](int num) +{ + MixDevice* md = _mixerBackend->m_mixDevices.at( num ); + Q_ASSERT( md ); + return md; +} + +MixSet Mixer::getMixSet() +{ + return _mixerBackend->m_mixDevices; +} + +bool Mixer::isValid() { + return _mixerBackend->isValid(); +} + +bool Mixer::isOpen() const { + if ( _mixerBackend == 0 ) + return false; + else + return _mixerBackend->isOpen(); +} + +/* ------- WRAPPER METHODS. END -------------------------------- */ + +/** + * After calling this, readSetFromHW() will do a complete update. This will + * trigger emitting the appropriate signals like newVolumeLevels(). + * + * This method is useful, if you need to get a "refresh signal" - used at: + * 1) Start of KMix - so that we can be sure an initial signal is emitted + * 2) When reconstructing any MixerWidget (e.g. DockIcon after applying preferences) + */ +void Mixer::readSetFromHWforceUpdate() const { + _readSetFromHWforceUpdate = true; +} + +/** + You can call this to retrieve the freshest information from the mixer HW. + This method is also called regulary by the mixer timer. +*/ +void Mixer::readSetFromHW() +{ + if ( ! _mixerBackend->isOpen() ) { + // bail out immediately, if the mixer is not open. + // This can happen currently only, if the user executes the DCOP close() call. + return; + } + bool updated = _mixerBackend->prepareUpdateFromHW(); + if ( (! updated) && (! _readSetFromHWforceUpdate) ) { + // Some drivers (ALSA) are smart. We don't need to run the following + // time-consuming update loop if there was no change + return; + } + _readSetFromHWforceUpdate = false; + MixDevice* md; + for( md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) + { + Volume& vol = md->getVolume(); + _mixerBackend->readVolumeFromHW( md->num(), vol ); + md->setRecSource( _mixerBackend->isRecsrcHW( md->num() ) ); + if (md->isEnum() ) { + md->setEnumId( _mixerBackend->enumIdHW(md->num()) ); + } + } + // Trivial implementation. Without looking at the devices + // kdDebug(67100) << "Mixer::readSetFromHW(): emit newVolumeLevels()" << endl; + emit newVolumeLevels(); + emit newRecsrc(); // cheap, but works +} + + +void Mixer::setBalance(int balance) +{ + // !! BAD, because balance only works on the master device. If you have not Master, the slider is a NOP + if( balance == m_balance ) { + // balance unchanged => return + return; + } + + m_balance = balance; + + MixDevice* master = masterDevice(); + if ( master == 0 ) { + // no master device available => return + return; + } + + Volume& vol = master->getVolume(); + _mixerBackend->readVolumeFromHW( master->num(), vol ); + + int left = vol[ Volume::LEFT ]; + int right = vol[ Volume::RIGHT ]; + int refvol = left > right ? left : right; + if( balance < 0 ) // balance left + { + vol.setVolume( Volume::LEFT, refvol); + vol.setVolume( Volume::RIGHT, (balance * refvol) / 100 + refvol ); + } + else + { + vol.setVolume( Volume::LEFT, -(balance * refvol) / 100 + refvol ); + vol.setVolume( Volume::RIGHT, refvol); + } + + _mixerBackend->writeVolumeToHW( master->num(), vol ); + + emit newBalance( vol ); +} + +QString Mixer::mixerName() +{ + return _mixerBackend->m_mixerName; +} + +QString Mixer::driverName( int driver ) +{ + getDriverNameFunc *f = g_mixerFactories[driver].getDriverName; + if( f!=0 ) + return f(); + else + return "unknown"; +} + +void Mixer::setID(QString& ref_id) +{ + _id = ref_id; +} + + +QString& Mixer::id() +{ + return _id; +} + +void Mixer::setMasterCard(QString& ref_id) +{ + // The value is taken over without checking on existance. This allows the User to define + // a MasterCard that is not always available (e.g. it is an USB hotplugging device). + // Also you can set the master at any time you like, e.g. after reading the KMix configuration file + // and before actually constructing the Mixer instances (hint: this mehtod is static!). + _masterCard = ref_id; +} + +Mixer* Mixer::masterCard() +{ + Mixer *mixer = 0; + kdDebug(67100) << "Mixer::masterCard() searching for id=" << _masterCard << "\n"; + for (mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) + { + if ( mixer->id() == _masterCard ) { +#ifdef MIXER_MASTER_DEBUG + kdDebug(67100) << "Mixer::masterCard() found id=" << mixer->id() << "\n"; +#endif + break; + } + } +#ifdef MIXER_MASTER_DEBUG + if ( mixer == 0) kdDebug(67100) << "Mixer::masterCard() found no Mixer* mixer \n"; +#endif + return mixer; +} + +void Mixer::setMasterCardDevice(QString& ref_id) +{ + // The value is taken over without checking on existance. This allows the User to define + // a MasterCard that is not always available (e.g. it is an USB hotplugging device). + // Also you can set the master at any time you like, e.g. after reading the KMix configuration file + // and before actually constructing the Mixer instances (hint: this mehtod is static!). + _masterCardDevice = ref_id; +#ifdef MIXER_MASTER_DEBUG + kdDebug(67100) << "Mixer::setMasterCardDevice(\"" << ref_id << "\")\n"; +#endif +} + +MixDevice* Mixer::masterCardDevice() +{ + MixDevice* md = 0; + Mixer *mixer = masterCard(); + if ( mixer != 0 ) { + for( md = mixer->_mixerBackend->m_mixDevices.first(); md != 0; md = mixer->_mixerBackend->m_mixDevices.next() ) { + + + if ( md->getPK() == _masterCardDevice ) + { +#ifdef MIXER_MASTER_DEBUG + kdDebug(67100) << "Mixer::masterCardDevice() getPK()=" + << md->getPK() << " , _masterCardDevice=" + << _masterCardDevice << "\n"; +#endif + break; + } + } + } + +#ifdef MIXER_MASTER_DEBUG + if ( md == 0) kdDebug(67100) << "Mixer::masterCardDevice() found no MixDevice* md" "\n"; +#endif + + return md; +} + + + + +/** + Used internally by the Mixer class and as DCOP method +*/ +void Mixer::setRecordSource( int devnum, bool on ) +{ + if( !_mixerBackend->setRecsrcHW( devnum, on ) ) // others have to be updated + { + for( MixDevice* md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) { + bool isRecsrc = _mixerBackend->isRecsrcHW( md->num() ); +// kdDebug(67100) << "Mixer::setRecordSource(): isRecsrcHW(" << md->num() << ") =" << isRecsrc << endl; + md->setRecSource( isRecsrc ); + } + // emitting is done after read + //emit newRecsrc(); // like "emit newVolumeLevels()", but for record source + } + else { + // just the actual mixdevice + for( MixDevice* md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) { + if( md->num() == devnum ) { + bool isRecsrc = _mixerBackend->isRecsrcHW( md->num() ); + md->setRecSource( isRecsrc ); + } + } + // emitting is done after read + //emit newRecsrc(); // like "emit newVolumeLevels()", but for record source + } + +} + + +MixDevice* Mixer::masterDevice() +{ + return find( _masterDevicePK ); +} + +void Mixer::setMasterDevice(QString &devPK) +{ + _masterDevicePK = devPK; +} + + +MixDevice* Mixer::find(QString& devPK) +{ + MixDevice* md = 0; + for( md = _mixerBackend->m_mixDevices.first(); md != 0; md = _mixerBackend->m_mixDevices.next() ) { + if( devPK == md->getPK() ) { + break; + } + } + return md; +} + + +MixDevice *Mixer::mixDeviceByType( int deviceidx ) +{ + unsigned int i=0; + while (i<size() && (*this)[i]->num()!=deviceidx) i++; + if (i==size()) return 0; + + return (*this)[i]; +} + +// @dcop +// Used also by the setMasterVolume() method. +void Mixer::setVolume( int deviceidx, int percentage ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return; + + Volume vol=mixdev->getVolume(); + // @todo The next call doesn't handle negative volumes correctly. + vol.setAllVolumes( (percentage*vol.maxVolume())/100 ); + _mixerBackend->writeVolumeToHW(deviceidx, vol); +} + +/** + Call this if you have a *reference* to a Volume object and have modified that locally. + Pass the MixDevice associated to that Volume to this method for writing back + the changed value to the mixer. + Hint: Why do we do it this way? + - It is fast (no copying of Volume objects required) + - It is easy to understand ( read - modify - commit ) +*/ +void Mixer::commitVolumeChange( MixDevice* md ) { + _mixerBackend->writeVolumeToHW(md->num(), md->getVolume() ); + _mixerBackend->setEnumIdHW(md->num(), md->enumId() ); +} + +// @dcop only +void Mixer::setMasterVolume( int percentage ) +{ + MixDevice *master = masterDevice(); + if (master != 0 ) { + setVolume( master->num(), percentage ); + } +} + +// @dcop +int Mixer::volume( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return 0; + + Volume vol=mixdev->getVolume(); + // @todo This will not work, if minVolume != 0 !!! + // e.g.: minVolume=5 or minVolume=-10 + // The solution is to check two cases: + // volume < 0 => use minVolume for volumeRange + // volume > 0 => use maxVolume for volumeRange + // If chosen volumeRange==0 => return 0 + // As this is potentially used often (Sliders, ...), it + // should beimplemented in the Volume class. + + // For now we go with "maxVolume()", like in the rest of KMix. + long volumeRange = vol.maxVolume(); // -vol.minVolume() ; + if ( volumeRange == 0 ) + { + return 0; + } + else + { + return ( vol.getVolume( Volume::LEFT )*100) / volumeRange ; + } +} + +// @dcop , especially for use in KMilo +void Mixer::setAbsoluteVolume( int deviceidx, long absoluteVolume ) { + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return; + + Volume vol=mixdev->getVolume(); + vol.setAllVolumes( absoluteVolume ); + _mixerBackend->writeVolumeToHW(deviceidx, vol); +} + +// @dcop , especially for use in KMilo +long Mixer::absoluteVolume( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return 0; + + Volume vol=mixdev->getVolume(); + long avgVolume=vol.getAvgVolume((Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT)); + return avgVolume; +} + +// @dcop , especially for use in KMilo +long Mixer::absoluteVolumeMax( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return 0; + + Volume vol=mixdev->getVolume(); + long maxVolume=vol.maxVolume(); + return maxVolume; +} + +// @dcop , especially for use in KMilo +long Mixer::absoluteVolumeMin( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return 0; + + Volume vol=mixdev->getVolume(); + long minVolume=vol.minVolume(); + return minVolume; +} + +// @dcop +int Mixer::masterVolume() +{ + int vol = 0; + MixDevice *master = masterDevice(); + if (master != 0 ) { + vol = volume( master->num() ); + } + return vol; +} + +// @dcop +void Mixer::increaseVolume( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (mixdev != 0) { + Volume vol=mixdev->getVolume(); + double fivePercent = (vol.maxVolume()-vol.minVolume()+1) / 20; + for (unsigned int i=Volume::CHIDMIN; i <= Volume::CHIDMAX; i++) { + int volToChange = vol.getVolume((Volume::ChannelID)i); + if ( fivePercent < 1 ) fivePercent = 1; + volToChange += (int)fivePercent; + vol.setVolume((Volume::ChannelID)i, volToChange); + } + _mixerBackend->writeVolumeToHW(deviceidx, vol); + } + + /* see comment at the end of decreaseVolume() + int vol=volume(deviceidx); + setVolume(deviceidx, vol+5); + */ +} + +// @dcop +void Mixer::decreaseVolume( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (mixdev != 0) { + Volume vol=mixdev->getVolume(); + double fivePercent = (vol.maxVolume()-vol.minVolume()+1) / 20; + for (unsigned int i=Volume::CHIDMIN; i <= Volume::CHIDMAX; i++) { + int volToChange = vol.getVolume((Volume::ChannelID)i); + //std::cout << "Mixer::decreaseVolume(): before: volToChange " <<i<< "=" <<volToChange << std::endl; + if ( fivePercent < 1 ) fivePercent = 1; + volToChange -= (int)fivePercent; + //std::cout << "Mixer::decreaseVolume(): after: volToChange " <<i<< "=" <<volToChange << std::endl; + vol.setVolume((Volume::ChannelID)i, volToChange); + //int volChanged = vol.getVolume((Volume::ChannelID)i); + //std::cout << "Mixer::decreaseVolume(): check: volChanged " <<i<< "=" <<volChanged << std::endl; + } // for + _mixerBackend->writeVolumeToHW(deviceidx, vol); + } + + /************************************************************ + It is important, not to implement this method like this: + int vol=volume(deviceidx); + setVolume(deviceidx, vol-5); + It creates too big rounding errors. If you don't beleive me, then + do a decreaseVolume() and increaseVolume() with "vol.maxVolume() == 31". + ***********************************************************/ +} + +// @dcop +void Mixer::setMute( int deviceidx, bool on ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return; + + mixdev->setMuted( on ); + + _mixerBackend->writeVolumeToHW(deviceidx, mixdev->getVolume() ); +} + +// @dcop only +void Mixer::setMasterMute( bool on ) +{ + MixDevice *master = masterDevice(); + if (master != 0 ) { + setMute( master->num(), on ); + } +} + + +// @dcop +void Mixer::toggleMute( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return; + + bool previousState= mixdev->isMuted(); + + mixdev->setMuted( !previousState ); + + _mixerBackend->writeVolumeToHW(deviceidx, mixdev->getVolume() ); +} + +// @dcop only +void Mixer::toggleMasterMute() +{ + MixDevice *master = masterDevice(); + if (master != 0 ) { + toggleMute( master->num() ); + } +} + + +// @dcop +bool Mixer::mute( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return true; + + return mixdev->isMuted(); +} + +// @dcop only +bool Mixer::masterMute() +{ + MixDevice *master = masterDevice(); + if (master != 0 ) { + return mute( master->num() ); + } + return true; +} + +// @dcop only +int Mixer::masterDeviceIndex() +{ + return masterDevice()->num(); +} + +bool Mixer::isRecordSource( int deviceidx ) +{ + MixDevice *mixdev= mixDeviceByType( deviceidx ); + if (!mixdev) return false; + + return mixdev->isRecSource(); +} + +/// @DCOP WHAT DOES THIS METHOD?!?!? +bool Mixer::isAvailableDevice( int deviceidx ) +{ + return mixDeviceByType( deviceidx ); +} + +#include "mixer.moc" diff --git a/kmix/mixer.h b/kmix/mixer.h new file mode 100644 index 00000000..b6d0917a --- /dev/null +++ b/kmix/mixer.h @@ -0,0 +1,174 @@ +//-*-C++-*- +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * 1996-2000 Christian Esken <esken@kde.org> + * Sven Fischer <herpes@kawo2.rwth-aachen.de> + * 2002 - Helio Chissini de Castro <helio@conectiva.com.br> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MIXER_H +#define MIXER_H + +#include <qstring.h> +#include <qtimer.h> +#include <qobject.h> +#include <qintdict.h> +#include <qptrlist.h> + +#include "volume.h" +class Mixer_Backend; +#include "mixerIface.h" +#include "mixset.h" +#include "mixdevice.h" + +class Volume; +class KConfig; + +class Mixer : public QObject, virtual public MixerIface +{ + Q_OBJECT + + public: + enum MixerError { ERR_PERM=1, ERR_WRITE, ERR_READ, ERR_NODEV, ERR_NOTSUPP, + ERR_OPEN, ERR_LASTERR, ERR_NOMEM, ERR_INCOMPATIBLESET, ERR_MIXEROPEN }; + + Mixer( int driver, int device ); + virtual ~Mixer(); + + static int numDrivers(); + + MixDevice* find(QString& devPK); + + void volumeSave( KConfig *config ); + void volumeLoad( KConfig *config ); + + /// Tells the number of the mixing devices + unsigned int size() const; + + bool isValid(); + bool isOpen() const; + + /// Returns a pointer to the mix device with the given number + MixDevice* operator[](int val_i_num); + + /// Returns a pointer to the mix device whose type matches the value + /// given by the parameter and the array MixerDevNames given in + /// mixer_oss.cpp (0 is Volume, 4 is PCM, etc.) + MixDevice *mixDeviceByType( int deviceidx ); + + /// Open/grab the mixer for further intraction + virtual int open(); + /// Close/release the mixer + virtual int close(); + + /// Returns a detailed state message after errors. Only for diagnostic purposes, no i18n. + QString& stateMessage() const; + + virtual QString mixerName(); + + // Returns the name of the driver, e.g. "OSS" or "ALSA0.9" + static QString driverName(int num); + + /// Returns an unique ID of the Mixer. It currently looks like "<soundcard_descr>:<hw_number>@<driver>" + QString& id(); + /// The owner/creator of the Mixer can set an unique name here. This key should never displayed to + /// the user, but can be used for referencing configuration items and such. + void setID(QString& ref_id); + + /// The KMix global master card. Please note that KMix and KMixPanelApplet can have a + /// different MasterCard's at the moment (but actually KMixPanelApplet does not read/save this yet). + /// At the moment it is only used for selecting the Mixer to use in KMix's DockIcon. + static void setMasterCard(QString& ref_id); + static Mixer* masterCard(); + /// The global Master Device inside the current MasterCard (as returned by masterCard()). + static void setMasterCardDevice(QString& ref_id); + static MixDevice* masterCardDevice(); + + + /// get the actual MixSet + MixSet getMixSet(); + + /// Returns the master volume device (doesn't work out :-(. See masterCard() and masterCardDevice() instead) + MixDevice* masterDevice(); + /// Sets the master volume device (doesn't work out :-(. See setMasterCard() and setMasterCardDevice() instead) + void setMasterDevice(QString&); + + /// DCOP oriented methods (look at mixerIface.h for the descriptions) + virtual void setVolume( int deviceidx, int percentage ); + virtual void setAbsoluteVolume( int deviceidx, long absoluteVolume ); + virtual void setMasterVolume( int percentage ); + + virtual void increaseVolume( int deviceidx ); + virtual void decreaseVolume( int deviceidx ); + + virtual long absoluteVolume( int deviceidx ); + virtual long absoluteVolumeMin( int deviceidx ); + virtual long absoluteVolumeMax( int deviceidx ); + virtual int volume( int deviceidx ); + virtual int masterVolume(); + virtual int masterDeviceIndex(); + + virtual void setMute( int deviceidx, bool on ); + virtual void setMasterMute( bool on ); + virtual bool mute( int deviceidx ); + virtual bool masterMute(); + virtual void toggleMute( int deviceidx ); + virtual void toggleMasterMute(); + virtual bool isRecordSource( int deviceidx ); + + virtual bool isAvailableDevice( int deviceidx ); + + void commitVolumeChange( MixDevice* md ); + + public slots: + virtual void readSetFromHW(); + void readSetFromHWforceUpdate() const; + virtual void setRecordSource( int deviceidx, bool on ); + + virtual void setBalance(int balance); // sets the m_balance (see there) + + signals: + void newBalance( Volume& ); + void newRecsrc( void ); + void newVolumeLevels(void); + + protected: + QTimer* _pollingTimer; + + int m_balance; // from -100 (just left) to 100 (just right) + + QPtrList<MixSet> m_profiles; + static QPtrList<Mixer> s_mixers; + + public: + int setupMixer( MixSet set ); + static QPtrList<Mixer>& mixers(); + + private: + Mixer_Backend *_mixerBackend; + mutable bool _readSetFromHWforceUpdate; + static int _dcopID; + QString _id; + QString _masterDevicePK; + static QString _masterCard; + static QString _masterCardDevice; +}; + +#endif diff --git a/kmix/mixerIface.h b/kmix/mixerIface.h new file mode 100644 index 00000000..6c8da9fd --- /dev/null +++ b/kmix/mixerIface.h @@ -0,0 +1,135 @@ +#ifndef __MIXER_IFACE_H +#define __MIXER_IFACE_H + +#include <dcopobject.h> + +class MixerIface : virtual public DCOPObject +{ + K_DCOP + +k_dcop: + /** + Sets the volume of the device with index deviceidx to the percentage + specified in the second parameter. The deviceidx is got from the array + at the start of mixer_oss.cpp . + */ + virtual void setVolume( int deviceidx, int percentage )=0; + /** + A simpler way to access the master volume (which is deviceidx 0). + */ + virtual void setMasterVolume( int percentage )=0; + + /** + Increase the volume of the given device by a 5% . + */ + virtual void increaseVolume( int deviceidx )=0; + /** + Decrease the volume of the given device by a 5% . + */ + virtual void decreaseVolume( int deviceidx )=0; + + /** + Returns the volume of the device (as a percentage, 0..100). + */ + virtual int volume( int deviceidx )=0; + /** + Returns the volume of the master device (as a percentage, 0..100). + */ + virtual int masterVolume()=0; + + + /** + Sets the absolute volume of the device. Lower bound is absoluteVolumeMin(), + upper bound is absoluteVolumeMax(). + */ + virtual void setAbsoluteVolume( int deviceidx, long absoluteVolume )=0; + /** + Returns the absolute volume of the device. The volume is in the range of + absoluteVolumeMin() <= absoluteVolume() <= absoluteVolumeMax() + */ + virtual long absoluteVolume( int deviceidx )=0; + /** + Returns the absolute maximum volume of the device. + */ + virtual long absoluteVolumeMin( int deviceidx )=0; + /** + Returns the absolute minimum volume of the device. + */ + virtual long absoluteVolumeMax( int deviceidx )=0; + + /** + Mutes or unmutes the specified device. + */ + virtual void setMute( int deviceidx, bool on )=0; + /** + Mutes or unmutes the master device. + */ + virtual void setMasterMute( bool on )=0; + /** + Toggles mute-state for the given device. + */ + virtual void toggleMute( int deviceidx )=0; + /** + Toggles mute-state for the master device. + */ + virtual void toggleMasterMute()=0; + /** + Returns if the given device is muted or not. If the device is not + available in this mixer, it is reported as muted. + */ + virtual bool mute( int deviceidx )=0; + /** + Returns if the master device is muted or not. If the device is not + available in this mixer, it is reported as muted. + */ + virtual bool masterMute()=0; + + /** + Returns the index of the master device + */ + virtual int masterDeviceIndex()=0; + + /** + Makes the given device a record source. + */ + virtual void setRecordSource( int deviceidx, bool on )=0; + + /** + Returns if the given device is a record source. + */ + virtual bool isRecordSource( int deviceidx )=0; + + /** + Sets the balance of the master device (negative means balanced to the left + speaker and positive to the right one) + */ + virtual void setBalance( int balance )=0; + + /** + Returns true if the given device is available in the current mixer + and false if it's not. + */ + virtual bool isAvailableDevice( int deviceidx )=0; + + /** + Returns the name of the mixer. + */ + virtual QString mixerName()=0; + + /** + * Open/grab the mixer for further intraction + * You should use this method after a prior call of close(). See close() for usage directions. + */ + virtual int open()=0; + + /** + * Close/release the mixer + * This method SHOULD NOT be used via DCOP. You MAY use it if you really need to close the mixer device, + * for example when a software suspend action (ACPI) takes place, and the soundcard driver + * doesn't handle this situation gracefully. + */ + virtual int close()=0; + +}; + +#endif diff --git a/kmix/mixer_alsa.h b/kmix/mixer_alsa.h new file mode 100644 index 00000000..da04e372 --- /dev/null +++ b/kmix/mixer_alsa.h @@ -0,0 +1,53 @@ +#ifndef MIXER_ALSA_H +#define MIXER_ALSA_H + +// QT includes +#include <qvaluelist.h> + +// Forward QT includes +class QString; +class QSocketNotifier; + +#include "mixer_backend.h" + +class Mixer_ALSA : public Mixer_Backend +{ + public: + Mixer_ALSA( int device = -1 ); + ~Mixer_ALSA(); + + virtual int readVolumeFromHW( int devnum, Volume &vol ); + virtual int writeVolumeToHW( int devnum, Volume &vol ); + virtual bool setRecsrcHW( int devnum, bool on); + virtual bool isRecsrcHW( int devnum ); + virtual void setEnumIdHW(int mixerIdx, unsigned int); + virtual unsigned int enumIdHW(int mixerIdx); + virtual bool prepareUpdateFromHW(); + virtual bool needsPolling() { return false; } + virtual void prepareSignalling( Mixer *mixer ); + + protected: + virtual int open(); + virtual int close(); + + private: + int identify( snd_mixer_selem_id_t *sid ); + snd_mixer_elem_t* getMixerElem(int devnum); + void removeSignalling(); + + virtual QString errorText(int mixer_error); + typedef QValueList<snd_mixer_selem_id_t *>AlsaMixerSidList; + AlsaMixerSidList mixer_sid_list; + typedef QValueList<snd_mixer_elem_t *> AlsaMixerElemList; // !! remove + AlsaMixerElemList mixer_elem_list; // !! remove + + bool _initialUpdate; + snd_mixer_t *_handle; + QString devName; + struct pollfd *m_fds; + QSocketNotifier **m_sns; + int m_count; + +}; + +#endif diff --git a/kmix/mixer_alsa9.cpp b/kmix/mixer_alsa9.cpp new file mode 100644 index 00000000..e55f6112 --- /dev/null +++ b/kmix/mixer_alsa9.cpp @@ -0,0 +1,828 @@ +/* + * KMix -- KDE's full featured mini mixer + * Alsa 0.9x and 1.0 - Based on original alsamixer code + * from alsa-project ( www/alsa-project.org ) + * + * + * Copyright (C) 2002 Helio Chissini de Castro <helio@conectiva.com.br> + * 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// STD Headers +#include <stdlib.h> +#include <stdio.h> +#include <iostream> +#include <assert.h> +#include <qsocketnotifier.h> + +extern "C" +{ + #include <alsa/asoundlib.h> +} + +// KDE Headers +#include <klocale.h> +#include <kdebug.h> + +// Local Headers +#include "mixer_alsa.h" +//#include "mixer.h" +#include "volume.h" +// #define if you want MUCH debugging output +//#define ALSA_SWITCH_DEBUG +//#define KMIX_ALSA_VOLUME_DEBUG + +Mixer_Backend* +ALSA_getMixer( int device ) +{ + Mixer_Backend *l_mixer; + l_mixer = new Mixer_ALSA( device ); + return l_mixer; +} + +Mixer_ALSA::Mixer_ALSA( int device ) : Mixer_Backend( device ) +{ + m_fds = 0; + m_sns = 0; + _handle = 0; + _initialUpdate = true; +} + +Mixer_ALSA::~Mixer_ALSA() +{ + close(); +} + +int +Mixer_ALSA::identify( snd_mixer_selem_id_t *sid ) +{ + QString name = snd_mixer_selem_id_get_name( sid ); + + if ( name == "Master" ) return MixDevice::VOLUME; + if ( name == "Capture" ) return MixDevice::RECMONITOR; + if ( name == "Master Mono" ) return MixDevice::VOLUME; + if ( name == "PC Speaker" ) return MixDevice::VOLUME; + if ( name == "Music" || name == "Synth" || name == "FM" ) return MixDevice::MIDI; + if ( name.find( "Headphone", 0, false ) != -1 ) return MixDevice::HEADPHONE; + if ( name == "Bass" ) return MixDevice::BASS; + if ( name == "Treble" ) return MixDevice::TREBLE; + if ( name == "CD" ) return MixDevice::CD; + if ( name == "Video" ) return MixDevice::VIDEO; + if ( name == "PCM" || name == "Wave" ) return MixDevice::AUDIO; + if ( name == "Surround" ) return MixDevice::SURROUND_BACK; + if ( name == "Center" ) return MixDevice::SURROUND_CENTERFRONT; + if ( name.find( "ac97", 0, false ) != -1 ) return MixDevice::AC97; + if ( name.find( "coaxial", 0, false ) != -1 ) return MixDevice::DIGITAL; + if ( name.find( "optical", 0, false ) != -1 ) return MixDevice::DIGITAL; + if ( name.find( "IEC958", 0, false ) != -1 ) return MixDevice::DIGITAL; + if ( name.find( "Mic" ) != -1 ) return MixDevice::MICROPHONE; + if ( name.find( "LFE" ) != -1 ) return MixDevice::SURROUND_LFE; + if ( name.find( "Monitor" ) != -1 ) return MixDevice::RECMONITOR; + if ( name.find( "3D", 0, false ) != -1 ) return MixDevice::SURROUND; // Should be probably some own icon + + return MixDevice::EXTERNAL; +} + +int +Mixer_ALSA::open() +{ + bool virginOpen = m_mixDevices.isEmpty(); + bool validDevice = false; + bool masterChosen = false; + int err; + + snd_ctl_t *ctl_handle; + snd_ctl_card_info_t *hw_info; + snd_ctl_card_info_alloca(&hw_info); + + snd_mixer_elem_t *elem; + snd_mixer_selem_id_t *sid; + snd_mixer_selem_id_alloca( &sid ); + + // Card information + if( m_devnum == -1 ) + m_devnum = 0; + if ( (unsigned)m_devnum > 31 ) + devName = "default"; + else + devName = QString( "hw:%1" ).arg( m_devnum ); + + QString probeMessage; + + if (virginOpen) + probeMessage += "Trying ALSA Device '" + devName + "': "; + + if ( ( err = snd_ctl_open ( &ctl_handle, devName.latin1(), 0 ) ) < 0 ) + { + kdDebug(67100) << probeMessage << "not found: snd_ctl_open err=" << snd_strerror(err) << endl; + //_stateMessage = errorText( Mixer::ERR_NODEV ); + return Mixer::ERR_OPEN; + } + + if ( ( err = snd_ctl_card_info ( ctl_handle, hw_info ) ) < 0 ) + { + kdDebug(67100) << probeMessage << "not found: snd_ctl_card_info err=" << snd_strerror(err) << endl; + //_stateMessage = errorText( Mixer::ERR_READ ); + snd_ctl_close( ctl_handle ); + return Mixer::ERR_READ; + } + + // Device and mixer names + const char* mixer_card_name = snd_ctl_card_info_get_name( hw_info ); + //mixer_device_name = snd_ctl_card_info_get_mixername( hw_info ); + // Copy the name of kmix mixer from card name (mixername is rumoured to be not that good) + m_mixerName = mixer_card_name; + + snd_ctl_close( ctl_handle ); + + /* open mixer device */ + + //kdDebug(67100) << "IN Mixer_ALSA snd_mixer_open()" << endl; + if ( ( err = snd_mixer_open ( &_handle, 0 ) ) < 0 ) + { + kdDebug(67100) << probeMessage << "not found: snd_mixer_open err=" << snd_strerror(err) << endl; + //errormsg( Mixer::ERR_NODEV ); + _handle = 0; + return Mixer::ERR_NODEV; // if we cannot open the mixer, we have no devices + } + //kdDebug(67100) << "OUT Mixer_ALSA snd_mixer_open()" << endl; + + if ( ( err = snd_mixer_attach ( _handle, devName.latin1() ) ) < 0 ) + { + kdDebug(67100) << probeMessage << "not found: snd_mixer_attach err=" << snd_strerror(err) << endl; + //errormsg( Mixer::ERR_PERM ); + return Mixer::ERR_OPEN; + } + + if ( ( err = snd_mixer_selem_register ( _handle, NULL, NULL ) ) < 0 ) + { + kdDebug(67100) << probeMessage << "not found: snd_mixer_selem_register err=" << snd_strerror(err) << endl; + //errormsg( Mixer::ERR_READ ); + return Mixer::ERR_READ; + } + + if ( ( err = snd_mixer_load ( _handle ) ) < 0 ) + { + kdDebug(67100) << probeMessage << "not found: snd_mixer_load err=" << snd_strerror(err) << endl; + //errormsg( Mixer::ERR_READ ); + close(); + return Mixer::ERR_READ; + } + + kdDebug(67100) << probeMessage << "found" << endl; + + unsigned int mixerIdx = 0; + for ( elem = snd_mixer_first_elem( _handle ); elem; elem = snd_mixer_elem_next( elem ), mixerIdx++ ) + { + // If element is not active, just skip + if ( ! snd_mixer_selem_is_active ( elem ) ) { + // ...but we still want to insert a null value into our mixer element + // list so that the list indexes match up. + mixer_elem_list.append( 0 ); + mixer_sid_list.append( 0 ); + continue; + } + + + sid = (snd_mixer_selem_id_t*)malloc(snd_mixer_selem_id_sizeof()); // I believe *we* must malloc it for ourself + snd_mixer_selem_get_id( elem, sid ); + + bool canRecord = false; + bool canMute = false; + bool canCapture = false; + long maxVolumePlay= 0, minVolumePlay= 0; + long maxVolumeRec = 0, minVolumeRec = 0; + validDevice = true; + + snd_mixer_selem_get_playback_volume_range( elem, &minVolumePlay, &maxVolumePlay ); + snd_mixer_selem_get_capture_volume_range( elem, &minVolumeRec , &maxVolumeRec ); + // New mix device + MixDevice::ChannelType ct = (MixDevice::ChannelType)identify( sid ); +/* + if (!masterChosen && ct==MixDevice::VOLUME) { + // Determine a nicer MasterVolume + m_masterDevice = mixerIdx; + masterChosen = true; + } +*/ + if( virginOpen ) + { + MixDevice::DeviceCategory cc = MixDevice::UNDEFINED; + + //kdDebug() << "--- Loop: name=" << snd_mixer_selem_id_get_name( sid ) << " , mixerIdx=" << mixerIdx << "------------" << endl; + + Volume* volPlay = 0, *volCapture = 0; + QPtrList<QString> enumList; + if ( snd_mixer_selem_is_enumerated(elem) ) { + cc = MixDevice::ENUM; + volPlay = new Volume(); // Dummy, unused + volCapture = new Volume(); + mixer_elem_list.append( elem ); + mixer_sid_list.append( sid ); + + // --- get Enum names START --- + int numEnumitems = snd_mixer_selem_get_enum_items(elem); + if ( numEnumitems > 0 ) { + // OK. no error + for (int iEnum = 0; iEnum<numEnumitems; iEnum++ ) { + char buffer[100]; + int ret = snd_mixer_selem_get_enum_item_name(elem, iEnum, 99, buffer); + if ( ret == 0 ) { + QString* enumName = new QString(buffer); + //enumName->append(buffer); + enumList.append( enumName); + } // enumName could be read succesfully + } // for all enum items of this device + } // no error in reading enum list + else { + // 0 items or Error code => ignore this entry + } + // --- get Enum names END --- + } // is an enum + + else { + Volume::ChannelMask chn = Volume::MNONE; + Volume::ChannelMask chnTmp; + if ( snd_mixer_selem_has_playback_volume(elem) ) { + //kdDebug(67100) << "has_playback_volume()" << endl; + chnTmp = snd_mixer_selem_is_playback_mono ( elem ) + ? Volume::MLEFT : (Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT); + chn = (Volume::ChannelMask) (chn | chnTmp); + cc = MixDevice::SLIDER; + volPlay = new Volume( chn, maxVolumePlay, minVolumePlay ); + } else { + volPlay = new Volume(); + } + if ( snd_mixer_selem_has_capture_volume(elem) ) { + //kdDebug(67100) << "has_capture_volume()" << endl; + chnTmp = snd_mixer_selem_is_capture_mono( elem ) + ? Volume::MLEFT : (Volume::ChannelMask)(Volume::MLEFT | Volume::MRIGHT ); + chn = (Volume::ChannelMask) (chn | chnTmp); + cc = MixDevice::SLIDER; + canCapture = true; + volCapture = new Volume( chn, maxVolumeRec, minVolumeRec, true ); + } else { + volCapture = new Volume(); + } + + /* Create Volume object. If there is no volume on this device, + * it will be created with maxVolume == 0 && minVolume == 0 */ + mixer_elem_list.append( elem ); + mixer_sid_list.append( sid ); + + if ( snd_mixer_selem_has_playback_switch ( elem ) ) { + //kdDebug(67100) << "has_playback_switch()" << endl; + canMute = true; + } + if ( snd_mixer_selem_has_capture_switch ( elem ) ) { + //kdDebug(67100) << "has_capture_switch()" << endl; + canRecord = true; + } + if ( snd_mixer_selem_has_common_switch ( elem ) ) { + //kdDebug(67100) << "has_common_switch()" << endl; + canMute = true; + canRecord = true; + } + + if ( /*snd_mixer_selem_has_common_switch ( elem ) || */ + cc == MixDevice::UNDEFINED ) + { + // Everything unknown is handled as switch + cc = MixDevice::SWITCH; + } + } // is ordinary mixer element (NOT an enum) + + MixDevice* md = new MixDevice( mixerIdx, + *volPlay, + canRecord, + canMute, + snd_mixer_selem_id_get_name( sid ), + ct, + cc ); + + m_mixDevices.append( md ); + + + if (!masterChosen && ct==MixDevice::VOLUME) { + // Determine a nicer MasterVolume + m_recommendedMaster = md; + masterChosen = true; + } + + if ( canCapture && !canRecord ) { + MixDevice *mdCapture = + new MixDevice( mixerIdx, + *volCapture, + true, + canMute, + snd_mixer_selem_id_get_name( sid ), + ct, + cc ); + m_mixDevices.append( mdCapture ); + } + + if ( enumList.count() > 0 ) { + int maxEnumId= enumList.count(); + QPtrList<QString>& enumValuesRef = md->enumValues(); // retrieve a ref + for (int i=0; i<maxEnumId; i++ ) { + // we have an enum. Lets set the names of the enum items in the MixDevice + // the enum names are assumed to be static! + enumValuesRef.append(enumList.at(i) ); + } + } + //kdDebug(67100) << "ALSA create MDW, vol= " << *vol << endl; + delete volPlay; + delete volCapture; + } // virginOpen + else + { + MixDevice* md; + bool found = false; + for ( md = m_mixDevices.first(); md != 0; md = m_mixDevices.next() ) { + if ( md->num() == mixerIdx ) { + found = true; + writeVolumeToHW( mixerIdx, md->getVolume() ); + } + } + if( !found ) + { + return Mixer::ERR_INCOMPATIBLESET; + } + } // !virginOpen + } // for all elems + + /************************************************************************************** + // If no devices are supported by this soundcard, return "NO Devices" + It is VERY important to return THIS error code, so that the caller knows, that the + the device exists. + This is used for scanning for existing soundcard devices, see MixerToolBox::initMixer(). + ***************************************************************************************/ + if ( !validDevice ) + { + return Mixer::ERR_NODEV; + } + + // Copy the name of kmix mixer from card name + // Real name of mixer is not too good + m_mixerName = mixer_card_name; + + // return with success + m_isOpen = true; + + /* setup for select on stdin and the mixer fd */ + if ((m_count = snd_mixer_poll_descriptors_count(_handle)) < 0) { + kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << m_count << "\n"; + return Mixer::ERR_OPEN; + } + + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 2\n"; + + m_fds = (struct pollfd*)calloc(m_count, sizeof(struct pollfd)); + if (m_fds == NULL) { + kdDebug(67100) << "Mixer_ALSA::poll() , calloc() = null" << "\n"; + return Mixer::ERR_OPEN; + } + + m_fds->events = POLLIN; + if ((err = snd_mixer_poll_descriptors(_handle, m_fds, m_count)) < 0) { + kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << "\n"; + return Mixer::ERR_OPEN; + } + if (err != m_count) { + kdDebug(67100) << "Mixer_ALSA::poll() , snd_mixer_poll_descriptors_count() err=" << err << " m_count=" << m_count << "\n"; + return Mixer::ERR_OPEN; + } + + return 0; +} + +void Mixer_ALSA::prepareSignalling( Mixer *mixer ) +{ + assert( !m_sns ); + + m_sns = new QSocketNotifier*[m_count]; + for ( int i = 0; i < m_count; ++i ) + { + kdDebug() << "socket " << i << endl; + m_sns[i] = new QSocketNotifier(m_fds[i].fd, QSocketNotifier::Read); + mixer->connect(m_sns[i], SIGNAL(activated(int)), mixer, SLOT(readSetFromHW())); + } +} + +void Mixer_ALSA::removeSignalling() +{ + if ( m_fds ) + free( m_fds ); + m_fds = 0; + + if ( m_sns ) + { + for ( int i = 0; i < m_count; i++ ) + delete m_sns[i]; + delete [] m_sns; + m_sns = 0; + } +} + +int +Mixer_ALSA::close() +{ + int ret=0; + m_isOpen = false; + if ( _handle != 0 ) + { + //kdDebug(67100) << "IN Mixer_ALSA::close()" << endl; + snd_mixer_free ( _handle ); + if ( ( ret = snd_mixer_detach ( _handle, devName.latin1() ) ) < 0 ) + { + kdDebug(67100) << "snd_mixer_detach err=" << snd_strerror(ret) << endl; + } + int ret2 = 0; + if ( ( ret2 = snd_mixer_close ( _handle ) ) < 0 ) + { + kdDebug(67100) << "snd_mixer_close err=" << snd_strerror(ret2) << endl; + if ( ret == 0 ) ret = ret2; // no error before => use current error code + } + + _handle = 0; + //kdDebug(67100) << "OUT Mixer_ALSA::close()" << endl; + + } + + mixer_elem_list.clear(); + mixer_sid_list.clear(); + m_mixDevices.clear(); + + removeSignalling(); + + return ret; +} + + +snd_mixer_elem_t* Mixer_ALSA::getMixerElem(int devnum) { + snd_mixer_elem_t* elem = 0; + if ( ! m_isOpen ) return elem; // unplugging guard + + if ( int( mixer_sid_list.count() ) > devnum ) { + snd_mixer_selem_id_t * sid = mixer_sid_list[ devnum ]; + // The next line (hopefully) only finds selem's, not elem's. + elem = snd_mixer_find_selem(_handle, sid); + + if ( elem == 0 ) { + // !! Check, whether the warning should be omitted. Probably + // Route controls are non-simple elements. + kdDebug(67100) << "Error finding mixer element " << devnum << endl; + } + } + return elem; + +/* + I would have liked to use the following trivial implementation instead of the + code above. But it will also return elem's. which are not selem's. As there is + no way to check an elem's type (e.g. elem->type == SND_MIXER_ELEM_SIMPLE), callers + of getMixerElem() cannot check the type. :-( + snd_mixer_elem_t* elem = mixer_elem_list[ devnum ]; + return elem; + */ +} + +bool Mixer_ALSA::prepareUpdateFromHW() +{ + if ( !m_fds || !m_isOpen ) + return false; + + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 1\n"; + + // Poll on fds with 10ms timeout + // Hint: alsamixer has an infinite timeout, but we cannot do this because we would block + // the X11 event handling (Qt event loop) with this. + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 3\n"; + int finished = poll(m_fds, m_count, 10); + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 4\n"; + + bool updated = false; + if (finished > 0) { + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 5\n"; + + unsigned short revents; + + if (snd_mixer_poll_descriptors_revents(_handle, m_fds, m_count, &revents) >= 0) { + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 6\n"; + + if (revents & POLLNVAL) { + /* Bug 127294 shows, that we receieve POLLNVAL when the user + unplugs an USB soundcard. Lets close the card. */ + kdDebug(67100) << "Mixer_ALSA::poll() , Error: poll() returns POLLNVAL\n"; + close(); // Card was unplugged (unplug, driver unloaded) + return false; + } + if (revents & POLLERR) { + kdDebug(67100) << "Mixer_ALSA::poll() , Error: poll() returns POLLERR\n"; + return false; + } + if (revents & POLLIN) { + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() 7\n"; + + snd_mixer_handle_events(_handle); + updated = true; + } + } + + } + //kdDebug(67100) << "Mixer_ALSA::prepareUpdate() " << updated << endl;; + return updated; +} + +bool +Mixer_ALSA::isRecsrcHW( int devnum ) +{ + bool isCurrentlyRecSrc = false; + snd_mixer_elem_t *elem = getMixerElem( devnum ); + + if ( !elem ) { + return false; + } + + if ( snd_mixer_selem_has_capture_switch( elem ) ) { + // Has a on-off switch + // Yes, this element can be record source. But the user can switch it off, so lets see if it is switched on. + int swLeft; + int ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &swLeft ); + if ( ret != 0 ) { + kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 1\n"; + } + + if (snd_mixer_selem_has_capture_switch_joined( elem ) ) { + isCurrentlyRecSrc = (swLeft != 0); +#ifdef ALSA_SWITCH_DEBUG + kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_switch joined: #" << devnum << " >>> " << swLeft << " : " << isCurrentlyRecSrc << endl; +#endif + } + else { + int swRight; + snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_RIGHT, &swRight ); + isCurrentlyRecSrc = ( (swLeft != 0) || (swRight != 0) ); +#ifdef ALSA_SWITCH_DEBUG + kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_switch non-joined, state " << isCurrentlyRecSrc << endl; +#endif + } + } + else { + // Has no on-off switch + if ( snd_mixer_selem_has_capture_volume( elem ) ) { + // Has a volume, but has no OnOffSwitch => We assume that this is a fixed record source (always on). (esken) + isCurrentlyRecSrc = true; +#ifdef ALSA_SWITCH_DEBUG + kdDebug(67100) << "Mixer_ALSA::isRecsrcHW() has_no_switch, state " << isCurrentlyRecSrc << endl; +#endif + } + } + + return isCurrentlyRecSrc; +} + +bool +Mixer_ALSA::setRecsrcHW( int devnum, bool on ) +{ + int sw = 0; + if (on) + sw = !sw; + + snd_mixer_elem_t *elem = getMixerElem( devnum ); + if ( !elem ) + { + return 0; + } + + if (snd_mixer_selem_has_capture_switch_joined( elem ) ) + { + int before, after; + int ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &before ); + if ( ret != 0 ) { + kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 1\n"; + } + + ret = snd_mixer_selem_set_capture_switch_all( elem, sw ); + if ( ret != 0 ) { + kdDebug(67100) << "snd_mixer_selem_set_capture_switch_all() failed 2: errno=" << ret << "\n"; + } + ret = snd_mixer_selem_get_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &after ); + if ( ret != 0 ) { + kdDebug(67100) << "snd_mixer_selem_get_capture_switch() failed 3: errno=" << ret << "\n"; + } + +#ifdef ALSA_SWITCH_DEBUG + kdDebug(67100) << "Mixer_ALSA::setRecsrcHW(" << devnum << "," << on << ")joined. Before=" << before << " Set=" << sw << " After=" << after <<"\n"; +#endif + + } + else + { +#ifdef ALSA_SWITCH_DEBUG + kdDebug(67100) << "Mixer_ALSA::setRecsrcHW LEFT\n"; +#endif + snd_mixer_selem_set_capture_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, sw ); +#ifdef ALSA_SWITCH_DEBUG + kdDebug(67100) << "Mixer_ALSA::setRecsrcHW RIGHT\n"; +#endif + snd_mixer_selem_set_capture_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, sw); + } + +#ifdef ALSA_SWITCH_DEBUG + kdDebug(67100) << "EXIT Mixer_ALSA::setRecsrcHW(" << devnum << "," << on << ")\n"; +#endif + return false; // we should always return false, so that other devnum's get updated + // The ALSA polling has been implemented some time ago. So it should be safe to + // return "true" here. + // The other devnum's Rec-Sources won't get update by KMix code, but ALSA will send + // us an event, if neccesary. But OTOH it is possibly better not to trust alsalib fully, + // because the old code is working also well (just takes more processing time). + // return true; +} + +/** + * Sets the ID of the currently selected Enum entry. + * Warning: ALSA supports to have different enums selected on each channel + * of the SAME snd_mixer_elem_t. KMix does NOT support that and + * always sets both channels (0 and 1). + */ +void Mixer_ALSA::setEnumIdHW(int mixerIdx, unsigned int idx) { + //kdDebug(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << ", idx=" << idx << ") 1\n"; + snd_mixer_elem_t *elem = getMixerElem( mixerIdx ); + if ( elem==0 || ( !snd_mixer_selem_is_enumerated(elem)) ) + { + return; + } + + //kdDebug(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << ", idx=" << idx << ") 2\n"; + int ret = snd_mixer_selem_set_enum_item(elem,SND_MIXER_SCHN_FRONT_LEFT,idx); + if (ret < 0) { + kdError(67100) << "Mixer_ALSA::setEnumIdHW(" << mixerIdx << "), errno=" << ret << "\n"; + } + snd_mixer_selem_set_enum_item(elem,SND_MIXER_SCHN_FRONT_RIGHT,idx); + // we don't care about possible error codes on channel 1 + return; +} + +/** + * Return the ID of the currently selected Enum entry. + * Warning: ALSA supports to have different enums selected on each channel + * of the SAME snd_mixer_elem_t. KMix does NOT support that and + * always shows the value of the first channel. + */ +unsigned int Mixer_ALSA::enumIdHW(int mixerIdx) { + snd_mixer_elem_t *elem = getMixerElem( mixerIdx ); + if ( elem==0 || ( !snd_mixer_selem_is_enumerated(elem)) ) + { + return 0; + } + + unsigned int idx = 0; + int ret = snd_mixer_selem_get_enum_item(elem,SND_MIXER_SCHN_FRONT_LEFT,&idx); + //kdDebug(67100) << "Mixer_ALSA::enumIdHW(" << mixerIdx << ") idx=" << idx << "\n"; + if (ret < 0) { + idx = 0; + kdError(67100) << "Mixer_ALSA::enumIdHW(" << mixerIdx << "), errno=" << ret << "\n"; + } + return idx; +} + + +int +Mixer_ALSA::readVolumeFromHW( int mixerIdx, Volume &volume ) +{ + int elem_sw; + long left, right; + + snd_mixer_elem_t *elem = getMixerElem( mixerIdx ); + if ( !elem ) + { + return 0; + } + + + // *** READ PLAYBACK VOLUMES ************* + if ( snd_mixer_selem_has_playback_volume( elem ) && !volume.isCapture() ) + { + int ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_LEFT, &left ); + if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_playback_volume,R] failed, errno=" << ret << endl; + if ( snd_mixer_selem_is_playback_mono ( elem )) { + volume.setVolume( Volume::LEFT , left ); + volume.setVolume( Volume::RIGHT, left ); + } + else { + int ret = snd_mixer_selem_get_playback_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT, &right ); + if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_playback_volume,R] failed, errno=" << ret << endl; + volume.setVolume( Volume::LEFT , left ); + volume.setVolume( Volume::RIGHT, right ); + } + } + else + if ( snd_mixer_selem_has_capture_volume ( elem ) && volume.isCapture() ) + { + int ret = snd_mixer_selem_get_capture_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, &left ); + if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [get_capture_volume,L] failed, errno=" << ret << endl; + if ( snd_mixer_selem_is_capture_mono ( elem )) { + volume.setVolume( Volume::LEFT , left ); + volume.setVolume( Volume::RIGHT, left ); + } + else + { + int ret = snd_mixer_selem_get_capture_volume( elem, SND_MIXER_SCHN_FRONT_RIGHT, &right ); + if ( ret != 0 ) kdDebug(67100) << "readVolumeFromHW(" << mixerIdx << ") [has_capture_volume,R] failed, errno=" << ret << endl; + volume.setVolume( Volume::LEFT , left ); + volume.setVolume( Volume::RIGHT, right ); + } + } + + //kdDebug() << "snd_mixer_selem_has_playback_volume " << mixerIdx << " " << snd_mixer_selem_has_playback_switch( elem ) << endl; + if ( snd_mixer_selem_has_playback_switch( elem ) ) + { + snd_mixer_selem_get_playback_switch( elem, SND_MIXER_SCHN_FRONT_LEFT, &elem_sw ); + volume.setMuted( elem_sw == 0 ); + } + + return 0; +} + +int +Mixer_ALSA::writeVolumeToHW( int devnum, Volume& volume ) +{ + int left, right; + + snd_mixer_elem_t *elem = getMixerElem( devnum ); + if ( !elem ) + { + return 0; + } + + // --- VOLUME - WE HAVE JUST ONE TYPE OF VOLUME A TIME, + // CAPTURE OR PLAYBACK, SO IT"S JUST USE VOLUME ------------ + left = volume[ Volume::LEFT ]; + right = volume[ Volume::RIGHT ]; + + if (snd_mixer_selem_has_playback_volume( elem ) && !volume.isCapture() ) { + snd_mixer_selem_set_playback_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, left ); + if ( ! snd_mixer_selem_is_playback_mono ( elem ) ) + snd_mixer_selem_set_playback_volume ( elem, SND_MIXER_SCHN_FRONT_RIGHT, right ); + } + else if ( snd_mixer_selem_has_capture_volume( elem ) && volume.isCapture() ) { + snd_mixer_selem_set_capture_volume ( elem, SND_MIXER_SCHN_FRONT_LEFT, left ); + if ( ! snd_mixer_selem_is_playback_mono ( elem ) ) + snd_mixer_selem_set_capture_volume ( elem, SND_MIXER_SCHN_FRONT_RIGHT, right ); + } + + if ( snd_mixer_selem_has_playback_switch( elem ) ) + { + int sw = 0; + if (! volume.isMuted()) + sw = !sw; + snd_mixer_selem_set_playback_switch_all(elem, sw); + } + + return 0; +} + +QString +Mixer_ALSA::errorText( int mixer_error ) +{ + QString l_s_errmsg; + switch ( mixer_error ) + { + case Mixer::ERR_PERM: + l_s_errmsg = i18n("You do not have permission to access the alsa mixer device.\n" \ + "Please verify if all alsa devices are properly created."); + break; + case Mixer::ERR_OPEN: + l_s_errmsg = i18n("Alsa mixer cannot be found.\n" \ + "Please check that the soundcard is installed and the\n" \ + "soundcard driver is loaded.\n" ); + break; + default: + l_s_errmsg = Mixer_Backend::errorText( mixer_error ); + } + return l_s_errmsg; +} + + +QString +ALSA_getDriverName() +{ + return "ALSA"; +} + + diff --git a/kmix/mixer_backend.cpp b/kmix/mixer_backend.cpp new file mode 100644 index 00000000..2eb61089 --- /dev/null +++ b/kmix/mixer_backend.cpp @@ -0,0 +1,147 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <klocale.h> + +#include "mixer_backend.h" +// for the "ERR_" declartions, #include mixer.h +#include "mixer.h" + +Mixer_Backend::Mixer_Backend(int device) : + m_devnum (device) , m_isOpen(false), m_recommendedMaster(0) +{ + m_mixDevices.setAutoDelete( true ); +} + +Mixer_Backend::~Mixer_Backend() +{ +} + + +bool Mixer_Backend::isValid() { + bool valid = false; + int ret = open(); + if ( ret == 0 && m_mixDevices.count() > 0) { + valid = true; + } + close(); + return valid; +} + +bool Mixer_Backend::isOpen() { + return m_isOpen; +} + +/** + * Queries the backend driver whether there are new changes in any of the controls. + * If you cannot find out for a backend, return "true" - this is also the default implementation. + * @return true, if there are changes. Otherwise false is returned. + */ +bool Mixer_Backend::prepareUpdateFromHW() { + return true; +} + +/** + * Return the MixDevice, that would qualify best as MasterDevice. The default is to return the + * first device in the device list. Backends can override this (i.e. the ALSA Backend does so). + * The users preference is NOT returned by this method - see the Mixer class for that. + */ +MixDevice* Mixer_Backend::recommendedMaster() { + MixDevice* recommendedMixDevice = 0; + if ( m_recommendedMaster != 0 ) { + recommendedMixDevice = m_recommendedMaster; + } // recommendation from Backend + else { + if ( m_mixDevices.count() > 0 ) { + recommendedMixDevice = m_mixDevices.at(0); + } //first device (if exists) + } + return recommendedMixDevice; +} + +/** + * Sets the ID of the currently selected Enum entry. + * This is a dummy implementation - if the Mixer backend + * wants to support it, it must implement the driver specific + * code in its subclass (see Mixer_ALSA.cpp for an example). + */ +void Mixer_Backend::setEnumIdHW(int, unsigned int) { + return; +} + +/** + * Return the ID of the currently selected Enum entry. + * This is a dummy implementation - if the Mixer backend + * wants to support it, it must implement the driver specific + * code in its subclass (see Mixer_ALSA.cpp for an example). + */ +unsigned int Mixer_Backend::enumIdHW(int) { + return 0; +} + +void Mixer_Backend::errormsg(int mixer_error) +{ + QString l_s_errText; + l_s_errText = errorText(mixer_error); + kdError() << l_s_errText << "\n"; +} + +QString Mixer_Backend::errorText(int mixer_error) +{ + QString l_s_errmsg; + switch (mixer_error) + { + case Mixer::ERR_PERM: + l_s_errmsg = i18n("kmix:You do not have permission to access the mixer device.\n" \ + "Please check your operating systems manual to allow the access."); + break; + case Mixer::ERR_WRITE: + l_s_errmsg = i18n("kmix: Could not write to mixer."); + break; + case Mixer::ERR_READ: + l_s_errmsg = i18n("kmix: Could not read from mixer."); + break; + case Mixer::ERR_NODEV: + l_s_errmsg = i18n("kmix: Your mixer does not control any devices."); + break; + case Mixer::ERR_NOTSUPP: + l_s_errmsg = i18n("kmix: Mixer does not support your platform. See mixer.cpp for porting hints (PORTING)."); + break; + case Mixer::ERR_NOMEM: + l_s_errmsg = i18n("kmix: Not enough memory."); + break; + case Mixer::ERR_OPEN: + case Mixer::ERR_MIXEROPEN: + // ERR_MIXEROPEN means: Soundcard could be opened, but has no mixer. ERR_MIXEROPEN is normally never + // passed to the errorText() method, because KMix handles that case explicitely + l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \ + "Please check that the soundcard is installed and that\n" \ + "the soundcard driver is loaded.\n"); + break; + case Mixer::ERR_INCOMPATIBLESET: + l_s_errmsg = i18n("kmix: Initial set is incompatible.\n" \ + "Using a default set.\n"); + break; + default: + l_s_errmsg = i18n("kmix: Unknown error. Please report how you produced this error."); + break; + } + return l_s_errmsg; +} + diff --git a/kmix/mixer_backend.h b/kmix/mixer_backend.h new file mode 100644 index 00000000..8132ea04 --- /dev/null +++ b/kmix/mixer_backend.h @@ -0,0 +1,104 @@ +//-*-C++-*- +/* + * KMix -- KDE's full featured mini mixer + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MIXER_BACKEND_H +#define MIXER_BACKEND_H + +#include "mixer.h" + +class Mixer_Backend +{ +// The Mixer Backend's may only be accessed from the Mixer class. +friend class Mixer; + +protected: + Mixer_Backend(int devnum); + virtual ~Mixer_Backend(); + + /// Derived classes MUST implement this to open the mixer. Returns a KMix error code (O=OK). + virtual int open() = 0; + virtual int close() = 0; + + /** Returns, whether this Mixer object contains a valid Mixer. You should return "false", when + * the Mixer with the devnum given in the constructor is not supported by the Backend. The two + * typical cases are: + * (1) No such hardware installed + * (2) The hardware exists, but has no mixer support (e.g. external soundcard with only mechanical volume knobs) + * The default implementation calls open(), checks the return code and whether the number of + * supported channels is > 0. Then it calls close(). + * You should reimplement this method in your backend, when there is a less time-consuming method than + * calling open() and close() for checking the existance of a Mixer. + */ + virtual bool isValid(); + + /** @return true, if the Mixer is open (and thus can be operated) */ + bool isOpen(); + + virtual bool prepareUpdateFromHW(); + + /// Volume Read + virtual int readVolumeFromHW( int devnum, Volume &vol ) = 0; + /// Volume Write + virtual int writeVolumeToHW( int devnum, Volume &volume ) = 0; + + /// Enums + virtual void setEnumIdHW(int mixerIdx, unsigned int); + virtual unsigned int enumIdHW(int mixerIdx); + + /// Recording Switches + virtual bool setRecsrcHW( int devnum, bool on) = 0; + virtual bool isRecsrcHW( int devnum ) = 0; + + /// Overwrite in the backend if the backend can see changes without polling + virtual bool needsPolling() { return true; } + + /** overwrite this if you need to connect to slots in the mixer (e.g. readSetFromHW) + this called in the very beginning and only if !needsPolling + */ + virtual void prepareSignalling( Mixer * ) {} + + MixDevice* recommendedMaster(); + + /** Return a translated error text for the given error number. + * Subclasses can override this method to produce platform + * specific error descriptions. + */ + virtual QString errorText(int mixer_error); + /// Prints out a translated error text for the given error number on stderr + void errormsg(int mixer_error); + + + int m_devnum; + /// User friendly name of the Mixer (e.g. "IRIX Audio Mixer"). If your mixer API + /// gives you a usable name, use that name. + QString m_mixerName; + // All mix devices of this phyisical device. + MixSet m_mixDevices; + + /****************************************************************************************** + * Please don't access the next vars from the Mixer class (even though Mixer is a friend). + * There are proper accesor methods for them. + ******************************************************************************************/ + bool m_isOpen; + // The MixDevice that would qualify best as MasterDevice (according to the taste of the Backend developer) + MixDevice* m_recommendedMaster; +}; + +#endif diff --git a/kmix/mixer_hpux.cpp b/kmix/mixer_hpux.cpp new file mode 100644 index 00000000..e7ca0a83 --- /dev/null +++ b/kmix/mixer_hpux.cpp @@ -0,0 +1,257 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2000 Christian Esken + * esken@kde.org + * + * HP/UX-Port: Copyright (C) 1999 by Helge Deller + * deller@gmx.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "mixer_hpux.h" + +#warning "HP/UX mixer (maybe) doesn't work yet !" + +#define HPUX_ERROR_OFFSET 1024 + +#define myGain AUnityGain /* AUnityGain or AZeroGain */ + +#define GAIN_OUT_DIFF ((long) ((int)aMaxOutputGain(audio) - (int)aMinOutputGain(audio))) +#define GAIN_OUT_MIN ((long) aMinOutputGain(audio)) +#define GAIN_IN_DIFF ((long) ((int)aMaxInputGain(audio) - (int)aMinInputGain(audio))) +#define GAIN_IN_MIN ((long) aMinOutputGain(audio)) + +/* standard */ +#define ID_PCM 4 + +/* AInputSrcType: */ /*OSS:*/ +#define ID_IN_MICROPHONE 7 /* AISTMonoMicrophone */ +#define ID_IN_AUX 6 /* AISTLeftAuxiliary, AISTRightAuxiliary */ + +/* AOutputDstType: */ +#define ID_OUT_INT_SPEAKER 0 /* AODTMonoIntSpeaker */ + +/* not yet implemented: + AODTLeftJack, AODTRightJack, + AODTLeftLineOut, AODTRightLineOut, + AODTLeftHeadphone, AODTRightHeadphone + +const char* MixerDevNames[32]={"Volume" , "Bass" , "Treble" , "Synth" , "Pcm" , \ + "Speaker" , "Line" , "Microphone", "CD" , "Mix" , \ + "Pcm2" , "RecMon" , "IGain" , "OGain" , "Line1", \ + "Line2" , "Line3" , "Digital1" , "Digital2", "Digital3", \ + "PhoneIn" , "PhoneOut", "Video" , "Radio" , "Monitor", \ + "3D-depth", "3D-center", "unknown" , "unknown" , "unknown", \ + "unknown" , "unused" }; +*/ + + + +Mixer_HPUX::Mixer_HPUX(int devnum) : Mixer_Backend(devnum) +{ + char ServerName[10]; + ServerName[0] = 0; + audio = AOpenAudio(ServerName,NULL); +} + +Mixer_HPUX::~Mixer_HPUX() +{ + if (audio) { + ACloseAudio(audio,0); + audio = 0; + } +} + + +int Mixer_HPUX::open() +{ + if (audio==0) { + return Mixer::ERR_OPEN; + } + else + { + /* Mixer is open. Now define properties */ + stereodevs = devmask = (1<<ID_PCM); /* activate pcm */ + recmask = 0; + + /* check Input devices... */ + if (AInputSources(audio) & AMonoMicrophoneMask) { + devmask |= (1<<ID_IN_MICROPHONE); + recmask |= (1<<ID_IN_MICROPHONE); + } + if (AInputSources(audio) & (ALeftAuxiliaryMask|ARightAuxiliaryMask)) { + devmask |= (1<<ID_IN_AUX); + recmask |= (1<<ID_IN_AUX); + stereodevs |= (1<<ID_IN_AUX); + } + + /* check Output devices... */ + if (AOutputDestinations(audio) & AMonoIntSpeakerMask) { + devmask |= (1<<ID_OUT_INT_SPEAKER); + stereodevs |= (1<<ID_OUT_INT_SPEAKER); + } + +/* implement later: + ---------------- + if (AOutputDestinations(audio) & AMonoLineOutMask) devmask |= 64; // Line + if (AOutputDestinations(audio) & AMonoJackMask) devmask |= (1<<14); // Line1 + if (AOutputDestinations(audio) & AMonoHeadphoneMask) devmask |= (1<<15); // Line2 +*/ + + MaxVolume = 255; + + long error = 0; + ASetSystemPlayGain(audio, myGain, &error); + if (error) errorText(error + HPUX_ERROR_OFFSET); + ASetSystemRecordGain(audio, myGain, &error); + if (error) errorText(error + HPUX_ERROR_OFFSET); + + i_recsrc = 0; + m_isOpen = true; + + m_mixerName = "HP Mixer"; /* AAudioString(audio); */ + return 0; + } +} + +int Mixer_HPUX::close() +{ + m_isOpen = false; + m_mixDevices.clear(); + return 0; +} + + +/* +void Mixer_HPUX::setDevNumName_I(int devnum) +{ + devname = "HP Mixer"; +} +*/ +bool Mixer_HPUX::setRecsrcHW( int devnum, bool on ) +{ + return FALSE; +} + +bool Mixer_HPUX::isRecsrcHW( int devnum ) +{ + return FALSE; +} + +int Mixer_HPUX::readVolumeFromHW( int devnum, Volume &vol ) +{ + long Gain; + long error = 0; + int vl,vr; + + switch (devnum) { + case ID_OUT_INT_SPEAKER: /* AODTMonoIntSpeaker */ + AGetSystemChannelGain(audio, ASGTPlay, ACTMono, &Gain, &error ); + vl = vr = (Gain-GAIN_OUT_MIN)*255 / GAIN_OUT_DIFF; + vol.setVolume( Volume::LEFT, vl); + vol.setVolume( Volume::RIGHT, vr); +printf("READ - Devnum: %d, Left: %d, Right: %d\n", devnum, vl, vr ); + break; + + case ID_IN_AUX: /* AISTLeftAuxiliary, AISTRightAuxiliary */ + case ID_IN_MICROPHONE: /* AISTMonoMicrophone */ + AGetSystemChannelGain(audio, ASGTRecord, ACTMono, &Gain, &error ); + vl = vr = (Gain-GAIN_IN_MIN)*255 / GAIN_IN_DIFF; + vol.setVolume( Volume::LEFT, vl); + vol.setVolume( Volume::RIGHT, vr); + break; + + default: + error = Mixer::ERR_NODEV - HPUX_ERROR_OFFSET; + break; + }; + + return (error ? (error+HPUX_ERROR_OFFSET) : 0); +} + +/* + ASystemGainType = ASGTPlay, ASGTRecord, ASGTMonitor + AChType = ACTMono, ACTLeft, ACTRight +*/ + +int Mixer_HPUX::writeVolumeToHW( int devnum, Volume& vol ) +{ + long Gain; + long error = 0; + int vl = vol.getVolume(Volume::LEFT); + int vr = vol.getVolume(Volume::RIGHT); + + switch (devnum) { + case ID_OUT_INT_SPEAKER: /* AODTMonoIntSpeaker */ +printf("WRITE - Devnum: %d, Left: %d, Right: %d\n", devnum, vl, vr); + Gain = vl; // only left Volume + Gain = (Gain*GAIN_OUT_DIFF) / 255 - GAIN_OUT_MIN; + ASetSystemChannelGain(audio, ASGTPlay, ACTMono, (AGainDB) Gain, &error ); + break; + + case ID_IN_MICROPHONE: /* AISTMonoMicrophone */ + Gain = vl; // only left Volume + Gain = (Gain*GAIN_IN_DIFF) / 255 - GAIN_IN_MIN; + ASetSystemChannelGain(audio, ASGTRecord, ACTMono, (AGainDB) Gain, &error ); + break; + + case ID_IN_AUX: /* AISTLeftAuxiliary, AISTRightAuxiliary */ + Gain = (vl*GAIN_IN_DIFF) / 255 - GAIN_IN_MIN; + ASetSystemChannelGain(audio, ASGTRecord, ACTLeft, (AGainDB) Gain, &error ); + Gain = (vr*GAIN_IN_DIFF) / 255 - GAIN_IN_MIN; + ASetSystemChannelGain(audio, ASGTRecord, ACTRight, (AGainDB) Gain, &error ); + break; + + default: + error = Mixer::ERR_NODEV - HPUX_ERROR_OFFSET; + break; + }; + return (error ? (error+HPUX_ERROR_OFFSET) : 0); +} + + +QString Mixer_HPUX::errorText(int mixer_error) +{ + QString l_s_errmsg; + if (mixer_error >= HPUX_ERROR_OFFSET) { + char errorstr[200]; + AGetErrorText(audio, (AError) (mixer_error-HPUX_ERROR_OFFSET), + errorstr, sizeof(errorstr)); + printf("kmix: %s: %s\n",mixerName().data(), errorstr); + l_s_errmsg = errorstr; + } else + switch (mixer_error) + { + case Mixer::ERR_OPEN: + // should use i18n... + l_s_errmsg = "kmix: HP-UX Alib-Mixer cannot be found.\n" \ + "Please check that you have:\n" \ + " 1. Installed the libAlib package and\n" \ + " 2. started the Aserver program from the /opt/audio/bin directory\n"; + break; + default: + l_s_errmsg = Mixer_Backend::errorText(mixer_error); + break; + } + return l_s_errmsg; +} + +QString HPUX_getDriverName() { + return "HPUX"; +} + diff --git a/kmix/mixer_hpux.h b/kmix/mixer_hpux.h new file mode 100644 index 00000000..bdc0d3d7 --- /dev/null +++ b/kmix/mixer_hpux.h @@ -0,0 +1,36 @@ +#ifndef MIXER_HPUX_H +#define MIXER_HPUX_H + +#define DEFAULT_MIXER "HP-UX Mixer" +#ifdef HAVE_ALIB_H +#include <Alib.h> +#define HPUX_MIXER +#endif + +#include "mixer_backend.h" + +class Mixer_HPUX : public Mixer_Backend +{ +public: + Mixer_HPUX(int devnum); + virtual ~Mixer_HPUX(); + + virtual QString errorText(int mixer_error); + + virtual int readVolumeFromHW( int devnum, Volume &vol ); + virtual int writeVolumeToHW( int devnum, Volume &vol ); + +protected: + virtual bool setRecsrcHW( int devnum, bool on = true ); + virtual bool isRecsrcHW( int devnum ); + + virtual int open(); + virtual int close(); + + Audio *audio; + unsigned int stereodevs,devmask, recmask, MaxVolume, i_recsrc; + + +}; + +#endif diff --git a/kmix/mixer_irix.cpp b/kmix/mixer_irix.cpp new file mode 100644 index 00000000..9c88b6f4 --- /dev/null +++ b/kmix/mixer_irix.cpp @@ -0,0 +1,133 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2000 Christian Esken + * esken@kde.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "mixer_irix.h" + +Mixer_Backend* IRIX_getMixer(int devnum) +{ + Mixer_Backend *l_mixer; + l_mixer = new Mixer_IRIX( devnum); + l_mixer->init(devnum); + return l_mixer; +} + + + +Mixer_IRIX::Mixer_IRIX(int devnum) : Mixer_Backend(devnum) +{ + close(); +} + +int Mixer_IRIX::open() +{ + // Create config + m_config = ALnewconfig(); + if (m_config == (ALconfig)0) { + cerr << "OpenAudioDevice(): ALnewconfig() failed\n"; + return Mixer::ERR_OPEN; + } + // Open audio device + m_port = ALopenport("XVDOPlayer", "w", m_config); + if (m_port == (ALport)0) { + return Mixer::ERR_OPEN; + } + else { + // Mixer is open. Now define properties + devmask = 1+128+2048; + recmask = 128; + i_recsrc = 128; + stereodevs = 1+128+2048; + MaxVolume = 255; + + i_s_mixer_name = "HPUX Audio Mixer"; + + isOpen = true; + return 0; + } +} + +int Mixer_IRIX::close() +{ + m_isOpen = false; + ALfreeconfig(m_config); + ALcloseport(m_port); + m_mixDevices.clear(); + return 0; +} + +int Mixer_IRIX::readVolumeFromHW( int devnum, int *VolLeft, int *VolRight ) +{ + long in_buf[4]; + switch( devnum() ) { + case 0: // Speaker Output + in_buf[0] = AL_RIGHT_SPEAKER_GAIN; + in_buf[2] = AL_LEFT_SPEAKER_GAIN; + break; + case 7: // Microphone Input (actually selectable). + in_buf[0] = AL_RIGHT_INPUT_ATTEN; + in_buf[2] = AL_LEFT_INPUT_ATTEN; + break; + case 11: // Record monitor + in_buf[0] = AL_RIGHT_MONITOR_ATTEN; + in_buf[2] = AL_LEFT_MONITOR_ATTEN; + break; + default: + printf("Unknown device %d\n", MixPtr->num() ); + } + ALgetparams(AL_DEFAULT_DEVICE, in_buf, 4); + *VolRight = in_buf[1]*100/255; + *VolLeft = in_buf[3]*100/255; + + return 0; +} + +int Mixer_IRIX::writeVolumeToHW( int devnum, int volLeft, int volRight ) +{ + // Set volume (right&left) + long out_buf[4] = + { + 0, volRight, + 0, volLeft + }; + switch( mixdevice->num() ) { + case 0: // Speaker + out_buf[0] = AL_RIGHT_SPEAKER_GAIN; + out_buf[2] = AL_LEFT_SPEAKER_GAIN; + break; + case 7: // Microphone (Input) + out_buf[0] = AL_RIGHT_INPUT_ATTEN; + out_buf[2] = AL_LEFT_INPUT_ATTEN; + break; + case 11: // Record monitor + out_buf[0] = AL_RIGHT_MONITOR_ATTEN; + out_buf[2] = AL_LEFT_MONITOR_ATTEN; + break; + } + ALsetparams(AL_DEFAULT_DEVICE, out_buf, 4); + + return 0; +} + +QString IRIX_getDriverName() { + return "IRIX"; +} + diff --git a/kmix/mixer_irix.h b/kmix/mixer_irix.h new file mode 100644 index 00000000..decf143c --- /dev/null +++ b/kmix/mixer_irix.h @@ -0,0 +1,28 @@ +#ifndef MIXER_IRIX_H +#define MIXER_IRIX_H + +#define _LANGUAGE_C_PLUS_PLUS +#include <dmedia/audio.h> + +#include "mixer_backend.h" + +class Mixer_IRIX : public Mixer_Backend +{ +public: + Mixer_IRIX(int devnum); + virtual ~Mixer_IRIX(); + + virtual void setRecsrc(unsigned int newRecsrc); + virtual int readVolumeFromHW( int devnum, int *VolLeft, int *VolRight ); + virtual int writeVolumeToHW( int devnum, int volLeft, int volRight ); + +protected: + virtual void setDevNumName_I(int devnum); + virtual int open(); + virtual int close(); + + ALport m_port; + ALconfig m_config; +}; + +#endif diff --git a/kmix/mixer_none.cpp b/kmix/mixer_none.cpp new file mode 100644 index 00000000..41e3d9b3 --- /dev/null +++ b/kmix/mixer_none.cpp @@ -0,0 +1,78 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2000 Christian Esken + * esken@kde.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "mixer_none.h" + +// This static method must be implemented (as fallback) +Mixer_Backend* Mixer::getMixer(int devnum) +{ + Mixer_Backend *l_mixer; + l_mixer = new Mixer_None( devnum); + return l_mixer; +} + +Mixer_None::Mixer_None(int devnum) : Mixer_Backend( device ) +{ +} + +Mixer_None::~Mixer_None() +{ + close(); +} + +int Mixer_None::open() +{ + //i_s_mixer_name = "No mixer"; + return Mixer::ERR_NOTSUPP; +} + +int Mixer_None::close() +{ + m_isOpen = false; + m_mixDevices.clear(); + return Mixer::ERR_NOTSUPP; +} + +int Mixer_None::readVolumeFromHW( int , Volume &vol ) +{ + return Mixer::ERR_NOTSUPP; +} + +int Mixer_None::writeVolumeToHW( int , Volume &vol ) +{ + return Mixer::ERR_NOTSUPP; +} + +bool Mixer_None::setRecsrcHW( int devnum, bool on) +{ + return false; +} + +bool Mixer_None::isRecsrcHW( int devnum ) +{ + return false; +} + +QString NONE_getDriverName() { + return "None"; +} + diff --git a/kmix/mixer_none.h b/kmix/mixer_none.h new file mode 100644 index 00000000..35d5dc93 --- /dev/null +++ b/kmix/mixer_none.h @@ -0,0 +1,22 @@ +#ifndef MIXER_NONE_H +#define MIXER_NONE_H + +#include "mixer_backend.h" + +class Mixer_None : public Mixer_Backend +{ +public: + Mixer_None(int devnum); + virtual ~Mixer_None(); + + virtual int readVolumeFromHW( int devnum, Volume& vol ); + virtual int writeVolumeToHW( int devnum, Volume& vol ); + virtual bool setRecsrcHW( int devnum, bool on); + virtual bool isRecsrcHW( int devnum ); + +protected: + virtual int open(); + virtual int close(); +}; + +#endif diff --git a/kmix/mixer_oss.cpp b/kmix/mixer_oss.cpp new file mode 100644 index 00000000..6991c1aa --- /dev/null +++ b/kmix/mixer_oss.cpp @@ -0,0 +1,330 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * Copyright (C) 1996-2000 Christian Esken + * esken@kde.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> + +// Since we're guaranteed an OSS setup here, let's make life easier +#if !defined(__NetBSD__) && !defined(__OpenBSD__) + #include <sys/soundcard.h> +#else + #include <soundcard.h> +#endif + +#include "mixer_oss.h" +#include <klocale.h> + +/* + I am using a fixed MAX_MIXDEVS #define here. + People might argue, that I should rather use the SOUND_MIXER_NRDEVICES + #define used by OSS. But using this #define is not good, because it is + evaluated during compile time. Compiling on one platform and running + on another with another version of OSS with a different value of + SOUND_MIXER_NRDEVICES is very bad. Because of this, usage of + SOUND_MIXER_NRDEVICES should be discouraged. + + The #define below is only there for internal reasons. + In other words: Don't play around with this value + */ +#define MAX_MIXDEVS 32 + +const char* MixerDevNames[32]={ + I18N_NOOP("Volume"), I18N_NOOP("Bass"), I18N_NOOP("Treble"), + I18N_NOOP("Synth"), I18N_NOOP("Pcm"), I18N_NOOP("Speaker"), + I18N_NOOP("Line"), I18N_NOOP("Microphone"), I18N_NOOP("CD"), + I18N_NOOP("Mix"), I18N_NOOP("Pcm2"), I18N_NOOP("RecMon"), + I18N_NOOP("IGain"), I18N_NOOP("OGain"), I18N_NOOP("Line1"), + I18N_NOOP("Line2"), I18N_NOOP("Line3"), I18N_NOOP("Digital1"), + I18N_NOOP("Digital2"), I18N_NOOP("Digital3"), I18N_NOOP("PhoneIn"), + I18N_NOOP("PhoneOut"), I18N_NOOP("Video"), I18N_NOOP("Radio"), + I18N_NOOP("Monitor"), I18N_NOOP("3D-depth"), I18N_NOOP("3D-center"), + I18N_NOOP("unknown"), I18N_NOOP("unknown"), I18N_NOOP("unknown"), + I18N_NOOP("unknown") , I18N_NOOP("unused") }; + +const MixDevice::ChannelType MixerChannelTypes[32] = { + MixDevice::VOLUME, MixDevice::BASS, MixDevice::TREBLE, MixDevice::MIDI, + MixDevice::AUDIO, MixDevice::EXTERNAL, MixDevice::EXTERNAL, MixDevice::MICROPHONE, + MixDevice::CD, MixDevice::VOLUME, MixDevice::AUDIO, MixDevice::RECMONITOR, + MixDevice::VOLUME, MixDevice::RECMONITOR, MixDevice::EXTERNAL, MixDevice::EXTERNAL, + MixDevice::EXTERNAL, MixDevice::AUDIO, MixDevice::AUDIO, MixDevice::AUDIO, + MixDevice::EXTERNAL, MixDevice::EXTERNAL, MixDevice::EXTERNAL, MixDevice::EXTERNAL, + MixDevice::EXTERNAL, MixDevice::VOLUME, MixDevice::VOLUME, MixDevice::UNKNOWN, + MixDevice::UNKNOWN, MixDevice::UNKNOWN, MixDevice::UNKNOWN, MixDevice::UNKNOWN }; + +Mixer_Backend* OSS_getMixer( int device ) +{ + Mixer_Backend *l_mixer; + l_mixer = new Mixer_OSS( device ); + return l_mixer; +} + +Mixer_OSS::Mixer_OSS(int device) : Mixer_Backend(device) +{ + if( device == -1 ) m_devnum = 0; +} + +Mixer_OSS::~Mixer_OSS() +{ + close(); +} + +int Mixer_OSS::open() +{ + if ((m_fd= ::open( deviceName( m_devnum ).latin1(), O_RDWR)) < 0) + { + if ( errno == EACCES ) + return Mixer::ERR_PERM; + else { + if ((m_fd= ::open( deviceNameDevfs( m_devnum ).latin1(), + O_RDWR)) < 0) + { + if ( errno == EACCES ) + return Mixer::ERR_PERM; + else + return Mixer::ERR_OPEN; + } + } + } + + int devmask, recmask, i_recsrc, stereodevs; + // Mixer is open. Now define properties + if (ioctl(m_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) + return Mixer::ERR_READ; + if (ioctl(m_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) + return Mixer::ERR_READ; + if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) + return Mixer::ERR_READ; + if (ioctl(m_fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) + return Mixer::ERR_READ; + if (!devmask) + return Mixer::ERR_NODEV; + int maxVolume =100; + + if( m_mixDevices.isEmpty() ) + { + int idx = 0; + while( devmask && idx < MAX_MIXDEVS ) + { + if( devmask & ( 1 << idx ) ) // device active? + { + Volume vol( stereodevs & ( 1 << idx ) ? 2 : 1, maxVolume); + readVolumeFromHW( idx, vol ); + MixDevice* md = + new MixDevice( idx, vol, recmask & ( 1 << idx ), true, + i18n(MixerDevNames[idx]), + MixerChannelTypes[idx]); + md->setRecSource( isRecsrcHW( idx ) ); + m_mixDevices.append( md ); + } + idx++; + } + } + else + for( unsigned int idx = 0; idx < m_mixDevices.count(); idx++ ) + { + MixDevice* md = m_mixDevices.at( idx ); + if( !md ) + return Mixer::ERR_INCOMPATIBLESET; + writeVolumeToHW( idx, md->getVolume() ); + } + +#if !defined(__FreeBSD__) + struct mixer_info l_mix_info; + if (ioctl(m_fd, SOUND_MIXER_INFO, &l_mix_info) != -1) + { + m_mixerName = l_mix_info.name; + } + else +#endif + + m_mixerName = "OSS Audio Mixer"; + + m_isOpen = true; + return 0; +} + +int Mixer_OSS::close() +{ + m_isOpen = false; + int l_i_ret = ::close(m_fd); + m_mixDevices.clear(); + return l_i_ret; +} + + +QString Mixer_OSS::deviceName(int devnum) +{ + switch (devnum) { + case 0: + return QString("/dev/mixer"); + break; + + default: + QString devname("/dev/mixer"); + devname += ('0'+devnum); + return devname; + } +} + +QString Mixer_OSS::deviceNameDevfs(int devnum) +{ + switch (devnum) { + case 0: + return QString("/dev/sound/mixer"); + break; + + default: + QString devname("/dev/sound/mixer"); + devname += ('0'+devnum); + return devname; + } +} + +QString Mixer_OSS::errorText(int mixer_error) +{ + QString l_s_errmsg; + switch (mixer_error) + { + case Mixer::ERR_PERM: + l_s_errmsg = i18n("kmix: You do not have permission to access the mixer device.\n" \ + "Login as root and do a 'chmod a+rw /dev/mixer*' to allow the access."); + break; + case Mixer::ERR_OPEN: + l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \ + "Please check that the soundcard is installed and the\n" \ + "soundcard driver is loaded.\n" \ + "On Linux you might need to use 'insmod' to load the driver.\n" \ + "Use 'soundon' when using commercial OSS."); + break; + default: + l_s_errmsg = Mixer_Backend::errorText(mixer_error); + } + return l_s_errmsg; +} + + +bool Mixer_OSS::setRecsrcHW( int devnum, bool on ) +{ + int i_recsrc, oldrecsrc; + if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) + errormsg(Mixer::ERR_READ); + + oldrecsrc = i_recsrc = on ? + (i_recsrc | (1 << devnum )) : + (i_recsrc & ~(1 << devnum )); + + // Change status of record source(s) + if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1) + errormsg (Mixer::ERR_WRITE); + // Re-read status of record source(s). Just in case, OSS does not like + // my settings. And with this line mix->recsrc gets its new value. :-) + if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) + errormsg(Mixer::ERR_READ); + + /* The following if {} patch was submitted by Tim McCormick <tim@pcbsd.org>. */ + /* Comment (cesken): This patch fixes an issue with mutual exclusive recording sources. + Actually the kernel soundcard driver *could* "do the right thing" by examining the change + (old-recsrc XOR new-recsrc), and knowing which sources are mutual exclusive. + The OSS v3 API docs indicate that the behaviour is undefined for this case, and it is not + clearly documented how and whether SOUND_MIXER_CAP_EXCL_INPUT is evaluated in the OSS driver. + Evaluating that in the application (KMix) could help, but the patch will work independent + on whether SOUND_MIXER_CAP_EXCL_INPUT ist set or not. + + In any case this patch is a superb workaround for a shortcoming of the OSS v3 API. + */ + // If the record source is supposed to be on, but wasn't set, explicitly + // set the record source. Not all cards support multiple record sources. + // As a result, we also need to do the read & write again. + if (((i_recsrc & ( 1<<devnum)) == 0) && on) + { + // Setting the new device failed => Try to enable it *exclusively* + oldrecsrc = i_recsrc = 1 << devnum; + if (ioctl(m_fd, SOUND_MIXER_WRITE_RECSRC, &i_recsrc) == -1) + errormsg (Mixer::ERR_WRITE); + if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) + errormsg(Mixer::ERR_READ); + } + + // PORTING: Hint: Do not forget to set i_recsrc to the new valid + // record source mask. + + return i_recsrc == oldrecsrc; +} + +bool Mixer_OSS::isRecsrcHW( int devnum ) +{ + bool isRecsrc = false; + int recsrcMask; + if (ioctl(m_fd, SOUND_MIXER_READ_RECSRC, &recsrcMask) == -1) + errormsg(Mixer::ERR_READ); + else { + // test if device bit is set in record bit mask + isRecsrc = ( (recsrcMask & ( 1<<devnum)) != 0 ); + } + return isRecsrc; +} + +int Mixer_OSS::readVolumeFromHW( int devnum, Volume &vol ) +{ + if( vol.isMuted() ) return 0; // Don't alter volume when muted + + int volume; + if (ioctl(m_fd, MIXER_READ( devnum ), &volume) == -1) + { + /* Oops, can't read mixer */ + return(Mixer::ERR_READ); + } + else + { + vol.setVolume( Volume::LEFT, (volume & 0x7f)); + if( vol.count() > 1 ) + vol.setVolume( Volume::RIGHT, ((volume>>8) & 0x7f)); + //fprintf(stderr, "Mixer_OSS::readVolumeFromHW(%i,vol) set vol %i %i\n", devnum, vol.getVolume(Volume::LEFT), vol.getVolume(Volume::RIGHT)); + return 0; + } +} + + + +int Mixer_OSS::writeVolumeToHW( int devnum, Volume &vol ) +{ + int volume; + if( vol.isMuted() ) volume = 0; + else + if ( vol.count() > 1 ) + volume = (vol[ Volume::LEFT ]) + ((vol[ Volume::RIGHT ])<<8); + else + volume = vol[ Volume::LEFT ]; + + if (ioctl(m_fd, MIXER_WRITE( devnum ), &volume) == -1) + return Mixer::ERR_WRITE; + + return 0; +} + +QString OSS_getDriverName() { + return "OSS"; +} + diff --git a/kmix/mixer_oss.h b/kmix/mixer_oss.h new file mode 100644 index 00000000..592802ea --- /dev/null +++ b/kmix/mixer_oss.h @@ -0,0 +1,33 @@ +//-*-C++-*- + +#ifndef MIXER_OSS_H +#define MIXER_OSS_H + +#include <qstring.h> + +#include "mixer_backend.h" + +class Mixer_OSS : public Mixer_Backend +{ +public: + Mixer_OSS(int device = -1); + virtual ~Mixer_OSS(); + + virtual QString errorText(int mixer_error); + virtual int readVolumeFromHW( int devnum, Volume &vol ); + virtual int writeVolumeToHW( int devnum, Volume &vol ); + +protected: + virtual bool setRecsrcHW( int devnum, bool on = true ); + virtual bool isRecsrcHW( int devnum ); + + virtual int open(); + virtual int close(); + + virtual QString deviceName( int ); + virtual QString deviceNameDevfs( int ); + int m_fd; + QString m_deviceName; +}; + +#endif diff --git a/kmix/mixer_oss4.cpp b/kmix/mixer_oss4.cpp new file mode 100644 index 00000000..dccea0be --- /dev/null +++ b/kmix/mixer_oss4.cpp @@ -0,0 +1,670 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * Copyright (C) 1996-2000 Christian Esken + * esken@kde.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +//OSS4 mixer backend for KMix by Yoper Team released under GPL v2 or later + + +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <qregexp.h> +#include <kdebug.h> + +// Since we're guaranteed an OSS setup here, let's make life easier +#if !defined(__NetBSD__) && !defined(__OpenBSD__) +#include <sys/soundcard.h> +#else +#include <soundcard.h> +#endif + +#include "mixer_oss4.h" +#include <klocale.h> + +Mixer_Backend* OSS4_getMixer(int device) +{ + Mixer_Backend *l_mixer; + l_mixer = new Mixer_OSS4(device); + return l_mixer; +} + +Mixer_OSS4::Mixer_OSS4(int device) : Mixer_Backend(device) +{ + if ( device == -1 ) m_devnum = 0; + m_numExtensions = 0; +} + +Mixer_OSS4::~Mixer_OSS4() +{ + close(); +} + +bool Mixer_OSS4::setRecsrcHW(int ctrlnum, bool on) +{ + return true; +} + +//dummy implementation only +bool Mixer_OSS4::isRecsrcHW(int ctrlnum) +{ + return false; +} + +//classifies mixexts according to their name, last classification wins +MixDevice::ChannelType Mixer_OSS4::classifyAndRename(QString &name, int flags) +{ + MixDevice::ChannelType cType = MixDevice::UNKNOWN; + QStringList classes = QStringList::split ( QRegExp ( "[-,.]" ), name ); + + + if ( flags & MIXF_PCMVOL || + flags & MIXF_MONVOL || + flags & MIXF_MAINVOL ) + { + cType = MixDevice::VOLUME; + } + + for ( QStringList::Iterator it = classes.begin(); it != classes.end(); ++it ) + { + if ( *it == "line" ) + { + *it = "Line"; + cType = MixDevice::EXTERNAL; + + } else + if ( *it == "mic" ) + { + *it = "Microphone"; + cType = MixDevice::MICROPHONE; + } else + if ( *it == "vol" ) + { + *it = "Volume"; + cType = MixDevice::VOLUME; + } else + if ( *it == "surr" ) + { + *it = "Surround"; + cType = MixDevice::SURROUND; + } else + if ( *it == "bass" ) + { + *it = "Bass"; + cType = MixDevice::BASS; + } else + if ( *it == "treble" ) + { + *it = "Treble"; + cType = MixDevice::TREBLE; + } else + if ( (*it).startsWith ( "pcm" ) ) + { + (*it).replace ( "pcm","PCM" ); + cType = MixDevice::AUDIO; + } else + if ( *it == "src" ) + { + *it = "Source"; + } else + if ( *it == "rec" ) + { + *it = "Recording"; + } else + if ( *it == "cd" ) + { + *it = (*it).upper(); + cType = MixDevice::CD; + } + if ( (*it).startsWith("vmix") ) + { + (*it).replace("vmix","Virtual Mixer"); + cType = MixDevice::VOLUME; + } else + if ( (*it).endsWith("vol") ) + { + QChar &ref = (*it).ref(0); + ref = ref.upper(); + cType = MixDevice::VOLUME; + } + else + { + QChar &ref = (*it).ref(0); + ref = ref.upper(); + } + } + name = classes.join( " "); + return cType; +} + +int Mixer_OSS4::open() +{ + if ( (m_fd= ::open("/dev/mixer", O_RDWR)) < 0 ) + { + if ( errno == EACCES ) + return Mixer::ERR_PERM; + else + return Mixer::ERR_OPEN; + } + + if (wrapIoctl( ioctl (m_fd, OSS_GETVERSION, &m_ossVersion) ) < 0) + { + return Mixer::ERR_READ; + } + if (m_ossVersion < 0x040000) + { + return Mixer::ERR_NOTSUPP; + } + + + wrapIoctl( ioctl (m_fd, SNDCTL_MIX_NRMIX, &m_numMixers) ); + + if ( m_mixDevices.isEmpty() ) + { + if ( m_devnum >= 0 && m_devnum < m_numMixers ) + { + m_numExtensions = m_devnum; + bool masterChosen = false; + oss_mixext ext; + ext.dev = m_devnum; + + if ( wrapIoctl( ioctl (m_fd, SNDCTL_MIX_NREXT, &m_numExtensions) ) < 0 ) + { + //TODO: more specific error handling here + if ( errno == ENODEV ) return Mixer::ERR_NODEV; + return Mixer::ERR_READ; + } + + if( m_numExtensions == 0 ) + { + return Mixer::ERR_NODEV; + } + + ext.ctrl = 0; + + //read MIXT_DEVROOT, return Mixer::NODEV on error + if ( wrapIoctl ( ioctl( m_fd, SNDCTL_MIX_EXTINFO, &ext) ) < 0 ) + { + return Mixer::ERR_NODEV; + } + + oss_mixext_root *root = (oss_mixext_root *) ext.data; + m_mixerName = root->name; + + for ( int i = 1; i < m_numExtensions; i++ ) + { + bool isCapture = false; + + ext.dev = m_devnum; + ext.ctrl = i; + + //wrapIoctl handles reinitialization, cancel loading on EIDRM + if ( wrapIoctl( ioctl( m_fd, SNDCTL_MIX_EXTINFO, &ext) ) == EIDRM ) + { + return 0; + } + + QString name = ext.extname; + + //skip vmix volume controls and mute controls + if ( (name.find("vmix") > -1 && name.find( "pcm") > -1) || + name.find("mute") > -1 +#ifdef MIXT_MUTE + || (ext.type == MIXT_MUTE) +#endif + ) + { + continue; + } + + //fix for old legacy names, according to Hannu's suggestions + if ( name.contains('_') ) + { + name = name.section('_',1,1).lower(); + } + + if ( ext.flags & MIXF_RECVOL || ext.flags & MIXF_MONVOL || name.find(".in") > -1 ) + { + isCapture = true; + } + + Volume::ChannelMask chMask = Volume::MNONE; + + MixDevice::ChannelType cType = classifyAndRename(name, ext.flags); + + if ( ext.type == MIXT_STEREOSLIDER16 || + ext.type == MIXT_STEREOSLIDER || + ext.type == MIXT_MONOSLIDER16 || + ext.type == MIXT_MONOSLIDER || + ext.type == MIXT_SLIDER + ) + { + if ( ext.type == MIXT_STEREOSLIDER16 || + ext.type == MIXT_STEREOSLIDER + ) + { + if ( isCapture ) + { + chMask = Volume::ChannelMask(Volume::MLEFT|Volume::MRIGHT); + } + else + { + chMask = Volume::ChannelMask(Volume::MLEFT|Volume::MRIGHT ); + } + } + else + { + if ( isCapture ) + { + chMask = Volume::MLEFT; + } + else + { + chMask = Volume::MLEFT; + } + } + + Volume vol (chMask, ext.maxvalue, ext.minvalue, isCapture); + + MixDevice* md = new MixDevice(i, vol, isCapture, true, + name, cType, MixDevice::SLIDER); + + m_mixDevices.append(md); + + if ( !masterChosen && ext.flags & MIXF_MAINVOL ) + { + m_recommendedMaster = md; + masterChosen = true; + } + } + else if ( ext.type == MIXT_ONOFF ) + { + Volume vol; + vol.setMuted(true); + MixDevice* md = new MixDevice(i, vol, false, true, name, MixDevice::VOLUME, MixDevice::SWITCH); + m_mixDevices.append(md); + } + else if ( ext.type == MIXT_ENUM ) + { + oss_mixer_enuminfo ei; + ei.dev = m_devnum; + ei.ctrl = i; + + if ( wrapIoctl( ioctl (m_fd, SNDCTL_MIX_ENUMINFO, &ei) ) != -1 ) + { + Volume vol(Volume::MLEFT, ext.maxvalue, ext.minvalue, false); + + MixDevice* md = new MixDevice (i, vol, false, false, + name, MixDevice::UNKNOWN, + MixDevice::ENUM); + + QPtrList<QString> &enumValuesRef = md->enumValues(); + QString thisElement; + + for ( int i = 0; i < ei.nvalues; i++ ) + { + thisElement = &ei.strings[ ei.strindex[i] ]; + + if ( thisElement.isEmpty() ) + { + thisElement = QString::number(i); + } + enumValuesRef.append( new QString(thisElement) ); + } + m_mixDevices.append(md); + } + } + + } + } + else + { + return -1; + } + } + m_isOpen = true; + return 0; +} + +int Mixer_OSS4::close() +{ + m_isOpen = false; + int l_i_ret = ::close(m_fd); + m_mixDevices.clear(); + return l_i_ret; +} + +QString Mixer_OSS4::errorText(int mixer_error) +{ + QString l_s_errmsg; + + switch( mixer_error ) + { + case Mixer::ERR_PERM: + l_s_errmsg = i18n("kmix: You do not have permission to access the mixer device.\n" \ + "Login as root and do a 'chmod a+rw /dev/mixer*' to allow the access."); + break; + case Mixer::ERR_OPEN: + l_s_errmsg = i18n("kmix: Mixer cannot be found.\n" \ + "Please check that the soundcard is installed and the\n" \ + "soundcard driver is loaded.\n" \ + "On Linux you might need to use 'insmod' to load the driver.\n" \ + "Use 'soundon' when using OSS4 from 4front."); + break; + case Mixer::ERR_NOTSUPP: + l_s_errmsg = i18n("kmix expected an OSSv4 mixer module,\n" \ + "but instead found an older version."); + break; + default: + l_s_errmsg = Mixer_Backend::errorText(mixer_error); + } + return l_s_errmsg; +} + +int Mixer_OSS4::readVolumeFromHW(int ctrlnum, Volume &vol) +{ + + oss_mixext extinfo; + oss_mixer_value mv; + + extinfo.dev = m_devnum; + extinfo.ctrl = ctrlnum; + + if ( wrapIoctl( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 ) + { + //TODO: more specific error handling + return Mixer::ERR_READ; + } + + mv.dev = extinfo.dev; + mv.ctrl = extinfo.ctrl; + mv.timestamp = extinfo.timestamp; + + if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_READ, &mv) ) < 0 ) + { + /* Oops, can't read mixer */ + return Mixer::ERR_READ; + } + else + { + if ( vol.isMuted() && extinfo.type != MIXT_ONOFF ) + { + return 0; + } + + if ( vol.isCapture() ) + { + switch ( extinfo.type ) + { + case MIXT_ONOFF: + vol.setMuted(mv.value != extinfo.maxvalue); + break; + + case MIXT_MONOSLIDER: + vol.setVolume(Volume::LEFT, mv.value & 0xff); + break; + + case MIXT_STEREOSLIDER: + vol.setVolume(Volume::LEFT, mv.value & 0xff); + vol.setVolume(Volume::RIGHT, ( mv.value >> 8 ) & 0xff); + break; + + case MIXT_SLIDER: + vol.setVolume(Volume::LEFT, mv.value); + break; + + case MIXT_MONOSLIDER16: + vol.setVolume(Volume::LEFT, mv.value & 0xffff); + break; + + case MIXT_STEREOSLIDER16: + vol.setVolume(Volume::LEFT, mv.value & 0xffff); + vol.setVolume(Volume::RIGHT, ( mv.value >> 16 ) & 0xffff); + break; + } + } + else + { + switch( extinfo.type ) + { + case MIXT_ONOFF: + vol.setMuted(mv.value != extinfo.maxvalue); + break; + case MIXT_MONOSLIDER: + vol.setVolume(Volume::LEFT, mv.value & 0xff); + break; + + case MIXT_STEREOSLIDER: + vol.setVolume(Volume::LEFT, mv.value & 0xff); + vol.setVolume(Volume::RIGHT, ( mv.value >> 8 ) & 0xff); + break; + + case MIXT_SLIDER: + vol.setVolume(Volume::LEFT, mv.value); + break; + + case MIXT_MONOSLIDER16: + vol.setVolume(Volume::LEFT, mv.value & 0xffff); + break; + + case MIXT_STEREOSLIDER16: + vol.setVolume(Volume::LEFT, mv.value & 0xffff); + vol.setVolume(Volume::RIGHT, ( mv.value >> 16 ) & 0xffff); + break; + } + } + } + return 0; +} + +int Mixer_OSS4::writeVolumeToHW(int ctrlnum, Volume &vol) +{ + int volume = 0; + + oss_mixext extinfo; + oss_mixer_value mv; + + extinfo.dev = m_devnum; + extinfo.ctrl = ctrlnum; + + if ( wrapIoctl( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 ) + { + //TODO: more specific error handling + kdDebug ( 67100 ) << "failed to read info for control " << ctrlnum << endl; + return Mixer::ERR_READ; + } + + if ( vol.isMuted() && extinfo.type != MIXT_ONOFF ) + { + volume = 0; + } + else + { + switch ( extinfo.type ) + { + case MIXT_ONOFF: + volume = (vol.isMuted()) ? (extinfo.minvalue) : (extinfo.maxvalue); + break; + case MIXT_MONOSLIDER: + volume = vol[Volume::LEFT]; + break; + + case MIXT_STEREOSLIDER: + volume = vol[Volume::LEFT] | ( vol[Volume::RIGHT] << 8 ); + break; + + case MIXT_SLIDER: + volume = vol[Volume::LEFT]; + break; + + case MIXT_MONOSLIDER16: + volume = vol[Volume::LEFT]; + break; + + case MIXT_STEREOSLIDER16: + volume = vol[Volume::LEFT] | ( vol[Volume::RIGHT] << 16 ); + break; + default: + return -1; + } + } + + mv.dev = extinfo.dev; + mv.ctrl = extinfo.ctrl; + mv.timestamp = extinfo.timestamp; + mv.value = volume; + + if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_WRITE, &mv) ) < 0 ) + { + kdDebug ( 67100 ) << "error writing: " << endl; + return Mixer::ERR_WRITE; + } + return 0; +} + +void Mixer_OSS4::setEnumIdHW(int ctrlnum, unsigned int idx) +{ + oss_mixext extinfo; + oss_mixer_value mv; + + extinfo.dev = m_devnum; + extinfo.ctrl = ctrlnum; + + if ( wrapIoctl ( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 ) + { + //TODO: more specific error handling + kdDebug ( 67100 ) << "failed to read info for control " << ctrlnum << endl; + return; + } + + if ( extinfo.type != MIXT_ENUM ) + { + return; + } + + + //according to oss docs maxVal < minVal could be true - strange... + unsigned int maxVal = (unsigned int) extinfo.maxvalue; + unsigned int minVal = (unsigned int) extinfo.minvalue; + + if ( maxVal < minVal ) + { + int temp; + temp = maxVal; + maxVal = minVal; + minVal = temp; + } + + if ( idx > maxVal || idx < minVal ) + idx = minVal; + + mv.dev = extinfo.dev; + mv.ctrl = extinfo.ctrl; + mv.timestamp = extinfo.timestamp; + mv.value = idx; + + if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_WRITE, &mv) ) < 0 ) + { + /* Oops, can't write to mixer */ + kdDebug ( 67100 ) << "error writing: " << endl; + } +} + +unsigned int Mixer_OSS4::enumIdHW(int ctrlnum) +{ + oss_mixext extinfo; + oss_mixer_value mv; + + extinfo.dev = m_devnum; + extinfo.ctrl = ctrlnum; + + if ( wrapIoctl ( ioctl(m_fd, SNDCTL_MIX_EXTINFO, &extinfo) ) < 0 ) + { + //TODO: more specific error handling + //TODO: check whether those return values are actually possible + return Mixer::ERR_READ; + } + + if ( extinfo.type != MIXT_ENUM ) + { + return Mixer::ERR_READ; + } + + mv.dev = extinfo.dev; + mv.ctrl = extinfo.ctrl; + mv.timestamp = extinfo.timestamp; + + if ( wrapIoctl ( ioctl (m_fd, SNDCTL_MIX_READ, &mv) ) < 0 ) + { + /* Oops, can't read mixer */ + return Mixer::ERR_READ; + } + return mv.value; +} + +int Mixer_OSS4::wrapIoctl(int ioctlRet) +{ + switch( ioctlRet ) + { + case EIO: + { + kdDebug ( 67100 ) << "A hardware level error occured" << endl; + break; + } + case EINVAL: + { + kdDebug ( 67100 ) << "Operation caused an EINVAL. You may have found a bug in kmix OSS4 module or in OSS4 itself" << endl; + break; + } + case ENXIO: + { + kdDebug ( 67100 ) << "Operation index out of bounds or requested device does not exist - you likely found a bug in the kmix OSS4 module" << endl; + break; + } + case EPERM: + case EACCES: + { + kdDebug ( 67100 ) << errorText ( Mixer::ERR_PERM ) << endl; + break; + } + case ENODEV: + { + kdDebug ( 67100 ) << "kmix received an ENODEV errors - are the OSS4 drivers loaded?" << endl; + break; + } + case EPIPE: + case EIDRM: + { + reinitialize(); + } + + } + return ioctlRet; +} + + +QString OSS4_getDriverName() +{ + return "OSS4"; +} + diff --git a/kmix/mixer_oss4.h b/kmix/mixer_oss4.h new file mode 100644 index 00000000..07a927d0 --- /dev/null +++ b/kmix/mixer_oss4.h @@ -0,0 +1,42 @@ +//-*-C++-*- + +#ifndef MIXER_OSS4_H +#define MIXER_OSS4_H + +#include <qstring.h> + +#include "mixer_backend.h" +#include <sys/soundcard.h> + +class Mixer_OSS4 : public Mixer_Backend +{ +public: + Mixer_OSS4(int device = -1); + virtual ~Mixer_OSS4(); + + virtual QString errorText(int mixer_error); + virtual int readVolumeFromHW( int ctrlnum, Volume &vol ); + virtual int writeVolumeToHW( int ctrlnum, Volume &vol ); + virtual void setEnumIdHW(int ctrlnum, unsigned int idx); + virtual unsigned int enumIdHW(int ctrlnum); + virtual bool setRecsrcHW( int ctrlnum, bool on); + virtual bool isRecsrcHW( int ctrlnum ); + +protected: + + MixDevice::ChannelType classifyAndRename(QString &name, int flags); + + int wrapIoctl(int ioctlRet); + + void reinitialize() { open(); close(); }; + virtual int open(); + virtual int close(); + virtual bool needsPolling() { return true; }; + + int m_ossVersion; + int m_fd; + int m_numExtensions; + int m_numMixers; + QString m_deviceName; +}; +#endif diff --git a/kmix/mixer_sun.cpp b/kmix/mixer_sun.cpp new file mode 100644 index 00000000..542fdcc8 --- /dev/null +++ b/kmix/mixer_sun.cpp @@ -0,0 +1,473 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2000 Christian Esken <esken@kde.org> + * 2000 Brian Hanson <bhanson@hotmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/audioio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +#include "mixer_sun.h" + + +//====================================================================== +// CONSTANT/ENUM DEFINITIONS +//====================================================================== + +// +// Mixer Device Numbers +// +// Note: We can't just use the Sun port #defines because : +// 1) Some logical devices don't correspond to ports (master&recmon) +// 2) The play and record port definitions reuse the same values +// +enum MixerDevs +{ + MIXERDEV_MASTER_VOLUME, + MIXERDEV_INTERNAL_SPEAKER, + MIXERDEV_HEADPHONE, + MIXERDEV_LINE_OUT, + MIXERDEV_RECORD_MONITOR, + MIXERDEV_MICROPHONE, + MIXERDEV_LINE_IN, + MIXERDEV_CD, + // Insert new devices before this marker + MIXERDEV_END_MARKER +}; +const int numDevs = MIXERDEV_END_MARKER; + +// +// Device name strings +// +const char* MixerDevNames[] = +{ + I18N_NOOP("Master Volume"), + I18N_NOOP("Internal Speaker"), + I18N_NOOP("Headphone"), + I18N_NOOP("Line Out"), + I18N_NOOP("Record Monitor"), + I18N_NOOP("Microphone"), + I18N_NOOP("Line In"), + I18N_NOOP("CD") +}; + +// +// Channel types (this specifies which icon to display) +// +const MixDevice::ChannelType MixerChannelTypes[] = +{ + MixDevice::VOLUME, // MASTER_VOLUME + MixDevice::AUDIO, // INTERNAL_SPEAKER + MixDevice::EXTERNAL, // HEADPHONE (we really need an icon for this) + MixDevice::EXTERNAL, // LINE_OUT + MixDevice::RECMONITOR, // RECORD_MONITOR + MixDevice::MICROPHONE, // MICROPHONE + MixDevice::EXTERNAL, // LINE_IN + MixDevice::CD // CD +}; + +// +// Mapping from device numbers to Sun port mask values +// +const uint_t MixerSunPortMasks[] = +{ + 0, // MASTER_VOLUME - no associated port + AUDIO_SPEAKER, + AUDIO_HEADPHONE, + AUDIO_LINE_OUT, + 0, // RECORD_MONITOR - no associated port + AUDIO_MICROPHONE, + AUDIO_LINE_IN, + AUDIO_CD +}; + + +//====================================================================== +// FUNCTION/METHOD DEFINITIONS +//====================================================================== + + +//====================================================================== +// FUNCTION : SUN_getMixer +// DESCRIPTION : Creates and returns a new mixer object. +//====================================================================== +Mixer_Backend* SUN_getMixer( int devnum ) +{ + Mixer_Backend *l_mixer; + l_mixer = new Mixer_SUN( devnum ); + return l_mixer; +} + + +//====================================================================== +// FUNCTION : Mixer::Mixer +// DESCRIPTION : Class constructor. +//====================================================================== +Mixer_SUN::Mixer_SUN(int devnum) : Mixer_Backend(devnum) +{ + if ( devnum == -1 ) + m_devnum = 0; +} + +//====================================================================== +// FUNCTION : Mixer::Mixer +// DESCRIPTION : Class destructor. +//====================================================================== +Mixer_SUN::~Mixer_SUN() +{ + close(); +} + +//====================================================================== +// FUNCTION : Mixer::open +// DESCRIPTION : Initialize the mixer and open the hardware driver. +//====================================================================== +int Mixer_SUN::open() +{ + // + // We don't support multiple devices + // + if ( m_devnum !=0 ) + return Mixer::ERR_OPEN; + + // + // Open the mixer hardware driver + // + QCString audiodev(getenv("AUDIODEV")); + if(audiodev.isNull()) + audiodev = "/dev/audio"; + audiodev += "ctl"; + if ( ( fd = ::open( audiodev.data(), O_RDWR ) ) < 0 ) + { + if ( errno == EACCES ) + return Mixer::ERR_PERM; + else + return Mixer::ERR_OPEN; + } + else + { + // + // Mixer is open. Now define all of the mix devices. + // + + if( m_mixDevices.isEmpty() ) + { + for ( int idx = 0; idx < numDevs; idx++ ) + { + Volume vol( 2, AUDIO_MAX_GAIN ); + readVolumeFromHW( idx, vol ); + MixDevice* md = new MixDevice( idx, vol, false, true, + QString(MixerDevNames[idx]), MixerChannelTypes[idx]); + md->setRecSource( isRecsrcHW( idx ) ); + m_mixDevices.append( md ); + } + } + else + { + for( unsigned int idx = 0; idx < m_mixDevices.count(); idx++ ) + { + MixDevice* md = m_mixDevices.at( idx ); + if( !md ) + return Mixer::ERR_INCOMPATIBLESET; + writeVolumeToHW( idx, md->getVolume() ); + } + } + + m_mixerName = "SUN Audio Mixer"; + m_isOpen = true; + + return 0; + } +} + +//====================================================================== +// FUNCTION : Mixer::close +// DESCRIPTION : Close the hardware driver. +//====================================================================== +int Mixer_SUN::close() +{ + m_isOpen = false; + int l_i_ret = ::close( fd ); + m_mixDevices.clear(); + return l_i_ret; +} + +//====================================================================== +// FUNCTION : Mixer::errorText +// DESCRIPTION : Convert an error code enum to a text string. +//====================================================================== +QString Mixer_SUN::errorText( int mixer_error ) +{ + QString errmsg; + switch (mixer_error) + { + case Mixer::ERR_PERM: + errmsg = i18n( + "kmix: You do not have permission to access the mixer device.\n" + "Ask your system administrator to fix /dev/audioctl to allow access." + ); + break; + default: + errmsg = Mixer_Backend::errorText( mixer_error ); + } + return errmsg; +} + + +//====================================================================== +// FUNCTION : Mixer::readVolumeFrmoHW +// DESCRIPTION : Read the audio information from the driver. +//====================================================================== +int Mixer_SUN::readVolumeFromHW( int devnum, Volume& volume ) +{ + audio_info_t audioinfo; + uint_t devMask = MixerSunPortMasks[devnum]; + + // + // Read the current audio information from the driver + // + if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 ) + { + return( Mixer::ERR_READ ); + } + else + { + // + // Extract the appropriate fields based on the requested device + // + switch ( devnum ) + { + case MIXERDEV_MASTER_VOLUME : + volume.setMuted( audioinfo.output_muted ); + GainBalanceToVolume( audioinfo.play.gain, + audioinfo.play.balance, + volume ); + break; + + case MIXERDEV_RECORD_MONITOR : + volume.setMuted(FALSE); + volume.setAllVolumes( audioinfo.monitor_gain ); + break; + + case MIXERDEV_INTERNAL_SPEAKER : + case MIXERDEV_HEADPHONE : + case MIXERDEV_LINE_OUT : + volume.setMuted( (audioinfo.play.port & devMask) ? FALSE : TRUE ); + GainBalanceToVolume( audioinfo.play.gain, + audioinfo.play.balance, + volume ); + break; + + case MIXERDEV_MICROPHONE : + case MIXERDEV_LINE_IN : + case MIXERDEV_CD : + volume.setMuted( (audioinfo.record.port & devMask) ? FALSE : TRUE ); + GainBalanceToVolume( audioinfo.record.gain, + audioinfo.record.balance, + volume ); + break; + + default : + return Mixer::ERR_NODEV; + } + return 0; + } +} + +//====================================================================== +// FUNCTION : Mixer::writeVolumeToHW +// DESCRIPTION : Write the specified audio settings to the hardware. +//====================================================================== +int Mixer_SUN::writeVolumeToHW( int devnum, Volume &volume ) +{ + uint_t gain; + uchar_t balance; + uchar_t mute; + + // + // Convert the Volume(left vol, right vol) to the Gain/Balance Sun uses + // + VolumeToGainBalance( volume, gain, balance ); + mute = volume.isMuted() ? 1 : 0; + + // + // Read the current audio settings from the hardware + // + audio_info_t audioinfo; + if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 ) + { + return( Mixer::ERR_READ ); + } + + // + // Now, based on the devnum that we are writing to, update the appropriate + // volume field and twiddle the appropriate bitmask to enable/mute the + // device as necessary. + // + switch ( devnum ) + { + case MIXERDEV_MASTER_VOLUME : + audioinfo.play.gain = gain; + audioinfo.play.balance = balance; + audioinfo.output_muted = mute; + break; + + case MIXERDEV_RECORD_MONITOR : + audioinfo.monitor_gain = gain; + // no mute or balance for record monitor + break; + + case MIXERDEV_INTERNAL_SPEAKER : + case MIXERDEV_HEADPHONE : + case MIXERDEV_LINE_OUT : + audioinfo.play.gain = gain; + audioinfo.play.balance = balance; + if ( mute ) + audioinfo.play.port &= ~MixerSunPortMasks[devnum]; + else + audioinfo.play.port |= MixerSunPortMasks[devnum]; + break; + + case MIXERDEV_MICROPHONE : + case MIXERDEV_LINE_IN : + case MIXERDEV_CD : + audioinfo.record.gain = gain; + audioinfo.record.balance = balance; + if ( mute ) + audioinfo.record.port &= ~MixerSunPortMasks[devnum]; + else + audioinfo.record.port |= MixerSunPortMasks[devnum]; + break; + + default : + return Mixer::ERR_NODEV; + } + + // + // Now that we've updated the audioinfo struct, write it back to the hardware + // + if ( ioctl( fd, AUDIO_SETINFO, &audioinfo ) < 0 ) + { + return( Mixer::ERR_WRITE ); + } + else + { + return 0; + } +} + +//====================================================================== +// FUNCTION : Mixer::setRecsrcHW +// DESCRIPTION : +//====================================================================== +bool Mixer_SUN::setRecsrcHW( int /* devnum */, bool /* on */ ) +{ + return FALSE; +} + +//====================================================================== +// FUNCTION : Mixer::isRecsrcHW +// DESCRIPTION : Returns true if the specified device is a record source. +//====================================================================== +bool Mixer_SUN::isRecsrcHW( int devnum ) +{ + switch ( devnum ) + { + case MIXERDEV_MICROPHONE : + case MIXERDEV_LINE_IN : + case MIXERDEV_CD : + return TRUE; + + default : + return FALSE; + } +} + +//====================================================================== +// FUNCTION : Mixer::VolumeToGainBalance +// DESCRIPTION : Converts a Volume(left vol + right vol) into the +// Gain/Balance values used by Sun. +//====================================================================== +void Mixer_SUN::VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance ) +{ + if ( ( volume.count() == 1 ) || + ( volume[Volume::LEFT] == volume[Volume::RIGHT] ) ) + { + gain = volume[Volume::LEFT]; + balance = AUDIO_MID_BALANCE; + } + else + { + if ( volume[Volume::LEFT] > volume[Volume::RIGHT] ) + { + gain = volume[Volume::LEFT]; + balance = AUDIO_LEFT_BALANCE + + ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) * + volume[Volume::RIGHT] / volume[Volume::LEFT]; + } + else + { + gain = volume[Volume::RIGHT]; + balance = AUDIO_RIGHT_BALANCE - + ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) * + volume[Volume::LEFT] / volume[Volume::RIGHT]; + } + } +} + +//====================================================================== +// FUNCTION : Mixer::GainBalanceToVolume +// DESCRIPTION : Converts Gain/Balance returned by Sun driver to the +// Volume(left vol + right vol) format used by kmix. +//====================================================================== +void Mixer_SUN::GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume ) +{ + if ( volume.count() == 1 ) + { + volume.setVolume( Volume::LEFT, gain ); + } + else + { + if ( balance <= AUDIO_MID_BALANCE ) + { + volume.setVolume( Volume::LEFT, gain ); + volume.setVolume( Volume::RIGHT, gain * + ( balance - AUDIO_LEFT_BALANCE ) / + ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) ); + } + else + { + volume.setVolume( Volume::RIGHT, gain ); + volume.setVolume( Volume::LEFT, gain * + ( AUDIO_RIGHT_BALANCE - balance ) / + ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) ); + } + } +} + +QString SUN_getDriverName() { + return "SUNAudio"; +} + diff --git a/kmix/mixer_sun.h b/kmix/mixer_sun.h new file mode 100644 index 00000000..cd1ef76a --- /dev/null +++ b/kmix/mixer_sun.h @@ -0,0 +1,52 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2000 Christian Esken <esken@kde.org> + * 2000 Brian Hanson <bhanson@hotmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef MIXER_SUN_H +#define MIXER_SUN_H + +#include <qstring.h> + +#include "mixer_backend.h" + +class Mixer_SUN : public Mixer_Backend +{ +public: + Mixer_SUN(int devnum); + virtual ~Mixer_SUN(); + + virtual QString errorText(int mixer_error); + virtual int readVolumeFromHW( int devnum, Volume& volume ); + virtual int writeVolumeToHW( int devnum, Volume& volume ); + bool setRecsrcHW( int devnum, bool on ); + bool isRecsrcHW( int devnum ); + +protected: + virtual int open(); + virtual int close(); + + void VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance ); + void GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume ); + + int fd; +}; + +#endif diff --git a/kmix/mixertoolbox.cpp b/kmix/mixertoolbox.cpp new file mode 100644 index 00000000..9b6ee943 --- /dev/null +++ b/kmix/mixertoolbox.cpp @@ -0,0 +1,226 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "qwidget.h" +#include "qstring.h" + +//#include <kdebug.h> +#include <klocale.h> + +#include "mixdevice.h" +#include "mixer.h" + +#include "mixertoolbox.h" + +/*********************************************************************************** + Attention: + This MixerToolBox is linked to the KMix Main Program, the KMix Applet and kmixctrl. + As we do not want to link in more than neccesary to kmixctrl, you are asked + not to put any GUI classes in here. + In the case where it is unavoidable, please put them in KMixToolBox. + ***********************************************************************************/ + +/** + * Scan for Mixers in the System. This is the method that implicitely fills the + * list of Mixer's, which is accesible via the static Mixer::mixer() method. + * @par mixers The list where to add the found Mixers. This parameter is superfluous + * nowadays, as it is now really trivial to get it - just call the static + * Mixer::mixer() method. + * @par multiDriverMode Whether the Mixer scan should try more all backendends. + * 'true' means to scan all backends. 'false' means: After scanning the + * current backend the next backend is only scanned if no Mixers were found yet. + */ +void MixerToolBox::initMixer(QPtrList<Mixer> &mixers, bool multiDriverMode, QString& ref_hwInfoString) +{ + //kdDebug(67100) << "IN MixerToolBox::initMixer()"<<endl; + + // Find all mixers and initalize them + QMap<QString,int> mixerNums; + int drvNum = Mixer::numDrivers(); + + int driverWithMixer = -1; + bool multipleDriversActive = false; + + QString driverInfo = ""; + QString driverInfoUsed = ""; + + for( int drv1=0; drv1<drvNum; drv1++ ) + { + QString driverName = Mixer::driverName(drv1); + if ( driverInfo.length() > 0 ) + driverInfo += " + "; + driverInfo += driverName; + } + /* Run a loop over all drivers. The loop will terminate after the first driver which + has mixers. And here is the reason: + - If you run ALSA with ALSA-OSS-Emulation enabled, mixers will show up twice: once + as native ALSA mixer, once as OSS mixer (emulated by ALSA). This is bad and WILL + confuse users. So it is a design decision that we can compile in multiple drivers + but we can run only one driver. + - For special usage scenarios, people will still want to run both drivers at the + same time. We allow them to hack their Config-File, where they can enable a + multi-driver mode. + - Another remark: For KMix3.0 or so, we should allow multiple-driver, for allowing + addition of special-use drivers, e.g. an Jack-Mixer-Backend, or a CD-Rom volume Backend. + */ + + bool autodetectionFinished = false; + for( int drv=0; drv<drvNum; drv++ ) + { + QString driverName = Mixer::driverName(drv); + + if ( autodetectionFinished ) { + // sane exit from loop + break; + } + + bool drvInfoAppended = false; + // The "19" below is just a "silly" number: + // (Old: The loop will break as soon as an error is detected - e.g. on 3rd loop when 2 soundcards are installed) + // New: We don't try be that clever anymore. We now blindly scan 20 cards, as the clever + // approach doesn't work for the one or other user. + int devNumMax = 19; + for( int dev=0; dev<=devNumMax; dev++ ) + { + Mixer *mixer = new Mixer( drv, dev ); + if ( mixer->isValid() ) { + mixer->open(); + mixers.append( mixer ); + // Count mixer nums for every mixer name to identify mixers with equal names. + // This is for creating persistent (reusable) primary keys, which can safely + // be referenced (especially for config file access, so it is meant to be persistent!). + mixerNums[mixer->mixerName()]++; + // Create a useful PK + /* As we use "::" and ":" as separators, the parts %1,%2 and %3 may not + * contain it. + * %1, the driver name is from the KMix backends, it does not contain colons. + * %2, the mixer name, is typically coming from an OS driver. It could contain colons. + * %3, the mixer number, is a number: it does not contain colons. + */ + QString mixerName = mixer->mixerName(); + mixerName.replace(":","_"); + QString primaryKeyOfMixer = QString("%1::%2:%3") + .arg(driverName) + .arg(mixerName) + .arg(mixerNums[mixer->mixerName()]); + // The following 3 replaces are for not messing up the config file + primaryKeyOfMixer.replace("]","_"); + primaryKeyOfMixer.replace("[","_"); // not strictly neccesary, but lets play safe + primaryKeyOfMixer.replace(" ","_"); + primaryKeyOfMixer.replace("=","_"); + + mixer->setID(primaryKeyOfMixer); + + } // valid + else + { + delete mixer; + mixer = 0; + } // invalid + + /* Lets decide if we the autoprobing shall continue: */ + if ( multiDriverMode ) { + // trivial case: In multiDriverMode, we scan ALL 20 devs of ALL drivers + // so we have to do "nothing" in this case + } // multiDriver + else { + // In No-multiDriver-mode we only need to check after we reached devNumMax + if ( dev == devNumMax ) { + if ( Mixer::mixers().count() != 0 ) { + // highest device number of driver and a Mixer => finished + autodetectionFinished = true; + } + } + } // !multiDriver + + // append driverName (used drivers) + if ( !drvInfoAppended ) + { + drvInfoAppended = true; + QString driverName = Mixer::driverName(drv); + if ( drv!= 0 && mixers.count() > 0) { + driverInfoUsed += " + "; + } + driverInfoUsed += driverName; + } + + // Check whether there are mixers in different drivers, so that the user can be warned + if (!multipleDriversActive) + { + if ( driverWithMixer == -1 ) + { + // Aha, this is the very first detected device + driverWithMixer = drv; + } + else + { + if ( driverWithMixer != drv ) + { + // Got him: There are mixers in different drivers + multipleDriversActive = true; + } + } + } // !multipleDriversActive + + } // loop over sound card devices of current driver + } // loop over soundcard drivers + + if ( Mixer::masterCard() == 0 ) { + // We have no master card yet. This actually only happens when there was + // not one defined in the kmixrc. + // So lets just set the first card as master card. + if ( Mixer::mixers().count() > 0 ) { + Mixer::setMasterCard( Mixer::mixers().first()->id()); + } + } + + ref_hwInfoString = i18n("Sound drivers supported:"); + ref_hwInfoString.append(" ").append( driverInfo ).append( "\n").append(i18n("Sound drivers used:")) .append(" ").append(driverInfoUsed); + + if ( multipleDriversActive ) + { + // this will only be possible by hacking the config-file, as it will not be officially supported + ref_hwInfoString += "\nExperimental multiple-Driver mode activated"; + } + + kdDebug(67100) << ref_hwInfoString << endl << "Total number of detected Mixers: " << Mixer::mixers().count() << endl; + //kdDebug(67100) << "OUT MixerToolBox::initMixer()"<<endl; + +} + + +/* + * Clean up and free all ressources of all found Mixers, which were found in the initMixer() call + */ +void MixerToolBox::deinitMixer() +{ + //kdDebug(67100) << "IN MixerToolBox::deinitMixer()"<<endl; + Mixer *mixer; + while ( (mixer=Mixer::mixers().first()) != 0) + { + //kdDebug(67100) << "MixerToolBox::deinitMixer() Remove Mixer" << endl; + mixer->close(); + Mixer::mixers().remove(mixer); + delete mixer; + } + // kdDebug(67100) << "OUT MixerToolBox::deinitMixer()"<<endl; +} diff --git a/kmix/mixertoolbox.h b/kmix/mixertoolbox.h new file mode 100644 index 00000000..1af4f22c --- /dev/null +++ b/kmix/mixertoolbox.h @@ -0,0 +1,22 @@ +#ifndef MIXERTOOLBOX_H +#define MIXERTOOLBOX_H + +#include <qptrlist.h> +#include <qstring.h> + +class Mixer; + +/** + * This toolbox contains various static methods that are shared throughout KMix. + * It only contains no-GUI code. The shared with-GUI code is in KMixToolBox + * The reason, why it is not put in a common base class is, that the classes are + * very different and cannot be changed (e.g. KPanelApplet) without major headache. + */ +class MixerToolBox { + public: + static void initMixer(QPtrList<Mixer>&, bool, QString&); + static void deinitMixer(); +}; + + +#endif diff --git a/kmix/mixset.cpp b/kmix/mixset.cpp new file mode 100644 index 00000000..c1b2a73f --- /dev/null +++ b/kmix/mixset.cpp @@ -0,0 +1,71 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include <qstring.h> + +#include <kdebug.h> +#include <kconfig.h> + +#include "mixdevice.h" +#include "mixset.h" + +void MixSet::clone( MixSet &set ) +{ + clear(); + + for( MixDevice *md=set.first(); md!=0; md=set.next() ) + { + append( new MixDevice( *md ) ); + } +} + +void MixSet::read( KConfig *config, const QString& grp ) +{ + kdDebug(67100) << "MixSet::read() of group " << grp << endl; + config->setGroup(grp); + m_name = config->readEntry( "name", m_name ); + + MixDevice* md; + for( md=first(); md!=0; md=next() ) + { + md->read( config, grp ); + } +} + +void MixSet::write( KConfig *config, const QString& grp ) +{ + kdDebug(67100) << "MixSet::write() of group " << grp << endl; + config->setGroup(grp); + config->writeEntry( "name", m_name ); + + MixDevice* md; + for( md=first(); md!=0; md=next() ) + { + md->write( config, grp ); + } +} + +void MixSet::setName( const QString &name ) +{ + m_name = name; +} + diff --git a/kmix/mixset.h b/kmix/mixset.h new file mode 100644 index 00000000..24448883 --- /dev/null +++ b/kmix/mixset.h @@ -0,0 +1,21 @@ +#ifndef MixSet_h +#define MixSet_h + +#include "mixdevice.h" + +class MixSet : public QPtrList<MixDevice> +{ + public: + void read( KConfig *config, const QString& grp ); + void write( KConfig *config, const QString& grp ); + + void clone( MixSet &orig ); + + QString name() { return m_name; }; + void setName( const QString &name ); + + private: + QString m_name; +}; + +#endif diff --git a/kmix/pics/Listener.png b/kmix/pics/Listener.png Binary files differnew file mode 100644 index 00000000..4f195914 --- /dev/null +++ b/kmix/pics/Listener.png diff --git a/kmix/pics/Makefile.am b/kmix/pics/Makefile.am new file mode 100644 index 00000000..bd1d3ee8 --- /dev/null +++ b/kmix/pics/Makefile.am @@ -0,0 +1,14 @@ + +picsdir = $(kde_datadir)/kmix/pics +pics_DATA = mix_audio.png mix_bass.png mix_cd.png mix_ext.png \ + mix_microphone.png mix_midi.png mix_recmon.png mix_treble.png \ + mix_unknown.png mix_volume.png mix_surround.png mix_video.png \ + mix_headphone.png mix_digital.png mix_ac97.png \ + kmixdocked.png kmixdocked_mute.png kmixdocked_error.png \ + mix_record.png \ + SpeakerFrontLeft.png SpeakerRearLeft.png SpeakerFrontRight.png SpeakerRearRight.png Listener.png + + +EXTRA_DIST = $(pics_DATA) + +KDE_ICON=kmix diff --git a/kmix/pics/SpeakerFrontLeft.png b/kmix/pics/SpeakerFrontLeft.png Binary files differnew file mode 100644 index 00000000..21f7795e --- /dev/null +++ b/kmix/pics/SpeakerFrontLeft.png diff --git a/kmix/pics/SpeakerFrontRight.png b/kmix/pics/SpeakerFrontRight.png Binary files differnew file mode 100644 index 00000000..33cee57e --- /dev/null +++ b/kmix/pics/SpeakerFrontRight.png diff --git a/kmix/pics/SpeakerRearLeft.png b/kmix/pics/SpeakerRearLeft.png Binary files differnew file mode 100644 index 00000000..375fe8df --- /dev/null +++ b/kmix/pics/SpeakerRearLeft.png diff --git a/kmix/pics/SpeakerRearRight.png b/kmix/pics/SpeakerRearRight.png Binary files differnew file mode 100644 index 00000000..e16af4c8 --- /dev/null +++ b/kmix/pics/SpeakerRearRight.png diff --git a/kmix/pics/hi128-app-kmix.png b/kmix/pics/hi128-app-kmix.png Binary files differnew file mode 100644 index 00000000..5543818b --- /dev/null +++ b/kmix/pics/hi128-app-kmix.png diff --git a/kmix/pics/hi16-app-kmix.png b/kmix/pics/hi16-app-kmix.png Binary files differnew file mode 100644 index 00000000..a422967e --- /dev/null +++ b/kmix/pics/hi16-app-kmix.png diff --git a/kmix/pics/hi32-app-kmix.png b/kmix/pics/hi32-app-kmix.png Binary files differnew file mode 100644 index 00000000..2036faa5 --- /dev/null +++ b/kmix/pics/hi32-app-kmix.png diff --git a/kmix/pics/hi48-app-kmix.png b/kmix/pics/hi48-app-kmix.png Binary files differnew file mode 100644 index 00000000..dc766a89 --- /dev/null +++ b/kmix/pics/hi48-app-kmix.png diff --git a/kmix/pics/hi64-app-kmix.png b/kmix/pics/hi64-app-kmix.png Binary files differnew file mode 100644 index 00000000..f7679b27 --- /dev/null +++ b/kmix/pics/hi64-app-kmix.png diff --git a/kmix/pics/kmixdocked.png b/kmix/pics/kmixdocked.png Binary files differnew file mode 100644 index 00000000..367ff42e --- /dev/null +++ b/kmix/pics/kmixdocked.png diff --git a/kmix/pics/kmixdocked_error.png b/kmix/pics/kmixdocked_error.png Binary files differnew file mode 100644 index 00000000..e9b68f2e --- /dev/null +++ b/kmix/pics/kmixdocked_error.png diff --git a/kmix/pics/kmixdocked_mute.png b/kmix/pics/kmixdocked_mute.png Binary files differnew file mode 100644 index 00000000..31f21541 --- /dev/null +++ b/kmix/pics/kmixdocked_mute.png diff --git a/kmix/pics/mix_ac97.png b/kmix/pics/mix_ac97.png Binary files differnew file mode 100644 index 00000000..ad4f3219 --- /dev/null +++ b/kmix/pics/mix_ac97.png diff --git a/kmix/pics/mix_audio.png b/kmix/pics/mix_audio.png Binary files differnew file mode 100644 index 00000000..74fc2764 --- /dev/null +++ b/kmix/pics/mix_audio.png diff --git a/kmix/pics/mix_bass.png b/kmix/pics/mix_bass.png Binary files differnew file mode 100644 index 00000000..5952d8b8 --- /dev/null +++ b/kmix/pics/mix_bass.png diff --git a/kmix/pics/mix_cd.png b/kmix/pics/mix_cd.png Binary files differnew file mode 100644 index 00000000..77ac81e9 --- /dev/null +++ b/kmix/pics/mix_cd.png diff --git a/kmix/pics/mix_digital.png b/kmix/pics/mix_digital.png Binary files differnew file mode 100644 index 00000000..739e60f5 --- /dev/null +++ b/kmix/pics/mix_digital.png diff --git a/kmix/pics/mix_ext.png b/kmix/pics/mix_ext.png Binary files differnew file mode 100644 index 00000000..c49f08ba --- /dev/null +++ b/kmix/pics/mix_ext.png diff --git a/kmix/pics/mix_headphone.png b/kmix/pics/mix_headphone.png Binary files differnew file mode 100644 index 00000000..de62b2fd --- /dev/null +++ b/kmix/pics/mix_headphone.png diff --git a/kmix/pics/mix_microphone.png b/kmix/pics/mix_microphone.png Binary files differnew file mode 100644 index 00000000..ca747403 --- /dev/null +++ b/kmix/pics/mix_microphone.png diff --git a/kmix/pics/mix_midi.png b/kmix/pics/mix_midi.png Binary files differnew file mode 100644 index 00000000..0b8477cc --- /dev/null +++ b/kmix/pics/mix_midi.png diff --git a/kmix/pics/mix_recmon.png b/kmix/pics/mix_recmon.png Binary files differnew file mode 100644 index 00000000..dba5c968 --- /dev/null +++ b/kmix/pics/mix_recmon.png diff --git a/kmix/pics/mix_record.png b/kmix/pics/mix_record.png Binary files differnew file mode 100644 index 00000000..f4aa7401 --- /dev/null +++ b/kmix/pics/mix_record.png diff --git a/kmix/pics/mix_surround.png b/kmix/pics/mix_surround.png Binary files differnew file mode 100644 index 00000000..d02d02c6 --- /dev/null +++ b/kmix/pics/mix_surround.png diff --git a/kmix/pics/mix_toslink.png b/kmix/pics/mix_toslink.png Binary files differnew file mode 100644 index 00000000..6766e613 --- /dev/null +++ b/kmix/pics/mix_toslink.png diff --git a/kmix/pics/mix_treble.png b/kmix/pics/mix_treble.png Binary files differnew file mode 100644 index 00000000..9871ab72 --- /dev/null +++ b/kmix/pics/mix_treble.png diff --git a/kmix/pics/mix_unknown.png b/kmix/pics/mix_unknown.png Binary files differnew file mode 100644 index 00000000..7c904971 --- /dev/null +++ b/kmix/pics/mix_unknown.png diff --git a/kmix/pics/mix_video.png b/kmix/pics/mix_video.png Binary files differnew file mode 100644 index 00000000..59efde57 --- /dev/null +++ b/kmix/pics/mix_video.png diff --git a/kmix/pics/mix_volume.png b/kmix/pics/mix_volume.png Binary files differnew file mode 100644 index 00000000..d17a85d2 --- /dev/null +++ b/kmix/pics/mix_volume.png diff --git a/kmix/resource.h b/kmix/resource.h new file mode 100644 index 00000000..57461096 --- /dev/null +++ b/kmix/resource.h @@ -0,0 +1,30 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef RESOURCE_H +#define RESOURCE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#endif // RESOURCE_H diff --git a/kmix/restore_kmix_volumes.desktop b/kmix/restore_kmix_volumes.desktop new file mode 100644 index 00000000..fed5e90d --- /dev/null +++ b/kmix/restore_kmix_volumes.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +X-KDE-autostart-phase=1 +X-KDE-autostart-condition=kmixrc::startkdeRestore:true +Type=Application +Exec=kmixctrl --restore +OnlyShowIn=KDE; +Name=Restore Mixer Volumes +Name[bg]=Възстановяване на стойностите на миксера +Name[bn]=মিক্সার ভলিউম পুনঃস্থাপন করে +Name[br]=Assav pep tolzennad mesker +Name[bs]=Vrati jačine miksera +Name[ca]=Restaura els volums del mesclador +Name[cs]=Obnovit nastavení hlasitosti +Name[cy]=Adfer Lefelau Sain y Cymysgydd +Name[da]=Genopret mikser lydstyrke +Name[de]=Lautstärken wiederherstellen +Name[el]=Επαναφορά των εντάσεων του μείκτη +Name[eo]=Restarigu Miksilagordon +Name[es]=Restaurar opciones del mezclador +Name[et]=Mikseri helitugevuste taastamine +Name[eu]=Nahasgailuaren bolumenak berreskuratu +Name[fa]=بازگردانی حجم صداهای مخلوطکن +Name[fi]=Palauta mikserin äänivoimakkuudet +Name[fr]=Restaurer les volumes du mixage +Name[gl]=Restaurar os Volumes do Mesturador +Name[he]=שיחזור עוצמות הקול של המערבל +Name[hu]=A hangkeverő hangerőinek visszaállítása +Name[is]=Sækja aftur stillingar hljóðrása +Name[it]=Ripristina i volumi del Mixer +Name[ja]=ミキサーの音量設定を復元する +Name[kk]=Микшер деңгейлерін қалпына келтіру +Name[km]=ស្ដារសំឡេងឧបករណ៍លាយ +Name[ko]=믹서 음량 복원 +Name[lt]=Atstatyti maišytuvo garso lygius +Name[mk]=Враќање на гласностите на миксетата +Name[nb]=Gjenopprett lydstyrkene til mikser +Name[nds]=Mischerluutstärken wedderherstellen +Name[ne]=मिक्सर भोल्युम पूर्वावस्थामा ल्यानुहोस् +Name[nl]=Mixervolumes herstellen +Name[nn]=Gjenopprett miksarlydstyrkar +Name[pl]=Odtwarzanie głośności miksera +Name[pt]=Repor os Volumes +Name[pt_BR]=Restaurar volumes do mixer +Name[ro]=Reface volumele mixerului +Name[ru]=Восстановление настроек микшера +Name[sk]=Obnoviť nastavenia mixéra +Name[sl]=Obnovi nastavitve mešalnika +Name[sr]=Обнови јачине миксете +Name[sr@Latn]=Obnovi jačine miksete +Name[sv]=Återställ mixervolymer +Name[ta]=ஒன்றுசேர்க்கும் ஒலியளவு மீட்கவும் +Name[th]=เรียกคืนระดับเสียงของมิกเซอร์ +Name[tr]=Karıştırıcı Seslerini Yenile +Name[uk]=Відновити параметри мікшера +Name[zh_CN]=恢复混音器设置 +Name[zh_HK]=回復混音器音量 +Name[zh_TW]=回復混音器音量 + diff --git a/kmix/version.h b/kmix/version.h new file mode 100644 index 00000000..c80dd2f6 --- /dev/null +++ b/kmix/version.h @@ -0,0 +1,21 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#define APP_VERSION "2.6.1" diff --git a/kmix/verticaltext.cpp b/kmix/verticaltext.cpp new file mode 100644 index 00000000..490e4c91 --- /dev/null +++ b/kmix/verticaltext.cpp @@ -0,0 +1,57 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 2003-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "verticaltext.h" +#include <qpainter.h> +#include <kdebug.h> + + +VerticalText::VerticalText(QWidget * parent, const char * name, WFlags f) : QWidget(parent,name,f) +{ + resize(20,100 /*parent->height() */ ); + setMinimumSize(20,10); // neccesary for smooth integration into layouts (we only care for the widths). +} + +VerticalText::~VerticalText() { +} + + +void VerticalText::paintEvent ( QPaintEvent * /*event*/ ) { + //kdDebug(67100) << "paintEvent(). height()=" << height() << "\n"; + QPainter paint(this); + paint.rotate(270); + paint.translate(0,-4); // Silly "solution" to make underlengths work + + // Fix for bug 72520 + //- paint.drawText(-height()+2,width(),name()); + //+ paint.drawText( -height()+2, width(), QString::fromUtf8(name()) ); + paint.drawText( -height()+2, width(), QString::fromUtf8(name()) ); +} + +QSize VerticalText::sizeHint() const { + return QSize(20,100); // !! UGLY. Should be reworked +} + +QSizePolicy VerticalText::sizePolicy () const +{ + return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); +} + diff --git a/kmix/verticaltext.h b/kmix/verticaltext.h new file mode 100644 index 00000000..7b1c07e0 --- /dev/null +++ b/kmix/verticaltext.h @@ -0,0 +1,19 @@ +#ifndef VerticalText_h +#define VerticalText_h + +#include <qwidget.h> + +class VerticalText : public QWidget +{ +public: + VerticalText(QWidget * parent, const char * name, WFlags f = 0); + ~VerticalText(); + + QSize sizeHint() const; + QSizePolicy sizePolicy () const; + +protected: + void paintEvent ( QPaintEvent * event ); +}; + +#endif diff --git a/kmix/viewapplet.cpp b/kmix/viewapplet.cpp new file mode 100644 index 00000000..23599190 --- /dev/null +++ b/kmix/viewapplet.cpp @@ -0,0 +1,242 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewapplet.h" + +// Qt +#include <qwidget.h> +#include <qlayout.h> +#include <qsize.h> + +// KDE +#include <kactioncollection.h> +#include <kdebug.h> +#include <kpanelapplet.h> +#include <kstdaction.h> + +// KMix +#include "kmixtoolbox.h" +#include "mdwslider.h" +#include "mixer.h" + +ViewApplet::ViewApplet(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KPanelApplet::Position position ) + : ViewBase(parent, name, QString::null, mixer, WStyle_Customize|WStyle_NoBorder, vflags) +{ + setBackgroundOrigin(AncestorOrigin); + // remove the menu bar action, that is put by the "ViewBase" constructor in _actions. + //KToggleAction *m = static_cast<KToggleAction*>(KStdAction::showMenubar( this, SLOT(toggleMenuBarSlot()), _actions )); + _actions->remove( KStdAction::showMenubar(this, SLOT(toggleMenuBarSlot()), _actions) ); + + + if ( position == KPanelApplet::pLeft || position == KPanelApplet::pRight ) { + //kdDebug(67100) << "ViewApplet() isVertical" << "\n"; + _viewOrientation = Qt::Vertical; + } + else { + //kdDebug(67100) << "ViewApplet() isHorizontal" << "\n"; + _viewOrientation = Qt::Horizontal; + } + + if ( _viewOrientation == Qt::Horizontal ) { + _layoutMDW = new QHBoxLayout( this ); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + } + else { + _layoutMDW = new QVBoxLayout( this ); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + } + + + //_layoutMDW->setResizeMode(QLayout::Fixed); + init(); +} + +ViewApplet::~ViewApplet() { +} + +void ViewApplet::setMixSet(MixSet *mixset) +{ + MixDevice* md; + for ( md = mixset->first(); md != 0; md = mixset->next() ) { + if ( (! md->isSwitch()) && ( ! md->isEnum() ) ) { + _mixSet->append(md); + } + } +} + +int ViewApplet::count() +{ + return ( _mixSet->count() ); +} + +int ViewApplet::advice() { + if ( _mixSet->count() > 0 ) { + // The standard input and output views are always advised, if there are devices in it + return 100; + } + else { + return 0; + } +} + + + +QWidget* ViewApplet::add(MixDevice *md) +{ + /** + Slider orientation is exactly the other way round. If the applet stretches horzontally, + the sliders must be vertical + */ + Qt::Orientation sliderOrientation; + if (_viewOrientation == Qt::Horizontal ) + sliderOrientation = Qt::Vertical; + else + sliderOrientation = Qt::Horizontal; + + // kdDebug(67100) << "ViewApplet::add()\n"; + MixDeviceWidget *mdw = + new MDWSlider( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + false, // Show Mute LED + false, // Show Record LED + true, // Small + sliderOrientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + mdw->setBackgroundOrigin(AncestorOrigin); + + static_cast<MDWSlider*>(mdw)->setValueStyle(MixDeviceWidget::NNONE); + static_cast<MDWSlider*>(mdw)->setIcons(shouldShowIcons( size()) ); // !!! This should use the panel size + _layoutMDW->add(mdw); + return mdw; +} + +void ViewApplet::constructionFinished() { + _layoutMDW->activate(); + + KMixToolBox::setIcons ( _mdws, shouldShowIcons( size()) ); // !!! This should use the panel size + KMixToolBox::setValueStyle( _mdws, MixDeviceWidget::NNONE); +} + + +QSize ViewApplet::sizeHint() const { + // Basically out main layout knows very good what the sizes should be + QSize qsz = _layoutMDW->sizeHint(); + //kdDebug(67100) << "ViewApplet::sizeHint(): NewSize is " << qsz << "\n"; + return qsz; +} + +QSizePolicy ViewApplet::sizePolicy() const { + if ( _viewOrientation == Qt::Horizontal ) { + //kdDebug(67100) << "ViewApplet::sizePolicy=(Fixed,Expanding)\n"; + return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + } + else { + //kdDebug(67100) << "ViewApplet::sizePolicy=(Expanding,Fixed)\n"; + return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + } +} + +bool ViewApplet::shouldShowIcons(QSize qsz) { + bool showIcons = false; + if ( _viewOrientation == Qt::Horizontal ) { + if ( qsz.height() >= 32 ) { + //kdDebug(67100) << "ViewApplet::resizeEvent() hor >=32" << qre->size() << "\n"; + showIcons = true; + } + } + else { + if ( qsz.width() >= 32 ) { + //kdDebug(67100) << "ViewApplet::resizeEvent() vert >=32" << qre->size() << "\n"; + showIcons = true; + } + } + return showIcons; +} + +void ViewApplet::resizeEvent(QResizeEvent *qre) +{ + //kdDebug(67100) << "ViewApplet::resizeEvent() size=" << qre->size() << "\n"; + // decide whether we have to show or hide all icons + bool showIcons = shouldShowIcons(qre->size()); + + for ( QWidget *mdw = _mdws.first(); mdw != 0; mdw = _mdws.next() ) { + if ( mdw == 0 ) { + kdError(67100) << "ViewApplet::resizeEvent(): mdw == 0\n"; + break; // sanity check (normally the lists are set up correctly) + } + else { + if ( mdw->inherits("MDWSlider")) { + static_cast<MDWSlider*>(mdw)->setIcons(showIcons); + static_cast<MDWSlider*>(mdw)->setValueStyle(MixDeviceWidget::NNONE); + //static_cast<MDWSlider*>(mdw)->resize(qre->size()); + } + } + } + // kdDebug(67100) << "ViewApplet::resizeEvent(). SHOULD resize _layoutMDW to " << qre->size() << endl; + //QWidget::resizeEvent(qre); + + // resizing changes our own sizeHint(), because we must take the new PanelSize in account. + // So updateGeometry() is amust for us. + updateGeometry(); +} + + +void ViewApplet::refreshVolumeLevels() { + //kdDebug(67100) << "ViewApplet::refreshVolumeLevels()\n"; + + QWidget *mdw = _mdws.first(); + MixDevice* md; + for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) { + if ( mdw == 0 ) { + kdError(67100) << "ViewApplet::refreshVolumeLevels(): mdw == 0\n"; + break; // sanity check (normally the lists are set up correctly) + } + else { + if ( mdw->inherits("MDWSlider")) { + //kdDebug(67100) << "ViewApplet::refreshVolumeLevels(): updating\n"; + // a slider, fine. Lets update its value + static_cast<MDWSlider*>(mdw)->update(); + } + else { + kdError(67100) << "ViewApplet::refreshVolumeLevels(): mdw is not slider\n"; + // no slider. Cannot happen in theory => skip it + } + } + mdw = _mdws.next(); + } +} + +void ViewApplet::configurationUpdate() { + updateGeometry(); + //_layoutMDW->activate(); + constructionFinished(); // contains "_layoutMDW->activate();" + emit appletContentChanged(); + kdDebug(67100) << "ViewApplet::configurationUpdate()" << endl; + // the following "emit" is only here to be picked up by KMixApplet, because it has to + // - make sure the panel is informed about the size change + // - save the new configuration + //emit configurationUpdated(); +} + +#include "viewapplet.moc" diff --git a/kmix/viewapplet.h b/kmix/viewapplet.h new file mode 100644 index 00000000..a03d90e2 --- /dev/null +++ b/kmix/viewapplet.h @@ -0,0 +1,47 @@ +#ifndef ViewApplet_h +#define ViewApplet_h + +#include "viewbase.h" +#include <kpanelapplet.h> + +class QBoxLayout; +class QHBox; +class QSize; + +class Mixer; + +class ViewApplet : public ViewBase +{ + Q_OBJECT +public: + ViewApplet(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KPanelApplet::Position pos); + ~ViewApplet(); + + virtual int count(); + virtual int advice(); + virtual void setMixSet(MixSet *mixset); + virtual QWidget* add(MixDevice *mdw); + virtual void constructionFinished(); + virtual void configurationUpdate(); + + QSize sizeHint() const; + QSizePolicy sizePolicy() const; + virtual void resizeEvent(QResizeEvent*); + +signals: + void appletContentChanged(); + +public slots: + virtual void refreshVolumeLevels(); + +private: + bool shouldShowIcons(QSize); + QBoxLayout* _layoutMDW; + // Position of the applet (pLeft, pRight, pTop, pBottom) + //KPanelApplet::Position _KMIXposition; + // Orientation of the applet (horizontal or vertical) + Qt::Orientation _viewOrientation; +}; + +#endif + diff --git a/kmix/viewbase.cpp b/kmix/viewbase.cpp new file mode 100644 index 00000000..af4a9aa5 --- /dev/null +++ b/kmix/viewbase.cpp @@ -0,0 +1,187 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewbase.h" + +// QT +#include <qlabel.h> +#include <qcursor.h> + +// KDE +#include <kaction.h> +#include <kpopupmenu.h> +#include <klocale.h> +#include <kiconloader.h> + +// KMix +#include "dialogviewconfiguration.h" +#include "mixdevicewidget.h" +#include "mixer.h" + + +ViewBase::ViewBase(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, WFlags f, ViewBase::ViewFlags vflags) + : QWidget(parent, name, f), _vflags(vflags), _caption(caption) +{ + _mixer = mixer; + _mixSet = new MixSet(); + + /* Can't use the following construct: + setMixSet( & mixer->getMixSet()); + C++ does not use overloaded methods like getMixSet() as long as the constructor has not completed :-((( + */ + _actions = new KActionCollection( this ); + + // Plug in the "showMenubar" action, if the caller wants it. Typically this is only neccesary for views in the KMix main window. + if ( vflags & ViewBase::HasMenuBar ) { + KToggleAction *m = static_cast<KToggleAction*>(KStdAction::showMenubar( this, SLOT(toggleMenuBarSlot()), _actions )); + if ( vflags & ViewBase::MenuBarVisible ) { + m->setChecked(true); + } + else { + m->setChecked(false); + } + } + new KAction(i18n("&Channels"), 0, this, SLOT(configureView()), _actions, "toggle_channels"); + connect ( _mixer, SIGNAL(newVolumeLevels()), this, SLOT(refreshVolumeLevels()) ); +} + +ViewBase::~ViewBase() { + delete _mixSet; +} + +void ViewBase::init() { + const MixSet& mixset = _mixer->getMixSet(); + setMixSet( const_cast<MixSet*>(&mixset)); // const_cast<> +} + +void ViewBase::setMixSet(MixSet *) +{ + // do nothing. Subclasses can do something if they feel like it +} + +/** + * Dummy implementation for add(). + */ +QWidget* ViewBase::add(MixDevice* mdw) { + QWidget* label = new QLabel( mdw->name(), this, mdw->name().latin1()); + label->move(0, mdw->num()*12); + return label; +} + +void ViewBase::configurationUpdate() { +} + +/** + * Create all widgets. + * This is a loop over all supported devices of the corresponding view. + * On each device add() is called - the derived class must implement add() for creating and placing + * the real MixDeviceWidget. + * The added MixDeviceWidget is appended to the _mdws list. + */ +void ViewBase::createDeviceWidgets() +{ + // create devices + MixDevice *mixDevice; + for ( mixDevice = _mixSet->first(); mixDevice != 0; mixDevice = _mixSet->next()) + { + QWidget* mdw = add(mixDevice); + _mdws.append(mdw); + } + // allow view to "polish" itself + constructionFinished(); +} + +// ---------- Popup stuff START --------------------- +void ViewBase::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button()==RightButton ) + showContextMenu(); +} + +/** + * Return a popup menu. This contains basic entries. + * More can be added by the caller. + */ +KPopupMenu* ViewBase::getPopup() +{ + popupReset(); + return _popMenu; +} + +void ViewBase::popupReset() +{ + KAction *a; + + _popMenu = new KPopupMenu( this ); + _popMenu->insertTitle( SmallIcon( "kmix" ), i18n("Device Settings") ); + + a = _actions->action( "toggle_channels" ); + if ( a ) a->plug( _popMenu ); + + a = _actions->action( "options_show_menubar" ); + if ( a ) a->plug( _popMenu ); +} + + +/** + This will only get executed, when the user has removed all items from the view. + Don't remove this method, because then the user cannot get a menu for getting his + channels back +*/ +void ViewBase::showContextMenu() +{ + //kdDebug(67100) << "ViewBase::showContextMenu()" << endl; + popupReset(); + + QPoint pos = QCursor::pos(); + _popMenu->popup( pos ); +} + + +void ViewBase::refreshVolumeLevels() +{ + // is virtual +} + +Mixer* ViewBase::getMixer() { + return _mixer; +} + +/** + * Open the View configuration dialog. The user can select which channels he wants + * to see and which not. + */ +void ViewBase::configureView() { + + DialogViewConfiguration* dvc = new DialogViewConfiguration(0, *this); + dvc->show(); + // !! The dialog is modal. Does it delete itself? +} + +void ViewBase::toggleMenuBarSlot() { + //kdDebug(67100) << "ViewBase::toggleMenuBarSlot() start\n"; + emit toggleMenuBar(); + //kdDebug(67100) << "ViewBase::toggleMenuBarSlot() done\n"; +} + +// ---------- Popup stuff END --------------------- + +#include "viewbase.moc" diff --git a/kmix/viewbase.h b/kmix/viewbase.h new file mode 100644 index 00000000..50d92b10 --- /dev/null +++ b/kmix/viewbase.h @@ -0,0 +1,121 @@ +#ifndef ViewBase_h +#define ViewBase_h + +// QT +#include "qwidget.h" + +// KDE +class KActionCollection; +class KPopupMenu; +class MixSet; +class Mixer; +class MixDevice; + +/** + * The ViewBase is a virtual base class, to be used for subclassing the real Mixer Views. + */ +class ViewBase : public QWidget +{ + Q_OBJECT +public: + + typedef uint ViewFlags; + enum ViewFlagsEnum { + // Regular flags + HasMenuBar = 0x0001, + MenuBarVisible = 0x0002, + Horizontal = 0x0004, + Vertical = 0x0008, + // Experimental flags + Experimental_SurroundView = 0x1000, + Experimental_GridView = 0x2000 + }; + + ViewBase(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, WFlags=0, ViewFlags vflags=0); + virtual ~ViewBase(); + + // Subclasses must define this method. It is called by the ViewBase() constuctor. + // The view class must initialize here the _mixSet. This will normally be a subset + // of the passed mixset. + // After that the subclass must be prepared for + // being fed MixDevice's via the add() method. + virtual void setMixSet(MixSet *mixset); + + // Returns the number of accepted MixDevice's from setMixerSet(). This is + // normally smaller that mixset->count(), except when the class creates virtual + // devices + virtual int count() = 0; + // returns an advice about whether this view should be used at all. The returned + // value is a percentage (0-100). A view without accepted devices would return 0, + // a "3D sound View" would return 75, if all vital but some optional devices are + // not available. + virtual int advice() = 0; + + // This method is called by ViewBase at the end of createDeviceWidgets(). The default + // implementation does nothing. Subclasses can override this method for doing final + // touches. This is very much like polish(), but called at an exactly well-known time. + // Also I do not want Views to interfere with polish() + virtual void constructionFinished() = 0; + + // This method is called after a configuration update (in other words: after the user + // has clicked "OK" on the "show/hide" configuration dialog. The default implementation + // does nothing. + virtual void configurationUpdate(); + + /** + * Creates the widgets for all supported devices. The default implementation loops + * over the supported MixDevice's and calls add() for each of it. + */ + virtual void createDeviceWidgets(); + + /** + * Creates a suitable representation for the given MixDevice. + * The default implementation creates a label + */ + virtual QWidget* add(MixDevice *); + + /** + * Popup stuff + */ + virtual KPopupMenu* getPopup(); + virtual void popupReset(); + virtual void showContextMenu(); + + Mixer* getMixer(); + + /** + * Contains the widgets for the _mixSet. There is a 1:1 relationship, which means: + * _mdws[i] is the Widget for the MixDevice _mixSet[i]. + * Hint: The new ViewSurround class shows that a 1:1 relationship does not work in a general scenario. + * I actually DID expect this. The solution is unclear yet, probably there will be a virtual mapper method. + */ + QPtrList<QWidget> _mdws; // this obsoletes the former Channel class + + QString caption() const { return _caption; } + +protected: + void init(); + + Mixer *_mixer; + MixSet *_mixSet; + KPopupMenu *_popMenu; + KActionCollection* _actions; + ViewFlags _vflags; + +public slots: + virtual void refreshVolumeLevels(); + virtual void configureView(); + void toggleMenuBarSlot(); + +protected slots: + void mousePressEvent( QMouseEvent *e ); + +signals: + void toggleMenuBar(); + +private: + QString _caption; +}; + +#endif + diff --git a/kmix/viewdockareapopup.cpp b/kmix/viewdockareapopup.cpp new file mode 100644 index 00000000..b08138f7 --- /dev/null +++ b/kmix/viewdockareapopup.cpp @@ -0,0 +1,206 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewdockareapopup.h" + +// Qt +#include <qwidget.h> +#include <qevent.h> +#include <qlayout.h> +#include <qframe.h> +#include <qpushbutton.h> +#include <qdatetime.h> + +// KDE +#include <kdebug.h> +#include <kaction.h> +#include <kapplication.h> +#include <klocale.h> + +// KMix +#include "mdwslider.h" +#include "mixer.h" +#include "kmixdockwidget.h" + +// !! Do NOT remove or mask out "WType_Popup" +// Users will not be able to close the Popup without opening the KMix main window then. +// See Bug #93443, #96332 and #96404 for further details. -- esken +ViewDockAreaPopup::ViewDockAreaPopup(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KMixDockWidget *dockW ) + : ViewBase(parent, name, QString::null, mixer, WStyle_Customize | WType_Popup | Qt::WStyle_DialogBorder, vflags), _mdw(0), _dock(dockW) +{ + QBoxLayout *layout = new QHBoxLayout( this ); + _frame = new QFrame( this ); + layout->addWidget( _frame ); + + _frame->setFrameStyle( QFrame::PopupPanel | QFrame::Raised ); + _frame->setLineWidth( 1 ); + + _layoutMDW = new QGridLayout( _frame, 1, 1, 2, 1, "KmixPopupLayout" ); + _hideTimer = new QTime(); + init(); +} + +ViewDockAreaPopup::~ViewDockAreaPopup() { +} + + + +void ViewDockAreaPopup::mousePressEvent(QMouseEvent *) +{ +// kdDebug() << "Teste pres mouse" << endl; + /** + Hide the popup: + This should work automatically, when the user clicks outside the bounds of this popup: + Alas - it does not work. + Why it does not work, I do not know: this->isPopup() returns "true", so Qt should + properly take care of it in QWidget. + */ + if ( ! this->hasMouse() ) { + _hideTimer->start(); + hide(); // needed! + } + return; +} + +bool ViewDockAreaPopup::justHidden() +{ + return _hideTimer->elapsed() < 300; +} + +void ViewDockAreaPopup::wheelEvent ( QWheelEvent * e ) { + // Pass wheel event from "border widget" to child + if ( _mdw != 0 ) { + QApplication::sendEvent( _mdw, e); + } +} + +MixDevice* ViewDockAreaPopup::dockDevice() +{ + return _dockDevice; +} + + +void ViewDockAreaPopup::showContextMenu() +{ + // no right-button-menu on "dock area popup" + return; +} + + +void ViewDockAreaPopup::setMixSet(MixSet *) +{ + // kdDebug(67100) << "ViewDockAreaPopup::setMixSet()\n"; + // This implementation of setMixSet() is a bit "exotic". But I will leave it like this, until I implement + // a configuration option for "what device to show on the dock area" + _dockDevice = _mixer->masterDevice(); + if ( _dockDevice == 0 ) { + // If we have no mixer device, we will take the first available mixer device + _dockDevice = (*_mixer)[0]; + } + _mixSet->append(_dockDevice); +} + +QWidget* ViewDockAreaPopup::add(MixDevice *md) +{ + _mdw = + new MDWSlider( + _mixer, // the mixer for this device + md, // only 1 device. This is actually _dockDevice + true, // Show Mute LED + false, // Show Record LED + false, // Small + Qt::Vertical, // Direction: only 1 device, so doesn't matter + _frame, // parent + 0, // Is "NULL", so that there is no RMB-popup + _dockDevice->name().latin1() ); + _layoutMDW->addItem( new QSpacerItem( 5, 20 ), 0, 2 ); + _layoutMDW->addItem( new QSpacerItem( 5, 20 ), 0, 0 ); + _layoutMDW->addWidget( _mdw, 0, 1 ); + + // Add button to show main panel + _showPanelBox = new QPushButton( i18n("Mixer"), _frame, "MixerPanel" ); + connect ( _showPanelBox, SIGNAL( clicked() ), SLOT( showPanelSlot() ) ); + _layoutMDW->addMultiCellWidget( _showPanelBox, 1, 1, 0, 2 ); + + return _mdw; +} + +int ViewDockAreaPopup::count() +{ + return ( _mixSet->count() ); +} + +int ViewDockAreaPopup::advice() { + if ( _dockDevice != 0 ) { + // I could also evaluate whether we have a "sensible" device available. + // For example + // 100 : "master volume" + // 100 : "PCM" + // 50 : "CD" + // 0 : all other devices + return 100; + } + else { + return 0; + } +} + +QSize ViewDockAreaPopup::sizeHint() const { + // kdDebug(67100) << "ViewDockAreaPopup::sizeHint(): NewSize is " << _mdw->sizeHint() << "\n"; + return( _mdw->sizeHint() ); +} + +void ViewDockAreaPopup::constructionFinished() { + // kdDebug(67100) << "ViewDockAreaPopup::constructionFinished()\n"; + + _mdw->move(0,0); + _mdw->show(); + _mdw->resize(_mdw->sizeHint() ); + resize(sizeHint()); + +} + + +void ViewDockAreaPopup::refreshVolumeLevels() { + // kdDebug(67100) << "ViewDockAreaPopup::refreshVolumeLevels()\n"; + QWidget* mdw = _mdws.first(); + if ( mdw == 0 ) { + kdError(67100) << "ViewDockAreaPopup::refreshVolumeLevels(): mdw == 0\n"; + // sanity check (normally the lists are set up correctly) + } + else { + if ( mdw->inherits("MDWSlider")) { + static_cast<MDWSlider*>(mdw)->update(); + } + else { + kdError(67100) << "ViewDockAreaPopup::refreshVolumeLevels(): mdw is not slider\n"; + // no slider. Cannot happen in theory => skip it + } + } +} + +void ViewDockAreaPopup::showPanelSlot() { + _dock->toggleActive(); + _dock->_dockAreaPopup->hide(); +} + +#include "viewdockareapopup.moc" + diff --git a/kmix/viewdockareapopup.h b/kmix/viewdockareapopup.h new file mode 100644 index 00000000..4205e831 --- /dev/null +++ b/kmix/viewdockareapopup.h @@ -0,0 +1,57 @@ +#ifndef ViewDockAreaPopup_h +#define ViewDockAreaPopup_h + +#include "viewbase.h" + +class QMouseEvent; +class QGridLayout; +class QWidget; +class QPushButton; + +class Mixer; +class KMixDockWidget; +class MixDeviceWidget; +class MixDevice; +class QFrame; +class QTime; + +class ViewDockAreaPopup : public ViewBase +{ + Q_OBJECT +public: + ViewDockAreaPopup(QWidget* parent, const char* name, Mixer* mixer, ViewBase::ViewFlags vflags, KMixDockWidget *dockW); + ~ViewDockAreaPopup(); + MixDevice* dockDevice(); + + virtual int count(); + virtual int advice(); + virtual void setMixSet(MixSet *mixset); + virtual QWidget* add(MixDevice *mdw); + virtual void constructionFinished(); + virtual void refreshVolumeLevels(); + virtual void showContextMenu(); + + QSize sizeHint() const; + bool justHidden(); + +protected: + MixDeviceWidget *_mdw; + KMixDockWidget *_dock; + MixDevice *_dockDevice; + QPushButton *_showPanelBox; + + void mousePressEvent(QMouseEvent *e); + void wheelEvent ( QWheelEvent * e ); + +private: + QGridLayout* _layoutMDW; + QFrame *_frame; + QTime *_hideTimer; + +private slots: + void showPanelSlot(); + +}; + +#endif + diff --git a/kmix/viewgrid.cpp b/kmix/viewgrid.cpp new file mode 100644 index 00000000..0a995ac8 --- /dev/null +++ b/kmix/viewgrid.cpp @@ -0,0 +1,212 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewgrid.h" + +// Qt +#include <qwidget.h> + +// KDE +#include <kdebug.h> + +// KMix +#include "mdwenum.h" +#include "mdwslider.h" +#include "mdwswitch.h" +#include "mixer.h" + +/** + */ +ViewGrid::ViewGrid(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags) + : ViewBase(parent, name, caption, mixer, WStyle_Customize|WStyle_NoBorder, vflags) +{ + m_spacingHorizontal = 5; + m_spacingVertical = 5; + + if ( _vflags & ViewBase::Vertical ) { + //_layoutMDW = new QVBoxLayout(this); + } + else { + //_layoutMDW = new QHBoxLayout(this); + } + init(); +} + +ViewGrid::~ViewGrid() { +} + +void ViewGrid::setMixSet(MixSet *mixset) +{ + MixDevice* md; + int testCounter = 0; + for ( md = mixset->first(); md != 0; md = mixset->next() ) { + if (testCounter<8) { + _mixSet->append(md); + } + testCounter++; + } +} + +int ViewGrid::count() +{ + return ( _mixSet->count() ); +} + +int ViewGrid::advice() { + if ( _mixSet->count() > 0 ) { + // The standard input and output views are always advised, if there are devices in it + return 100; + } + else { + return 0; + } +} + +QWidget* ViewGrid::add(MixDevice *md) +{ + MixDeviceWidget *mdw = 0; + if ( md->isEnum() ) { + Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical; + mdw = new MDWEnum( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + orientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + } // an enum + else if (md->isSwitch()) { + Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical; + mdw = + new MDWSwitch( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + false, // Small + orientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + } // a switch + + else { // must be a slider + Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical; + mdw = + new MDWSlider( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + true, // Show Mute LED + true, // Show Record LED + false, // Small + orientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + } + return mdw; +} + +QSize ViewGrid::sizeHint() const { + // kdDebug(67100) << "ViewGrid::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n"; + return( m_sizeHint); +} + +void ViewGrid::constructionFinished() { + //_layoutMDW->activate(); + + // do a manual layout + configurationUpdate(); +} + +void ViewGrid::refreshVolumeLevels() { + // kdDebug(67100) << "ViewGrid::refreshVolumeLevels()\n"; + + m_sizeHint.setWidth (0); + m_sizeHint.setHeight(0); + + m_testingX = 0; + m_testingY = 0; + + QWidget *mdw = _mdws.first(); + MixDevice* md; + for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) { + if ( mdw == 0 ) { + kdError(67100) << "ViewGrid::refreshVolumeLevels(): mdw == 0\n"; + break; // sanity check (normally the lists are set up correctly) + } + else { + if ( mdw->inherits("MDWSlider")) { + //kdDebug(67100) << "ViewGrid::refreshVolumeLevels(): updating\n"; + // a slider, fine. Lets update its value + static_cast<MDWSlider*>(mdw)->update(); + } + else if ( mdw->inherits("MDWSwitch")) { + //kdDebug(67100) << "ViewGrid::refreshVolumeLevels(): updating\n"; + // a slider, fine. Lets update its value + static_cast<MDWSwitch*>(mdw)->update(); + } + else if ( mdw->inherits("MDWEnum")) { + static_cast<MDWEnum*>(mdw)->update(); + } + else { + kdError(67100) << "ViewGrid::refreshVolumeLevels(): mdw is unknown/unsupported type\n"; + // no slider. Cannot happen in theory => skip it + } + } + mdw = _mdws.next(); + } +} + +/** + This implementation makes sure the Grid's geometry is updated + after hiding/showing channels. +*/ +void ViewGrid::configurationUpdate() { + m_sizeHint.setWidth (0); + m_sizeHint.setHeight(0); + + m_testingX = 0; + m_testingY = 0; + + for (QWidget *qw = _mdws.first(); qw !=0; qw = _mdws.next() ) { + + if ( qw->inherits("MixDeviceWidget")) { + MixDeviceWidget* mdw = static_cast<MixDeviceWidget*>(qw); + int xPos = m_testingX * m_spacingHorizontal; + int yPos = m_testingY * m_spacingVertical ; + mdw->move( xPos, yPos ); + mdw->resize( mdw->sizeHint() ); + int xMax = xPos + mdw->width() ; if ( xMax > m_sizeHint.width() ) m_sizeHint.setWidth(xMax); + int yMax = yPos + mdw->height(); if ( yMax > m_sizeHint.height() ) m_sizeHint.setHeight(yMax); + + m_testingX += 5; + if ( m_testingX > 50 ) { + m_testingY += 10; + m_testingX = 0; + } + } // inherits MixDeviceWidget + } // for all MDW's +} + + +#include "viewgrid.moc" diff --git a/kmix/viewgrid.h b/kmix/viewgrid.h new file mode 100644 index 00000000..f57cdc59 --- /dev/null +++ b/kmix/viewgrid.h @@ -0,0 +1,42 @@ +#ifndef ViewGrid_h +#define ViewGrid_h + +class QBoxLayout; +#include "qsize.h" +class QWidget; + +class Mixer; +#include "viewbase.h" + +class ViewGrid : public ViewBase +{ + Q_OBJECT +public: + ViewGrid(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags); + ~ViewGrid(); + + virtual int count(); + virtual int advice(); + virtual void setMixSet(MixSet *mixset); + virtual QWidget* add(MixDevice *mdw); + virtual void configurationUpdate(); + virtual void constructionFinished(); + + QSize sizeHint() const; + +public slots: + virtual void refreshVolumeLevels(); + +private: + unsigned int m_spacingHorizontal; + unsigned int m_spacingVertical; + + // m_maxX and m_maxY are the highest coordiantes encountered + QSize m_sizeHint; + + unsigned int m_testingX; + unsigned int m_testingY; +}; + +#endif + diff --git a/kmix/viewinput.cpp b/kmix/viewinput.cpp new file mode 100644 index 00000000..646e94d6 --- /dev/null +++ b/kmix/viewinput.cpp @@ -0,0 +1,50 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewinput.h" +#include <qwidget.h> + +#include "mixer.h" +#include "mixdevicewidget.h" + +ViewInput::ViewInput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags) + : ViewSliders(parent, name, caption, mixer, vflags) +{ + init(); + connect ( _mixer, SIGNAL(newRecsrc()) , this, SLOT(refreshVolumeLevels()) ); // only the input widget has record sources +} + +ViewInput::~ViewInput() { +} + +void ViewInput::setMixSet(MixSet *mixset) +{ + MixDevice* md; + for ( md = mixset->first(); md != 0; md = mixset->next() ) { + if ( md->isRecordable() && ! md->isSwitch() && ! md->isEnum() ) { + _mixSet->append(md); + } + else { + } + } +} + +#include "viewinput.moc" diff --git a/kmix/viewinput.h b/kmix/viewinput.h new file mode 100644 index 00000000..1b5d5be8 --- /dev/null +++ b/kmix/viewinput.h @@ -0,0 +1,19 @@ +#ifndef ViewInput_h +#define ViewInput_h + +#include "viewsliders.h" +class QWidget; +class Mixer; + +class ViewInput : public ViewSliders +{ + Q_OBJECT +public: + ViewInput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags); + ~ViewInput(); + + virtual void setMixSet(MixSet *mixset); +}; + +#endif + diff --git a/kmix/viewoutput.cpp b/kmix/viewoutput.cpp new file mode 100644 index 00000000..81c850b1 --- /dev/null +++ b/kmix/viewoutput.cpp @@ -0,0 +1,50 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewoutput.h" +#include <qwidget.h> + +#include "mixer.h" +#include "mixdevicewidget.h" + +ViewOutput::ViewOutput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags) + : ViewSliders(parent, name, caption, mixer, vflags) +{ + init(); +} + +ViewOutput::~ViewOutput() { +} + +void ViewOutput::setMixSet(MixSet *mixset) +{ + MixDevice* md; + for ( md = mixset->first(); md != 0; md = mixset->next() ) { + if ( ! md->isRecordable() && ! md->isSwitch() && ! md->isEnum()) { + _mixSet->append(md); + } + else { + } + } +} + + +#include "viewoutput.moc" diff --git a/kmix/viewoutput.h b/kmix/viewoutput.h new file mode 100644 index 00000000..439aa7d8 --- /dev/null +++ b/kmix/viewoutput.h @@ -0,0 +1,19 @@ +#ifndef ViewOutput_h +#define ViewOutput_h + +#include "viewsliders.h" +class QWidget; +class Mixer; + +class ViewOutput : public ViewSliders +{ + Q_OBJECT +public: + ViewOutput(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags); + ~ViewOutput(); + + virtual void setMixSet(MixSet *mixset); +}; + +#endif + diff --git a/kmix/viewsliders.cpp b/kmix/viewsliders.cpp new file mode 100644 index 00000000..cb9b441e --- /dev/null +++ b/kmix/viewsliders.cpp @@ -0,0 +1,140 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewsliders.h" + +// Qt +#include <qlayout.h> +#include <qwidget.h> + +// KDE +#include <kdebug.h> + +// KMix +#include "mdwslider.h" +#include "mixer.h" + +/** + * Don't instanciate objects of this class directly. It won't work + * correctly because init() does not get called. + * See ViewInput and ViewOutput for "real" implementations. + */ +ViewSliders::ViewSliders(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags) + : ViewBase(parent, name, caption, mixer, WStyle_Customize|WStyle_NoBorder, vflags) +{ + if ( _vflags & ViewBase::Vertical ) { + _layoutMDW = new QVBoxLayout(this); + } + else { + _layoutMDW = new QHBoxLayout(this); + } + /* + * Do not call init(). Call init() only for "end usage" classes. + * Otherwise setMixSet() will be called multiple times. + * Yes, this is rotten ... I will think of something smart later !! + * Perhaps I can have a boolean "init-has-run" instance variable. + */ + //init(); +} + +ViewSliders::~ViewSliders() { +} + +void ViewSliders::setMixSet(MixSet *mixset) +{ + MixDevice* md; + for ( md = mixset->first(); md != 0; md = mixset->next() ) { + if ( (! md->isSwitch()) && ( ! md->isEnum() ) ) { + _mixSet->append(md); + } + } +} + +int ViewSliders::count() +{ + return ( _mixSet->count() ); +} + +int ViewSliders::advice() { + if ( _mixSet->count() > 0 ) { + // The standard input and output views are always advised, if there are devices in it + return 100; + } + else { + return 0; + } +} + +QWidget* ViewSliders::add(MixDevice *md) +{ + Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical; + MixDeviceWidget *mdw = + new MDWSlider( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + true, // Show Mute LED + true, // Show Record LED + false, // Small + orientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + _layoutMDW->add(mdw); + return mdw; +} + +QSize ViewSliders::sizeHint() const { + // kdDebug(67100) << "ViewSliders::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n"; + return( _layoutMDW->sizeHint() ); +} + +void ViewSliders::constructionFinished() { + _layoutMDW->activate(); +} + +void ViewSliders::refreshVolumeLevels() { + // kdDebug(67100) << "ViewSliders::refreshVolumeLevels()\n"; + + QWidget *mdw = _mdws.first(); + MixDevice* md; + for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) { + if ( mdw == 0 ) { + kdError(67100) << "ViewSliders::refreshVolumeLevels(): mdw == 0\n"; + break; // sanity check (normally the lists are set up correctly) + } + else { + if ( mdw->inherits("MDWSlider")) { + //kdDebug(67100) << "ViewSliders::refreshVolumeLevels(): updating\n"; + // a slider, fine. Lets update its value + static_cast<MDWSlider*>(mdw)->update(); + } + else { + kdError(67100) << "ViewSliders::refreshVolumeLevels(): mdw is not slider\n"; + // no slider. Cannot happen in theory => skip it + } + } + mdw = _mdws.next(); + } +} + + +#include "viewsliders.moc" diff --git a/kmix/viewsliders.h b/kmix/viewsliders.h new file mode 100644 index 00000000..ea108ee3 --- /dev/null +++ b/kmix/viewsliders.h @@ -0,0 +1,33 @@ +#ifndef ViewSliders_h +#define ViewSliders_h + +class QBoxLayout; +class QWidget; + +class Mixer; +#include "viewbase.h" + +class ViewSliders : public ViewBase +{ + Q_OBJECT +public: + ViewSliders(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags); + ~ViewSliders(); + + virtual int count(); + virtual int advice(); + virtual void setMixSet(MixSet *mixset); + virtual QWidget* add(MixDevice *mdw); + virtual void constructionFinished(); + + QSize sizeHint() const; + +public slots: + virtual void refreshVolumeLevels(); + +private: + QBoxLayout* _layoutMDW; +}; + +#endif + diff --git a/kmix/viewsurround.cpp b/kmix/viewsurround.cpp new file mode 100644 index 00000000..a5eb7068 --- /dev/null +++ b/kmix/viewsurround.cpp @@ -0,0 +1,270 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewsurround.h" + +// Qt +#include <qlabel.h> +#include <qlayout.h> +#include <qwidget.h> + +// KDE +#include <kdebug.h> +#include <kiconloader.h> + +// KMix +#include "kmixtoolbox.h" +#include "mdwslider.h" +#include "mixer.h" + +/** + * Demonstration verion of a "surround view" + * Not really usable right now. + */ +ViewSurround::ViewSurround(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags) + : ViewBase(parent, name, caption, mixer, WStyle_Customize|WStyle_NoBorder, vflags) +{ + _mdSurroundFront = 0; + _mdSurroundBack = 0; + _layoutMDW = new QHBoxLayout(this); + _layoutMDW->setMargin(8); + // Create switch buttonGroup + if ( _vflags & ViewBase::Vertical ) { + _layoutSliders = new QVBoxLayout(_layoutMDW); + } + else { + _layoutSliders = new QHBoxLayout(_layoutMDW); + } + _layoutSurround = new QGridLayout(_layoutMDW,3,5); + // _layoutMDW->setMargin(8); + init(); +} + +ViewSurround::~ViewSurround() { +} + +void ViewSurround::setMixSet(MixSet *mixset) +{ + MixDevice* md; + for ( md = mixset->first(); md != 0; md = mixset->next() ) { + if ( ! md->isSwitch() ) { + switch ( md->type() ) { + case MixDevice::VOLUME: + case MixDevice::SURROUND: + case MixDevice::SURROUND_BACK: + case MixDevice::SURROUND_LFE: + case MixDevice::SURROUND_CENTERFRONT: + case MixDevice::SURROUND_CENTERBACK: + case MixDevice::AC97: + _mixSet->append(md); + break; + default: + // we are not interested in other channels + break; + } // switch(type) + } // !is_switch() + } // for +} + +int ViewSurround::count() +{ + return ( _mixSet->count() ); +} + +int ViewSurround::advice() { + if ( _mixSet->count() > 0 ) { + // The standard input and output views are always advised, if there are devices in it + return 100; + } + else { + return 0; + } +} + +QWidget* ViewSurround::add(MixDevice *md) +{ + bool small = false; + Qt::Orientation orientation = Qt::Vertical; + switch ( md->type() ) { + case MixDevice::VOLUME: + _mdSurroundFront = md; + small = true; + break; + case MixDevice::SURROUND_BACK: + _mdSurroundBack = md; + small = true; + break; + case MixDevice::SURROUND_LFE: + orientation = Qt::Horizontal; + small = true; + break; + case MixDevice::SURROUND_CENTERFRONT: + orientation = Qt::Horizontal; + small = true; + break; + case MixDevice::SURROUND_CENTERBACK: + orientation = Qt::Horizontal; + small = true; + break; + + default: + small = false; + // these are the sliders on the left side of the surround View + orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical; + } // switch(type) + + MixDeviceWidget *mdw = createMDW(md, small, orientation); + + switch ( md->type() ) { + case MixDevice::VOLUME: + _layoutSurround->addWidget(mdw ,0,0, Qt::AlignBottom | Qt::AlignLeft); + break; + + case MixDevice::SURROUND_BACK: + _layoutSurround->addWidget(mdw ,2,0, Qt::AlignTop | Qt::AlignLeft); + break; + case MixDevice::SURROUND_LFE: + _layoutSurround->addWidget(mdw,1,3, Qt::AlignVCenter | Qt::AlignRight ); break; + break; + case MixDevice::SURROUND_CENTERFRONT: + _layoutSurround->addWidget(mdw,0,2, Qt::AlignTop | Qt::AlignHCenter); break; + break; + case MixDevice::SURROUND_CENTERBACK: + _layoutSurround->addWidget(mdw,2,2, Qt::AlignBottom | Qt::AlignHCenter); break; + break; + + case MixDevice::SURROUND: + case MixDevice::AC97: + default: + // Add as slider to the layout on the left side + _layoutSliders->add(mdw); + break; + } // switch(type) + + return mdw; +} + +QSize ViewSurround::sizeHint() const { + // kdDebug(67100) << "ViewSurround::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n"; + return( _layoutMDW->sizeHint() ); +} + +void ViewSurround::constructionFinished() { + QLabel* personLabel = new QLabel("Listener", this); + QPixmap icon = UserIcon( "Listener" ); + if ( ! icon.isNull()) personLabel->setPixmap(icon); + personLabel->setLineWidth( 4 ); + personLabel->setMidLineWidth( 3 ); + personLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + int rowOfSpeaker = 0; + if ( _mdSurroundBack != 0 ) { + // let the speaker "sit" in the rear of the room, if there is + // rear speaker support in this sound card + rowOfSpeaker = 1; + } + _layoutSurround->addWidget(personLabel ,rowOfSpeaker, 2, Qt::AlignHCenter | Qt::AlignVCenter); + + if ( _mdSurroundFront != 0 ) { + MixDeviceWidget *mdw = createMDW(_mdSurroundFront, true, Qt::Vertical); + _layoutSurround->addWidget(mdw,0,4, Qt::AlignBottom | Qt::AlignRight); + _mdws.append(mdw); + + QLabel* speakerIcon = new QLabel("Speaker", this); + icon = UserIcon( "SpeakerFrontLeft" ); + if ( ! icon.isNull()) speakerIcon->setPixmap(icon); + _layoutSurround->addWidget(speakerIcon,0,1, Qt::AlignTop | Qt::AlignLeft); + + speakerIcon = new QLabel("Speaker", this); + icon = UserIcon( "SpeakerFrontRight" ); + if ( ! icon.isNull()) speakerIcon->setPixmap(icon); + _layoutSurround->addWidget(speakerIcon,0,3, Qt::AlignTop | Qt::AlignRight); + + } + + if ( _mdSurroundBack != 0 ) { + MixDeviceWidget *mdw = createMDW(_mdSurroundBack, true, Qt::Vertical); + _layoutSurround->addWidget(mdw,2,4, Qt::AlignTop | Qt::AlignRight); + _mdws.append(mdw); + + QLabel* speakerIcon = new QLabel("Speaker", this); + icon = UserIcon( "SpeakerRearLeft" ); + if ( ! icon.isNull()) speakerIcon->setPixmap(icon); + _layoutSurround->addWidget(speakerIcon,2,1, Qt::AlignBottom | Qt::AlignLeft); + + speakerIcon = new QLabel("Speaker", this); + icon = UserIcon( "SpeakerRearRight" ); + if ( ! icon.isNull()) speakerIcon->setPixmap(icon); + _layoutSurround->addWidget(speakerIcon,2,3, Qt::AlignBottom | Qt::AlignRight); + + + } + + // !! just for the demo version + KMixToolBox::setIcons (_mdws, true); + KMixToolBox::setLabels(_mdws, true); + KMixToolBox::setTicks (_mdws, true); + + _layoutMDW->activate(); +} + +void ViewSurround::refreshVolumeLevels() { + // kdDebug(67100) << "ViewSurround::refreshVolumeLevels()\n"; + + QWidget *mdw = _mdws.first(); + MixDevice* md; + for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) { + if ( mdw == 0 ) { + kdError(67100) << "ViewSurround::refreshVolumeLevels(): mdw == 0\n"; + break; // sanity check (normally the lists are set up correctly) + } + else { + if ( mdw->inherits("MDWSlider")) { + //kdDebug(67100) << "ViewSurround::refreshVolumeLevels(): updating\n"; + // a slider, fine. Lets update its value + static_cast<MDWSlider*>(mdw)->update(); + } + else { + kdError(67100) << "ViewSurround::refreshVolumeLevels(): mdw is not slider\n"; + // no slider. Cannot happen in theory => skip it + } + } + mdw = _mdws.next(); + } +} + + +MixDeviceWidget* ViewSurround::createMDW(MixDevice *md, bool small, Qt::Orientation orientation) +{ + MixDeviceWidget* mdw = new MDWSlider( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + false, // Show Mute LED + false, // Show Record LED + small, // Small + orientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + return mdw; +} + +#include "viewsurround.moc" diff --git a/kmix/viewsurround.h b/kmix/viewsurround.h new file mode 100644 index 00000000..1427f41d --- /dev/null +++ b/kmix/viewsurround.h @@ -0,0 +1,42 @@ +#ifndef ViewSurround_h +#define ViewSurround_h + +class QBoxLayout; +class QGridLayout; +class QWidget; + +class MixDevice; +class MixDeviceWidget; +class Mixer; +#include "viewbase.h" + +class ViewSurround : public ViewBase +{ + Q_OBJECT +public: + ViewSurround(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags); + ~ViewSurround(); + + virtual int count(); + virtual int advice(); + virtual void setMixSet(MixSet *mixset); + virtual QWidget* add(MixDevice *mdw); + virtual void constructionFinished(); + + QSize sizeHint() const; + +public slots: + virtual void refreshVolumeLevels(); + +private: + MixDeviceWidget* createMDW(MixDevice *md, bool small, Qt::Orientation orientation); + MixDevice *_mdSurroundFront; + MixDevice *_mdSurroundBack; + + QBoxLayout* _layoutMDW; + QBoxLayout* _layoutSliders; + QGridLayout* _layoutSurround; +}; + +#endif + diff --git a/kmix/viewswitches.cpp b/kmix/viewswitches.cpp new file mode 100644 index 00000000..d5a9b60c --- /dev/null +++ b/kmix/viewswitches.cpp @@ -0,0 +1,189 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "viewswitches.h" + +#include <qlayout.h> +#include <qwidget.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "mdwswitch.h" +#include "mdwenum.h" +#include "mixer.h" + +ViewSwitches::ViewSwitches(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags) + : ViewBase(parent, name, caption, mixer, 0, vflags) +{ + // Create switch buttonGroup + if ( _vflags & ViewBase::Vertical ) { + _layoutMDW = new QVBoxLayout(this); + _layoutSwitch = new QVBoxLayout(_layoutMDW); + _layoutEnum = new QVBoxLayout(_layoutMDW); // always vertical! + } + else { + _layoutMDW = new QHBoxLayout(this); + _layoutSwitch = new QHBoxLayout(_layoutMDW); + // Place enums right from the switches: This is done, so that there will be no + // ugly space on the left side, when no Switch is shown. + // Actually it is not really clear yet, why there is empty space at all: There are 0 items in + // the _layoutEnum, so it might be a sizeHint() or some other subtle layout issue. + _layoutEnum = new QVBoxLayout(_layoutMDW); + } + init(); +} + +ViewSwitches::~ViewSwitches() { +} + +void ViewSwitches::setMixSet(MixSet *mixset) +{ + MixDevice* md; + for ( md = mixset->first(); md != 0; md = mixset->next() ) { + if ( md->isSwitch() || md->isEnum() ) { + _mixSet->append(md); + } + else { + } + } +} + + +int ViewSwitches::count() +{ + return ( _mixSet->count() ); +} + +int ViewSwitches::advice() { + if ( _mixSet->count() > 0 ) { + // The Switch Views is always advised, if there are devices in it + return 100; + } + else { + return 0; + } +} + +QWidget* ViewSwitches::add(MixDevice *md) +{ + MixDeviceWidget *mdw; + + if ( md->isEnum() ) { + Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical; + mdw = new MDWEnum( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + orientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + _layoutEnum->add(mdw); + } // an enum + else { + // must be a switch + Qt::Orientation orientation = (_vflags & ViewBase::Vertical) ? Qt::Horizontal : Qt::Vertical; + mdw = + new MDWSwitch( + _mixer, // the mixer for this device + md, // MixDevice (parameter) + false, // Small + orientation, // Orientation + this, // parent + this, // View widget + md->name().latin1() + ); + _layoutSwitch->add(mdw); + } // a switch + + return mdw; +} + +QSize ViewSwitches::sizeHint() const { + //kdDebug(67100) << "ViewSwitches::sizeHint(): NewSize is " << _layoutMDW->sizeHint() << "\n"; + return( _layoutMDW->sizeHint() ); +} + +void ViewSwitches::constructionFinished() { + configurationUpdate(); // also does _layoutMDW->activate(); +} + +void ViewSwitches::refreshVolumeLevels() { + //kdDebug(67100) << "ViewSwitches::refreshVolumeLevels()\n"; + QWidget *mdw = _mdws.first(); + MixDevice* md; + for ( md = _mixSet->first(); md != 0; md = _mixSet->next() ) { + if ( mdw == 0 ) { + kdError(67100) << "ViewSwitches::refreshVolumeLevels(): mdw == 0\n"; + break; // sanity check (normally the lists are set up correctly) + } + else { + if ( mdw->inherits("MDWSwitch")) { + //kdDebug(67100) << "ViewSwitches::refreshVolumeLevels(): updating\n"; + // a slider, fine. Lets update its value + static_cast<MDWSwitch*>(mdw)->update(); + } + else if ( mdw->inherits("MDWEnum")) { + static_cast<MDWEnum*>(mdw)->update(); + } + else { + kdError(67100) << "ViewSwitches::refreshVolumeLevels(): mdw is not slider\n"; + // no switch. Cannot happen in theory => skip it + // If I start putting other stuff in the switch tab, I will get a nice warning. + } + } + mdw = _mdws.next(); + } +} + + +/** + This implementation makes sure the BackgroundMode's are properly updated + with their alternating colors after hiding/showing channels. +*/ +void ViewSwitches::configurationUpdate() { + bool backGoundModeToggler = true; + for (QWidget *qw = _mdws.first(); qw !=0; qw = _mdws.next() ) { + if ( qw->inherits("MDWSwitch")) { + MixDeviceWidget* mdw = static_cast<MDWSwitch*>(qw); + if ( ! mdw->isDisabled() ) { + if ( backGoundModeToggler ) { + mdw->setBackgroundMode(PaletteBackground); + } + else { + // !! Should use KGlobalSettings::alternateBackgroundColor() + // or KGlobalSettings::calculateAlternateBackgroundColor() instead. + mdw->setBackgroundMode( PaletteBase ); + } + backGoundModeToggler = !backGoundModeToggler; + } // ! isDisabled() + else { + //kdDebug(67100) << "ViewSwitches::configurationUpdate() ignoring diabled switch\n"; + } + } // inherits("MDWSwitch") + } + _layoutMDW->activate(); +} + + +#include "viewswitches.moc" + diff --git a/kmix/viewswitches.h b/kmix/viewswitches.h new file mode 100644 index 00000000..043fce07 --- /dev/null +++ b/kmix/viewswitches.h @@ -0,0 +1,36 @@ +#ifndef ViewSwitches_h +#define ViewSwitches_h + +class QLayout; +class QWidget; + +class Mixer; +#include "viewbase.h" + +class ViewSwitches : public ViewBase +{ + Q_OBJECT +public: + ViewSwitches(QWidget* parent, const char* name, const QString & caption, Mixer* mixer, ViewBase::ViewFlags vflags); + ~ViewSwitches(); + + virtual int count(); + virtual int advice(); + virtual void setMixSet(MixSet *mixset); + virtual QWidget* add(MixDevice *mdw); + virtual void constructionFinished(); + virtual void configurationUpdate(); + + QSize sizeHint() const; + +public slots: + virtual void refreshVolumeLevels(); + +private: + QLayout* _layoutMDW; + QLayout* _layoutEnum; + QLayout* _layoutSwitch; +}; + +#endif + diff --git a/kmix/volume.cpp b/kmix/volume.cpp new file mode 100644 index 00000000..20d056a4 --- /dev/null +++ b/kmix/volume.cpp @@ -0,0 +1,266 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * + * Copyright (C) 1996-2004 Christian Esken <esken@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +// for operator<<() +#include <iostream> + +#include <kdebug.h> + +#include "volume.h" + + +int Volume::_channelMaskEnum[10] = + { MLEFT, MRIGHT, MCENTER, + MREARLEFT, MREARRIGHT, MWOOFER, + MLEFTREC , MRIGHTREC , + MCUSTOM1, MCUSTOM2 + }; + +Volume::Volume( ChannelMask chmask, long maxVolume, long minVolume, bool isCapture ) +{ + init(chmask, maxVolume, minVolume, isCapture); +} + + +// @ compatiblity constructor +Volume::Volume( int channels, long maxVolume ) { + if (channels == 1 ) { + init(Volume::MLEFT, maxVolume, 0, false); + } + else if (channels == 2) { + init(ChannelMask(Volume::MLEFT|Volume::MRIGHT), maxVolume, 0, false ); + } + else { + init(ChannelMask(Volume::MLEFT|Volume::MRIGHT), maxVolume, 0, false ); + kdError(67100) << "Warning: Multi-channel Volume object created with old constructor - this will not work fully\n"; + } +} + +Volume::Volume( const Volume &v ) +{ + _chmask = v._chmask; + _maxVolume = v._maxVolume; + _minVolume = v._minVolume; + _muted = v._muted; + _isCapture = v._isCapture; + setVolume(v, (ChannelMask)v._chmask); + + // kdDebug(67100) << "Volume::copy-constructor initialized " << v << "\n"; +} + +void Volume::init( ChannelMask chmask, long maxVolume, long minVolume, bool isCapture ) +{ + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + _volumes[i] = 0; + } + _chmask = chmask; + _maxVolume = maxVolume; + _minVolume = minVolume; + _isCapture = isCapture; + _muted = false; +} + +// @ compatibility +void Volume::setAllVolumes(long vol) +{ + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + if ( (_channelMaskEnum[i]) & _chmask ) { + // we are supposed to set it + _volumes[i] = volrange(vol); + } + } +} + +// @ compatibility +void Volume::setVolume( ChannelID chid, long vol) +{ + if ( chid>=0 && chid<=Volume::CHIDMAX ) { + // accepted. we don't care if we support the channel, + // because there is NO good action we could take. + // Anyway: getVolume() on an unsupported channel will return 0 all the time + _volumes[chid] = volrange(vol); + } +} + +/** + * Copy the volume elements contained in v to this Volume object. + * Only those elments are copied, that are supported in BOTH Volume objects. + */ +void Volume::setVolume(const Volume &v) +{ + setVolume(v, (ChannelMask)(v._chmask&_chmask) ); +} + +/** + * Copy the volume elements contained in v to this Volume object. + * Only those elments are copied, that are supported in BOTH Volume objects + * and match the ChannelMask given by chmask. + */ +void Volume::setVolume(const Volume &v, ChannelMask chmask) { + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + if ( _channelMaskEnum[i] & _chmask & (int)chmask ) { + // we are supposed to copy it + _volumes[i] = volrange(v._volumes[i]); + } + else { + // Safety first! Lets play safe here and put sane values in + _volumes[i] = 0; + } + } + +} + +long Volume::maxVolume() { + return _maxVolume; +} + +long Volume::minVolume() { + return _minVolume; +} + +// @ compatibility +long Volume::operator[](int id) { + return getVolume( (Volume::ChannelID) id ); +} + +long Volume::getVolume(ChannelID chid) { + long vol = 0; + + if ( chid < 0 || chid > (Volume::CHIDMAX) ) { + // should throw exception here. I will return 0 instead + } + else { + // check if channel is supported + int chmask = _channelMaskEnum[chid]; + if ( (chmask & _chmask) != 0 ) { + // channel is supported + vol = _volumes[chid]; + } + else { + // should throw exception here. I will return 0 instead + } + } + + return vol; +} + +long Volume::getAvgVolume(ChannelMask chmask) { + int avgVolumeCounter = 0; + long long sumOfActiveVolumes = 0; + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + if ( (_channelMaskEnum[i] & _chmask) & (int)chmask ) { + avgVolumeCounter++; + sumOfActiveVolumes += _volumes[i]; + } + } + if (avgVolumeCounter != 0) { + sumOfActiveVolumes /= avgVolumeCounter; + } + else { + // just return 0; + } + return (long)sumOfActiveVolumes; +} + +long Volume::getTopStereoVolume(ChannelMask chmask) { + long long topVolumeCount = 0; + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + if ( (_channelMaskEnum[i] & _chmask) & (int)chmask ) { + if ( topVolumeCount < _volumes[i] ) + topVolumeCount = _volumes[i]; + } + } + return (long)topVolumeCount; +} + +int Volume::count() { + int counter = 0; + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + if ( _channelMaskEnum[i] & _chmask ) { + counter++; + } + } + return counter; +} + +/** + * returns a "sane" volume level. This means, it is a volume level inside the + * valid bounds + */ +long Volume::volrange( int vol ) +{ + if ( vol < _minVolume ) { + return _minVolume; + } + else if ( vol < _maxVolume ) { + return vol; + } + else { + return _maxVolume; + } +} + + +std::ostream& operator<<(std::ostream& os, const Volume& vol) { + os << "("; + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + if ( i != 0 ) { + os << ","; + } + if ( Volume::_channelMaskEnum[i] & vol._chmask ) { + // supported channel: Print Volume + os << vol._volumes[i]; + } + else { + // unsupported channel: Print "x" + os << "x"; + } + } // all channels + os << ")"; + + os << " [" << vol._minVolume << "-" << vol._maxVolume; + if ( vol._muted ) { os << " : muted ]"; } else { os << " : playing ]"; } + + return os; +} + +kdbgstream& operator<<(kdbgstream &os, const Volume& vol) { + os << "("; + for ( int i=0; i<= Volume::CHIDMAX; i++ ) { + if ( i != 0 ) { + os << ","; + } + if ( Volume::_channelMaskEnum[i] & vol._chmask ) { + // supported channel: Print Volume + os << vol._volumes[i]; + } + else { + // unsupported channel: Print "x" + os << "x"; + } + } // all channels + os << ")"; + + os << " [" << vol._minVolume << "-" << vol._maxVolume; + if ( vol._muted ) { os << " : muted ]"; } else { os << " : playing ]"; } + + return os; +} diff --git a/kmix/volume.h b/kmix/volume.h new file mode 100644 index 00000000..9051ed0c --- /dev/null +++ b/kmix/volume.h @@ -0,0 +1,80 @@ +// -*-C++-*- +#ifndef VOLUME_H +#define VOLUME_H + +#include <fstream> + +#include <kdebug.h> + +class Volume +{ + public: + enum ChannelMask { MNONE = 0, + MLEFT = 1, MRIGHT = 2, MCENTER = 4, + MMAIN = 3, MFRONT = 7, + MREARLEFT = 8, MREARRIGHT = 16, MWOOFER = 32, + MREAR = 56, + MLEFTREC = 64, MRIGHTREC = 128, + MREC =192, + MCUSTOM1 =256, MCUSTOM2 = 512, + MALL=65535 }; + + + enum ChannelID { CHIDMIN = 0, + LEFT = 0, RIGHT = 1, CENTER = 2, + REARLEFT = 3, REARRIGHT = 4, WOOFER = 5, + LEFTREC = 6, RIGHTREC = 7, + CUSTOM1 = 8, CUSTOM2 = 9, CHIDMAX = 9 }; + + + Volume( ChannelMask chmask = MALL, long maxVolume = 100, long minVolume=0, bool isCapture=false ); + Volume( const Volume &v ); + Volume( int channels, long maxVolume ); + + + + // Set all volumes as given by vol + void setAllVolumes(long vol); + // Set all volumes to the ones given in vol + void setVolume(const Volume &vol ); + // Set volumes as specified by the channel mask + void setVolume( const Volume &vol, ChannelMask chmask); + void setVolume( ChannelID chid, long volume); + + long getVolume(ChannelID chid); + long getAvgVolume(ChannelMask chmask); + long getTopStereoVolume(ChannelMask chmask); + long operator[](int); + long maxVolume(); + long minVolume(); + int count(); + + void setMuted( bool val ) { _muted = val; }; + bool isMuted() { return _muted; }; + bool isCapture() { return _isCapture; }; + + friend std::ostream& operator<<(std::ostream& os, const Volume& vol); + friend kdbgstream& operator<<(kdbgstream& os, const Volume& vol); + + // _channelMaskEnum[] and the following elements moved to public seection. operator<<() could not + // access it, when private. Strange, as operator<<() is declared friend. + static int _channelMaskEnum[10]; + bool _muted; + bool _isCapture; // true, when the Volume represents capture/record levels + long _chmask; + long _volumes[CHIDMAX+1]; + long _maxVolume; + long _minVolume; + +private: + void init( ChannelMask chmask, long, long, bool ); + + long volrange( int vol ); + long volrangeRec( int vol ); +}; + +std::ostream& operator<<(std::ostream& os, const Volume& vol); +kdbgstream& operator<<(kdbgstream &os, const Volume& vol); + +#endif // VOLUME + |