summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--doc/README.html.in8
-rw-r--r--src/Makefile.am1
-rw-r--r--src/cardwidget.h1
-rw-r--r--src/channelwidget.cc26
-rw-r--r--src/channelwidget.h8
-rw-r--r--src/devicewidget.cc153
-rw-r--r--src/devicewidget.h84
-rw-r--r--src/mainwindow.cc90
-rw-r--r--src/minimalstreamwidget.cc48
-rw-r--r--src/minimalstreamwidget.h14
-rw-r--r--src/pavucontrol.cc14
-rw-r--r--src/pavucontrol.glade228
-rw-r--r--src/rolewidget.cc7
-rw-r--r--src/rolewidget.h1
-rw-r--r--src/sinkinputwidget.cc110
-rw-r--r--src/sinkinputwidget.h49
-rw-r--r--src/sinkwidget.cc62
-rw-r--r--src/sinkwidget.h11
-rw-r--r--src/sourceoutputwidget.cc99
-rw-r--r--src/sourceoutputwidget.h53
-rw-r--r--src/sourcewidget.cc43
-rw-r--r--src/sourcewidget.h11
-rw-r--r--src/streamwidget.cc43
-rw-r--r--src/streamwidget.h15
25 files changed, 871 insertions, 312 deletions
diff --git a/configure.ac b/configure.ac
index baba768..246680f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -19,7 +19,7 @@
# along with pavucontrol. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.62)
-AC_INIT([pavucontrol],[0.9.8],[mzcnihpbageby (at) 0pointer (dot) de])
+AC_INIT([pavucontrol],[0.9.9],[mzcnihpbageby (at) 0pointer (dot) de])
AC_CONFIG_SRCDIR([src/pavucontrol.cc])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
@@ -39,7 +39,7 @@ AC_PROG_LN_S
AC_TYPE_SIGNAL
AC_HEADER_STDC
-PKG_CHECK_MODULES(GUILIBS, [ gtkmm-2.4 libglademm-2.4 sigc++-2.0 libcanberra-gtk >= 0.11 ])
+PKG_CHECK_MODULES(GUILIBS, [ gtkmm-2.4 libglademm-2.4 sigc++-2.0 libcanberra-gtk >= 0.16 ])
AC_SUBST(GUILIBS_CFLAGS)
AC_SUBST(GUILIBS_LIBS)
diff --git a/doc/README.html.in b/doc/README.html.in
index 8991692..7e6d541 100644
--- a/doc/README.html.in
+++ b/doc/README.html.in
@@ -38,6 +38,10 @@ General Public License for more details.</p>
<h2><a name="news">News</a></h2>
+<div class="news-date">Thu Sep 10
+2009: </div> <p class="news-text"><a href="@PACKAGE_URL@pavucontrol-0.9.9.tar.gz">Version
+0.9.9</a> released; allow configuring device ports; various updates.</p>
+
<div class="news-date">Mon Apr 13
2009: </div> <p class="news-text"><a href="@PACKAGE_URL@pavucontrol-0.9.8.tar.gz">Version
0.9.8</a> released; allow configuring card profiles; various updates.</p>
@@ -106,7 +110,7 @@ source code configuration.</p>
<p><tt>pavucontrol</tt> requires <a href="http://gtkmm.sf.net/"><tt>gtkmm</tt></a> and <tt>glademmm</tt> installed.</p>
-<p>Obviously <tt>pavucontrol</tt> requires an installation of <a href="http://pulseaudio.org/">PulseAudio</a> (version 0.9.15).</p>
+<p>Obviously <tt>pavucontrol</tt> requires an installation of <a href="http://pulseaudio.org/">PulseAudio</a> (version 0.9.16).</p>
<h2><a name="installation">Installation</a></h2>
@@ -131,7 +135,7 @@ compilation and <tt>make install</tt> (as root) for installation of
<pre>git clone git://git.0pointer.de/pavucontrol</pre>
<hr/>
-<address class="grey">Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;, April 2009</address>
+<address class="grey">Lennart Poettering &lt;@PACKAGE_BUGREPORT@&gt;, September 2009</address>
</body>
</html>
diff --git a/src/Makefile.am b/src/Makefile.am
index 629219c..7257260 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,6 +28,7 @@ pavucontrol_SOURCES= \
minimalstreamwidget.h minimalstreamwidget.cc \
channelwidget.h channelwidget.cc \
streamwidget.h streamwidget.cc \
+ devicewidget.h devicewidget.cc \
cardwidget.h cardwidget.cc \
sinkwidget.h sinkwidget.cc \
sourcewidget.h sourcewidget.cc \
diff --git a/src/cardwidget.h b/src/cardwidget.h
index 1f99b2b..ca90273 100644
--- a/src/cardwidget.h
+++ b/src/cardwidget.h
@@ -29,7 +29,6 @@ public:
static CardWidget* create();
Gtk::Label *nameLabel;
- Gtk::ToggleButton *streamToggleButton;
Gtk::Menu menu;
Gtk::Image *iconImage;
Glib::ustring name;
diff --git a/src/channelwidget.cc b/src/channelwidget.cc
index eac41ad..9fd62d8 100644
--- a/src/channelwidget.cc
+++ b/src/channelwidget.cc
@@ -23,7 +23,7 @@
#endif
#include "channelwidget.h"
-#include "streamwidget.h"
+#include "minimalstreamwidget.h"
#include "i18n.h"
@@ -83,28 +83,11 @@ void ChannelWidget::onVolumeScaleValueChanged() {
if (!volumeScaleEnabled)
return;
- if (streamWidget->updating)
+ if (minimalStreamWidget->updating)
return;
pa_volume_t volume = (pa_volume_t) ((volumeScale->get_value() * PA_VOLUME_NORM) / 100);
- streamWidget->updateChannelVolume(channel, volume);
-
- if (beepDevice != "") {
- ca_context_change_device(ca_gtk_context_get(), beepDevice.c_str());
-
- ca_context_cancel(ca_gtk_context_get(), 2);
-
- ca_gtk_play_for_widget(GTK_WIDGET(volumeScale->gobj()),
- 2,
- CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"),
- CA_PROP_EVENT_ID, "audio-volume-change",
- CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
- CA_PROP_CANBERRA_VOLUME, "0",
- CA_PROP_CANBERRA_ENABLE, "1",
- NULL);
-
- ca_context_change_device(ca_gtk_context_get(), NULL);
- }
+ minimalStreamWidget->updateChannelVolume(channel, volume);
}
void ChannelWidget::set_sensitive(bool enabled) {
@@ -117,7 +100,8 @@ void ChannelWidget::set_sensitive(bool enabled) {
void ChannelWidget::setBaseVolume(pa_volume_t v) {
- gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), 0.0, (GtkPositionType) GTK_POS_BOTTOM, _("<small>Silence</small>"));
+ gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), 0.0, (GtkPositionType) GTK_POS_BOTTOM,
+ can_decibel ? _("<small>Silence</small>") : _("<small>Min</small>"));
gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), 100.0, (GtkPositionType) GTK_POS_BOTTOM, _("<small>Max</small>"));
if (v > PA_VOLUME_MUTED && v < PA_VOLUME_NORM) {
diff --git a/src/channelwidget.h b/src/channelwidget.h
index 1de22d0..b15fe84 100644
--- a/src/channelwidget.h
+++ b/src/channelwidget.h
@@ -23,9 +23,7 @@
#include "pavucontrol.h"
-#include <canberra-gtk.h>
-
-class StreamWidget;
+class MinimalStreamWidget;
class ChannelWidget : public Gtk::EventBox {
public:
@@ -39,15 +37,13 @@ public:
Gtk::HScale *volumeScale;
int channel;
- StreamWidget *streamWidget;
+ MinimalStreamWidget *minimalStreamWidget;
void onVolumeScaleValueChanged();
bool can_decibel;
bool volumeScaleEnabled;
- Glib::ustring beepDevice;
-
virtual void set_sensitive(bool enabled);
virtual void setBaseVolume(pa_volume_t);
virtual void setSteps(unsigned n);
diff --git a/src/devicewidget.cc b/src/devicewidget.cc
new file mode 100644
index 0000000..e249944
--- /dev/null
+++ b/src/devicewidget.cc
@@ -0,0 +1,153 @@
+/***
+ This file is part of pavucontrol.
+
+ Copyright 2006-2008 Lennart Poettering
+ Copyright 2009 Colin Guthrie
+
+ pavucontrol is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ pavucontrol 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with pavucontrol. If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "devicewidget.h"
+#include "channelwidget.h"
+
+/*** DeviceWidget ***/
+DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
+ MinimalStreamWidget(cobject, x) {
+
+ x->get_widget("lockToggleButton", lockToggleButton);
+ x->get_widget("muteToggleButton", muteToggleButton);
+ x->get_widget("defaultToggleButton", defaultToggleButton);
+ x->get_widget("portSelect", portSelect);
+ x->get_widget("portList", portList);
+
+ muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onMuteToggleButton));
+ defaultToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onDefaultToggleButton));
+
+ treeModel = Gtk::ListStore::create(portModel);
+ portList->set_model(treeModel);
+ portList->pack_start(portModel.desc);
+
+ portList->signal_changed().connect(sigc::mem_fun(*this, &DeviceWidget::onPortChange));
+
+ for (unsigned i = 0; i < PA_CHANNELS_MAX; i++)
+ channelWidgets[i] = NULL;
+}
+
+void DeviceWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
+ channelMap = m;
+
+ for (int i = 0; i < m.channels; i++) {
+ ChannelWidget *cw = channelWidgets[i] = ChannelWidget::create();
+ cw->channel = i;
+ cw->can_decibel = can_decibel;
+ cw->minimalStreamWidget = this;
+ char text[64];
+ snprintf(text, sizeof(text), "<b>%s</b>", pa_channel_position_to_pretty_string(m.map[i]));
+ cw->channelLabel->set_markup(text);
+ channelsVBox->pack_start(*cw, false, false, 0);
+ }
+
+ lockToggleButton->set_sensitive(m.channels > 1);
+}
+
+void DeviceWidget::setVolume(const pa_cvolume &v, bool force) {
+ g_assert(v.channels == channelMap.channels);
+
+ volume = v;
+
+ if (timeoutConnection.empty() || force) { /* do not update the volume when a volume change is still in flux */
+ for (int i = 0; i < volume.channels; i++)
+ channelWidgets[i]->setVolume(volume.values[i]);
+ }
+}
+
+void DeviceWidget::updateChannelVolume(int channel, pa_volume_t v) {
+ pa_cvolume n;
+ g_assert(channel < volume.channels);
+
+ n = volume;
+ if (lockToggleButton->get_active())
+ pa_cvolume_set(&n, n.channels, v);
+ else
+ n.values[channel] = v;
+
+ setVolume(n, true);
+
+ if (timeoutConnection.empty())
+ timeoutConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this, &DeviceWidget::timeoutEvent), 100);
+}
+
+void DeviceWidget::onMuteToggleButton() {
+
+ lockToggleButton->set_sensitive(!muteToggleButton->get_active());
+
+ for (int i = 0; i < channelMap.channels; i++)
+ channelWidgets[i]->set_sensitive(!muteToggleButton->get_active());
+}
+
+void DeviceWidget::onDefaultToggleButton() {
+}
+
+void DeviceWidget::setDefault(bool isDefault) {
+ defaultToggleButton->set_active(isDefault);
+ /*defaultToggleButton->set_sensitive(!isDefault);*/
+}
+
+bool DeviceWidget::timeoutEvent() {
+ executeVolumeUpdate();
+ return false;
+}
+
+void DeviceWidget::executeVolumeUpdate() {
+}
+
+void DeviceWidget::setBaseVolume(pa_volume_t v) {
+
+ if (channelMap.channels > 0)
+ channelWidgets[channelMap.channels-1]->setBaseVolume(v);
+}
+
+void DeviceWidget::setSteps(unsigned n) {
+
+ for (int i = 0; i < channelMap.channels; i++)
+ channelWidgets[channelMap.channels-1]->setSteps(n);
+}
+
+void DeviceWidget::prepareMenu() {
+ int idx = 0;
+ int active_idx = -1;
+
+ treeModel->clear();
+ /* Fill the ComboBox's Tree Model */
+ for (uint32_t i = 0; i < ports.size(); ++i) {
+ Gtk::TreeModel::Row row = *(treeModel->append());
+ row[portModel.name] = ports[i].first;
+ row[portModel.desc] = ports[i].second;
+ if (ports[i].first == activePort)
+ active_idx = idx;
+ idx++;
+ }
+
+ if (active_idx >= 0)
+ portList->set_active(active_idx);
+
+ if (ports.size() > 0)
+ portSelect->show();
+ else
+ portSelect->hide();
+}
diff --git a/src/devicewidget.h b/src/devicewidget.h
new file mode 100644
index 0000000..e4ba4af
--- /dev/null
+++ b/src/devicewidget.h
@@ -0,0 +1,84 @@
+/***
+ This file is part of pavucontrol.
+
+ Copyright 2006-2008 Lennart Poettering
+ Copyright 2009 Colin Guthrie
+
+ pavucontrol is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ pavucontrol 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with pavucontrol. If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifndef devicewidget_h
+#define devicewidget_h
+
+#include "pavucontrol.h"
+
+#include "minimalstreamwidget.h"
+
+class ChannelWidget;
+
+class DeviceWidget : public MinimalStreamWidget {
+public:
+ DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+
+ void setChannelMap(const pa_channel_map &m, bool can_decibel);
+ void setVolume(const pa_cvolume &volume, bool force = false);
+ virtual void updateChannelVolume(int channel, pa_volume_t v);
+
+ Gtk::ToggleButton *lockToggleButton, *muteToggleButton, *defaultToggleButton;
+
+ pa_channel_map channelMap;
+ pa_cvolume volume;
+
+ ChannelWidget *channelWidgets[PA_CHANNELS_MAX];
+
+ virtual void onMuteToggleButton();
+ virtual void onDefaultToggleButton();
+ virtual void setDefault(bool isDefault);
+
+ sigc::connection timeoutConnection;
+
+ bool timeoutEvent();
+
+ virtual void executeVolumeUpdate();
+ virtual void setBaseVolume(pa_volume_t v);
+ virtual void setSteps(unsigned n);
+
+ std::vector< std::pair<Glib::ustring,Glib::ustring> > ports;
+ Glib::ustring activePort;
+
+ void prepareMenu();
+
+protected:
+ virtual void onPortChange() = 0;
+
+ /* Tree model columns */
+ class ModelColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+
+ ModelColumns()
+ { add(name); add(desc); }
+
+ Gtk::TreeModelColumn<Glib::ustring> name;
+ Gtk::TreeModelColumn<Glib::ustring> desc;
+ };
+
+ ModelColumns portModel;
+
+ Gtk::HBox *portSelect;
+ Gtk::ComboBox *portList;
+ Glib::RefPtr<Gtk::ListStore> treeModel;
+};
+
+#endif
diff --git a/src/mainwindow.cc b/src/mainwindow.cc
index 4b56b61..563a615 100644
--- a/src/mainwindow.cc
+++ b/src/mainwindow.cc
@@ -46,6 +46,25 @@ struct profile_prio_compare {
}
};
+struct sink_port_prio_compare {
+ bool operator() (const pa_sink_port_info& lhs, const pa_sink_port_info& rhs) const {
+
+ if (lhs.priority == rhs.priority)
+ return strcmp(lhs.name, rhs.name) > 0;
+
+ return lhs.priority > rhs.priority;
+ }
+};
+
+struct source_port_prio_compare {
+ bool operator() (const pa_source_port_info& lhs, const pa_source_port_info& rhs) const {
+
+ if (lhs.priority == rhs.priority)
+ return strcmp(lhs.name, rhs.name) > 0;
+
+ return lhs.priority > rhs.priority;
+ }
+};
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
Gtk::Window(cobject),
@@ -162,11 +181,10 @@ void MainWindow::updateCard(const pa_card_info &info) {
}
w->profiles.clear();
-
for (std::set<pa_card_profile_info>::iterator i = profile_priorities.begin(); i != profile_priorities.end(); ++i)
w->profiles.push_back(std::pair<Glib::ustring,Glib::ustring>(i->name, i->description));
- w->activeProfile = info.active_profile->name;
+ w->activeProfile = info.active_profile ? info.active_profile->name : "";
w->updating = false;
@@ -180,12 +198,12 @@ void MainWindow::updateSink(const pa_sink_info &info) {
SinkWidget *w;
bool is_new = false;
const char *icon;
+ std::set<pa_sink_port_info,sink_port_prio_compare> port_priorities;
if (sinkWidgets.count(info.index))
w = sinkWidgets[info.index];
else {
sinkWidgets[info.index] = w = SinkWidget::create();
- w->beepDevice = info.name;
w->setChannelMap(info.channel_map, !!(info.flags & PA_SINK_DECIBEL_VOLUME));
sinksVBox->pack_start(*w, false, false, 0);
w->index = info.index;
@@ -214,10 +232,23 @@ void MainWindow::updateSink(const pa_sink_info &info) {
w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute);
- w->defaultMenuItem.set_active(w->name == defaultSinkName);
+ w->setDefault(w->name == defaultSinkName);
+
+ port_priorities.clear();
+ for (uint32_t i=0; i<info.n_ports; ++i) {
+ port_priorities.insert(*info.ports[i]);
+ }
+
+ w->ports.clear();
+ for (std::set<pa_sink_port_info>::iterator i = port_priorities.begin(); i != port_priorities.end(); ++i)
+ w->ports.push_back(std::pair<Glib::ustring,Glib::ustring>(i->name, i->description));
+
+ w->activePort = info.active_port ? info.active_port->name : "";
w->updating = false;
+ w->prepareMenu();
+
if (is_new)
updateDeviceVisibility();
}
@@ -327,6 +358,7 @@ void MainWindow::updateSource(const pa_source_info &info) {
SourceWidget *w;
bool is_new = false;
const char *icon;
+ std::set<pa_source_port_info,source_port_prio_compare> port_priorities;
if (sourceWidgets.count(info.index))
w = sourceWidgets[info.index];
@@ -362,10 +394,23 @@ void MainWindow::updateSource(const pa_source_info &info) {
w->setVolume(info.volume);
w->muteToggleButton->set_active(info.mute);
- w->defaultMenuItem.set_active(w->name == defaultSourceName);
+ w->setDefault(w->name == defaultSourceName);
+
+ port_priorities.clear();
+ for (uint32_t i=0; i<info.n_ports; ++i) {
+ port_priorities.insert(*info.ports[i]);
+ }
+
+ w->ports.clear();
+ for (std::set<pa_source_port_info>::iterator i = port_priorities.begin(); i != port_priorities.end(); ++i)
+ w->ports.push_back(std::pair<Glib::ustring,Glib::ustring>(i->name, i->description));
+
+ w->activePort = info.active_port ? info.active_port->name : "";
w->updating = false;
+ w->prepareMenu();
+
if (is_new)
updateDeviceVisibility();
}
@@ -408,7 +453,7 @@ void MainWindow::setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const cha
finish:
- icon->set_from_icon_name(t, Gtk::ICON_SIZE_SMALL_TOOLBAR);
+ set_icon_name_fallback(icon, t, Gtk::ICON_SIZE_SMALL_TOOLBAR);
}
void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
@@ -418,12 +463,11 @@ void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
if (sinkInputWidgets.count(info.index))
w = sinkInputWidgets[info.index];
else {
- sinkInputWidgets[info.index] = w = SinkInputWidget::create();
+ sinkInputWidgets[info.index] = w = SinkInputWidget::create(this);
w->setChannelMap(info.channel_map, true);
streamsVBox->pack_start(*w, false, false, 0);
w->index = info.index;
w->clientIndex = info.client;
- w->mainWindow = this;
is_new = true;
if (pa_context_get_server_protocol_version(get_context()) >= 13)
@@ -434,7 +478,7 @@ void MainWindow::updateSinkInput(const pa_sink_input_info &info) {
w->type = info.client != PA_INVALID_INDEX ? SINK_INPUT_CLIENT : SINK_INPUT_VIRTUAL;
- w->sinkIndex = info.sink;
+ w->setSinkIndex(info.sink);
char *txt;
if (clientNames.count(info.client)) {
@@ -470,18 +514,17 @@ void MainWindow::updateSourceOutput(const pa_source_output_info &info) {
if (sourceOutputWidgets.count(info.index))
w = sourceOutputWidgets[info.index];
else {
- sourceOutputWidgets[info.index] = w = SourceOutputWidget::create();
+ sourceOutputWidgets[info.index] = w = SourceOutputWidget::create(this);
recsVBox->pack_start(*w, false, false, 0);
w->index = info.index;
w->clientIndex = info.client;
- w->mainWindow = this;
}
w->updating = true;
w->type = info.client != PA_INVALID_INDEX ? SOURCE_OUTPUT_CLIENT : SOURCE_OUTPUT_VIRTUAL;
- w->sourceIndex = info.source;
+ w->setSourceIndex(info.source);
char *txt;
if (clientNames.count(info.client)) {
@@ -533,7 +576,8 @@ void MainWindow::updateServer(const pa_server_info &info) {
continue;
w->updating = true;
- w->defaultMenuItem.set_active(w->name == defaultSinkName);
+ w->setDefault(w->name == defaultSinkName);
+
w->updating = false;
}
@@ -544,7 +588,7 @@ void MainWindow::updateServer(const pa_server_info &info) {
continue;
w->updating = true;
- w->defaultMenuItem.set_active(w->name == defaultSourceName);
+ w->setDefault(w->name == defaultSourceName);
w->updating = false;
}
}
@@ -645,7 +689,7 @@ void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_id
for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
SourceOutputWidget* w = i->second;
- if (w->sourceIndex == source_index)
+ if (w->sourceIndex() == source_index)
w->updatePeak(v);
}
}
@@ -673,6 +717,14 @@ void MainWindow::reallyUpdateDeviceVisibility() {
for (std::map<uint32_t, SinkInputWidget*>::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) {
SinkInputWidget* w = i->second;
+ if (sinkWidgets.size() > 1) {
+ w->directionLabel->show();
+ w->deviceButton->show();
+ } else {
+ w->directionLabel->hide();
+ w->deviceButton->hide();
+ }
+
if (showSinkInputType == SINK_INPUT_ALL || w->type == showSinkInputType) {
w->show();
is_empty = false;
@@ -693,6 +745,14 @@ void MainWindow::reallyUpdateDeviceVisibility() {
for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
SourceOutputWidget* w = i->second;
+ if (sourceWidgets.size() > 1) {
+ w->directionLabel->show();
+ w->deviceButton->show();
+ } else {
+ w->directionLabel->hide();
+ w->deviceButton->hide();
+ }
+
if (showSourceOutputType == SOURCE_OUTPUT_ALL || w->type == showSourceOutputType) {
w->show();
is_empty = false;
diff --git a/src/minimalstreamwidget.cc b/src/minimalstreamwidget.cc
index 1044d7a..ac665de 100644
--- a/src/minimalstreamwidget.cc
+++ b/src/minimalstreamwidget.cc
@@ -35,62 +35,14 @@ MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject, const Glib::Re
x->get_widget("channelsVBox", channelsVBox);
x->get_widget("nameLabel", nameLabel);
x->get_widget("boldNameLabel", boldNameLabel);
- x->get_widget("streamToggle", streamToggleButton);
x->get_widget("iconImage", iconImage);
peakProgressBar.set_size_request(-1, 10);
channelsVBox->pack_end(peakProgressBar, false, false);
- streamToggleButton->set_active(false);
- streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onStreamToggleButton));
- menu.signal_deactivate().connect(sigc::mem_fun(*this, &MinimalStreamWidget::onMenuDeactivated));
-
peakProgressBar.hide();
}
-void MinimalStreamWidget::prepareMenu(void) {
-}
-
-void MinimalStreamWidget::onMenuDeactivated(void) {
- streamToggleButton->set_active(false);
-}
-
-void MinimalStreamWidget::popupMenuPosition(int& x, int& y, bool& push_in G_GNUC_UNUSED) {
- Gtk::Requisition r;
-
- streamToggleButton->get_window()->get_origin(x, y);
- r = menu.size_request();
-
- /* Align the right side of the menu with the right side of the togglebutton */
- x += streamToggleButton->get_allocation().get_x();
- x += streamToggleButton->get_allocation().get_width();
- x -= r.width;
-
- /* Align the top of the menu with the buttom of the togglebutton */
- y += streamToggleButton->get_allocation().get_y();
- y += streamToggleButton->get_allocation().get_height();
-}
-
-void MinimalStreamWidget::onStreamToggleButton(void) {
- if (streamToggleButton->get_active()) {
- prepareMenu();
- menu.popup(sigc::mem_fun(*this, &MinimalStreamWidget::popupMenuPosition), 0, gtk_get_current_event_time());
- }
-}
-
-bool MinimalStreamWidget::on_button_press_event (GdkEventButton* event) {
- if (Gtk::VBox::on_button_press_event(event))
- return TRUE;
-
- if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
- prepareMenu();
- menu.popup(0, event->time);
- return TRUE;
- }
-
- return FALSE;
-}
-
#define DECAY_STEP .04
void MinimalStreamWidget::updatePeak(double v) {
diff --git a/src/minimalstreamwidget.h b/src/minimalstreamwidget.h
index af5c9b5..ba7eb1d 100644
--- a/src/minimalstreamwidget.h
+++ b/src/minimalstreamwidget.h
@@ -29,28 +29,18 @@ public:
Gtk::VBox *channelsVBox;
Gtk::Label *nameLabel, *boldNameLabel;
- Gtk::ToggleButton *streamToggleButton;
- Gtk::Menu menu;
Gtk::Image *iconImage;
Gtk::ProgressBar peakProgressBar;
double lastPeak;
bool updating;
- void onStreamToggleButton();
- void onMenuDeactivated();
- void popupMenuPosition(int& x, int& y, bool& push_in);
-
- virtual void prepareMenu(void);
+ virtual void onMuteToggleButton() = 0;
+ virtual void updateChannelVolume(int channel, pa_volume_t v) = 0;
bool volumeMeterEnabled;
void enableVolumeMeter();
void updatePeak(double v);
-
- Glib::ustring beepDevice;
-
-protected:
- virtual bool on_button_press_event(GdkEventButton* event);
};
#endif
diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc
index faf7c89..1e85184 100644
--- a/src/pavucontrol.cc
+++ b/src/pavucontrol.cc
@@ -22,11 +22,13 @@
#include <config.h>
#endif
-#include "pavucontrol.h"
-
+#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
#include <pulse/ext-stream-restore.h>
+#include <canberra-gtk.h>
+
+#include "pavucontrol.h"
#include "i18n.h"
#include "minimalstreamwidget.h"
#include "channelwidget.h"
@@ -217,8 +219,6 @@ void ext_stream_restore_read_cb(
return;
}
- w->createEventRoleWidget();
-
if (eol > 0) {
dec_outstanding(w);
return;
@@ -461,7 +461,11 @@ int main(int argc, char *argv[]) {
ca_context_set_driver(ca_gtk_context_get(), "pulse");
- Gtk::Window* mainWindow = MainWindow::create();
+ MainWindow* mainWindow = MainWindow::create();
+
+ /* Create event widget immediately so it's first in the list */
+ mainWindow->createEventRoleWidget();
+
pa_glib_mainloop *m = pa_glib_mainloop_new(g_main_context_default());
g_assert(m);
diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade
index ff4f00e..4d7a574 100644
--- a/src/pavucontrol.glade
+++ b/src/pavucontrol.glade
@@ -498,7 +498,6 @@ Monitors</property>
<widget class="GtkImage" id="iconImage">
<property name="visible">True</property>
<property name="stock">gtk-missing-image</property>
- <property name="icon-size">4</property>
</widget>
<packing>
<property name="expand">False</property>
@@ -508,6 +507,7 @@ Monitors</property>
<child>
<widget class="GtkHBox" id="hbox12">
<property name="visible">True</property>
+ <property name="spacing">2</property>
<child>
<widget class="GtkLabel" id="boldNameLabel">
<property name="visible">True</property>
@@ -524,12 +524,40 @@ Monitors</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Stream Title</property>
<property name="use_markup">True</property>
- <property name="ellipsize">middle</property>
</widget>
<packing>
+ <property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
+ <child>
+ <widget class="GtkLabel" id="directionLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">direction</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="deviceButton">
+ <property name="label" translatable="yes">Device</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">half</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
</widget>
<packing>
<property name="position">1</property>
@@ -582,17 +610,173 @@ Monitors</property>
<property name="position">1</property>
</packing>
</child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="channelsVBox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="deviceWindow">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">window1</property>
+ <child>
+ <widget class="GtkEventBox" id="deviceWidget">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkVBox" id="streamWidget2">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkVBox" id="vbox26">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkImage" id="iconImage">
+ <property name="visible">True</property>
+ <property name="stock">gtk-missing-image</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkLabel" id="boldNameLabel">
+ <property name="visible">True</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="nameLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Stream Title</property>
+ <property name="use_markup">True</property>
+ <property name="ellipsize">middle</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="spacing">3</property>
<child>
- <widget class="GtkToggleButton" id="streamToggle">
+ <widget class="GtkToggleButton" id="muteToggleButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="tooltip" translatable="yes">Open menu</property>
+ <property name="tooltip" translatable="yes">Mute audio</property>
<property name="relief">none</property>
<child>
- <widget class="GtkArrow" id="arrow3">
+ <widget class="GtkImage" id="image20">
<property name="visible">True</property>
- <property name="arrow_type">down</property>
+ <property name="icon_name">audio-volume-muted</property>
+ <property name="icon-size">1</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToggleButton" id="lockToggleButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip" translatable="yes">Lock channels together</property>
+ <property name="relief">none</property>
+ <property name="active">True</property>
+ <child>
+ <widget class="GtkImage" id="image18">
+ <property name="visible">True</property>
+ <property name="icon_name">stock_lock</property>
+ <property name="icon-size">1</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToggleButton" id="defaultToggleButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip" translatable="yes">Set as fallback</property>
+ <child>
+ <widget class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="icon_name">emblem-default</property>
</widget>
</child>
</widget>
@@ -616,6 +800,35 @@ Monitors</property>
</packing>
</child>
<child>
+ <widget class="GtkHBox" id="portSelect">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Port:&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkComboBox" id="portList">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
<widget class="GtkVBox" id="channelsVBox">
<property name="visible">True</property>
<property name="spacing">6</property>
@@ -629,7 +842,7 @@ Monitors</property>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
</widget>
@@ -727,7 +940,6 @@ Monitors</property>
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="stock">gtk-missing-image</property>
- <property name="icon-size">4</property>
</widget>
<packing>
<property name="expand">False</property>
diff --git a/src/rolewidget.cc b/src/rolewidget.cc
index e86ba3d..31be005 100644
--- a/src/rolewidget.cc
+++ b/src/rolewidget.cc
@@ -32,7 +32,8 @@ RoleWidget::RoleWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade:
StreamWidget(cobject, x) {
lockToggleButton->hide();
- streamToggleButton->hide();
+ directionLabel->hide();
+ deviceButton->hide();
}
RoleWidget* RoleWidget::create() {
@@ -42,6 +43,10 @@ RoleWidget* RoleWidget::create() {
return w;
}
+bool RoleWidget::onContextTriggerEvent(GdkEventButton*) {
+ return false;
+}
+
void RoleWidget::onMuteToggleButton() {
StreamWidget::onMuteToggleButton();
diff --git a/src/rolewidget.h b/src/rolewidget.h
index bbd39d6..c7b198c 100644
--- a/src/rolewidget.h
+++ b/src/rolewidget.h
@@ -35,6 +35,7 @@ public:
virtual void onMuteToggleButton();
virtual void executeVolumeUpdate();
+ virtual bool onContextTriggerEvent(GdkEventButton*);
};
#endif
diff --git a/src/sinkinputwidget.cc b/src/sinkinputwidget.cc
index e5443f1..76d865f 100644
--- a/src/sinkinputwidget.cc
+++ b/src/sinkinputwidget.cc
@@ -29,31 +29,42 @@
#include "i18n.h"
SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- StreamWidget(cobject, x),
- mainWindow(NULL),
- titleMenuItem(_("_Move Stream..."), true),
- killMenuItem(_("_Terminate Stream"), true) {
+ StreamWidget(cobject, x) {
- add_events(Gdk::BUTTON_PRESS_MASK);
+ gchar *txt;
+ directionLabel->set_label(txt = g_markup_printf_escaped("<i>%s</i>", _("on")));
+ g_free(txt);
- menu.append(titleMenuItem);
- titleMenuItem.set_submenu(submenu);
-
- menu.append(killMenuItem);
- killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SinkInputWidget::onKill));
+ terminate.set_label(_("Terminate Playback"));
}
-SinkInputWidget::~SinkInputWidget() {
- clearMenu();
-}
-
-SinkInputWidget* SinkInputWidget::create() {
+SinkInputWidget* SinkInputWidget::create(MainWindow* mainWindow) {
SinkInputWidget* w;
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
x->get_widget_derived("streamWidget", w);
+ w->init(mainWindow);
return w;
}
+SinkInputWidget::~SinkInputWidget(void) {
+ clearMenu();
+}
+
+void SinkInputWidget::setSinkIndex(uint32_t idx) {
+ mSinkIndex = idx;
+
+ if (mpMainWindow->sinkWidgets.count(idx)) {
+ SinkWidget *w = mpMainWindow->sinkWidgets[idx];
+ deviceButton->set_label(w->description.c_str());
+ }
+ else
+ deviceButton->set_label(_("Unknown output"));
+}
+
+uint32_t SinkInputWidget::sinkIndex() {
+ return mSinkIndex;
+}
+
void SinkInputWidget::executeVolumeUpdate() {
pa_operation* o;
@@ -80,30 +91,6 @@ void SinkInputWidget::onMuteToggleButton() {
pa_operation_unref(o);
}
-void SinkInputWidget::prepareMenu() {
- clearMenu();
- buildMenu();
-}
-
-void SinkInputWidget::clearMenu() {
-
- while (!sinkMenuItems.empty()) {
- std::map<uint32_t, SinkMenuItem*>::iterator i = sinkMenuItems.begin();
- delete i->second;
- sinkMenuItems.erase(i);
- }
-}
-
-void SinkInputWidget::buildMenu() {
- for (std::map<uint32_t, SinkWidget*>::iterator i = mainWindow->sinkWidgets.begin(); i != mainWindow->sinkWidgets.end(); ++i) {
- SinkMenuItem *m;
- sinkMenuItems[i->second->index] = m = new SinkMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sinkIndex);
- submenu.append(m->menuItem);
- }
-
- menu.show_all();
-}
-
void SinkInputWidget::onKill() {
pa_operation* o;
if (!(o = pa_context_kill_sink_input(get_context(), index, NULL, NULL))) {
@@ -114,19 +101,44 @@ void SinkInputWidget::onKill() {
pa_operation_unref(o);
}
+void SinkInputWidget::clearMenu() {
+ while (!sinkMenuItems.empty()) {
+ std::map<uint32_t, SinkMenuItem*>::iterator i = sinkMenuItems.begin();
+ delete i->second;
+ sinkMenuItems.erase(i);
+ }
+}
+
+void SinkInputWidget::buildMenu() {
+ for (std::map<uint32_t, SinkWidget*>::iterator i = mpMainWindow->sinkWidgets.begin(); i != mpMainWindow->sinkWidgets.end(); ++i) {
+ SinkMenuItem *m;
+ sinkMenuItems[i->second->index] = m = new SinkMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == mSinkIndex);
+ menu.append(m->menuItem);
+ }
+ menu.show_all();
+}
+
void SinkInputWidget::SinkMenuItem::onToggle() {
+ if (widget->updating)
+ return;
- if (widget->updating)
- return;
+ if (!menuItem.get_active())
+ return;
- if (!menuItem.get_active())
- return;
+ /*if (!mpMainWindow->sinkWidgets.count(widget->index))
+ return;*/
- pa_operation* o;
- if (!(o = pa_context_move_sink_input_by_index(get_context(), widget->index, index, NULL, NULL))) {
- show_error(_("pa_context_move_sink_input_by_index() failed"));
- return;
- }
+ pa_operation* o;
+ if (!(o = pa_context_move_sink_input_by_index(get_context(), widget->index, index, NULL, NULL))) {
+ show_error(_("pa_context_move_sink_input_by_index() failed"));
+ return;
+ }
- pa_operation_unref(o);
+ pa_operation_unref(o);
+}
+
+void SinkInputWidget::onDeviceChangePopup() {
+ clearMenu();
+ buildMenu();
+ menu.popup(1, 0);
}
diff --git a/src/sinkinputwidget.h b/src/sinkinputwidget.h
index 6db504b..11893ff 100644
--- a/src/sinkinputwidget.h
+++ b/src/sinkinputwidget.h
@@ -30,41 +30,44 @@ class MainWindow;
class SinkInputWidget : public StreamWidget {
public:
SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static SinkInputWidget* create();
- virtual ~SinkInputWidget();
+ static SinkInputWidget* create(MainWindow* mainWindow);
+ ~SinkInputWidget(void);
SinkInputType type;
- uint32_t index, clientIndex, sinkIndex;
+ uint32_t index, clientIndex;
+ void setSinkIndex(uint32_t idx);
+ uint32_t sinkIndex();
virtual void executeVolumeUpdate();
+ virtual void onDeviceChangePopup();
virtual void onMuteToggleButton();
virtual void onKill();
- virtual void prepareMenu();
- MainWindow *mainWindow;
- Gtk::Menu submenu;
- Gtk::MenuItem titleMenuItem, killMenuItem;
+private:
+ uint32_t mSinkIndex;
+
+ void clearMenu();
+ void buildMenu();
+
+ Gtk::Menu menu;
struct SinkMenuItem {
- SinkMenuItem(SinkInputWidget *w, const char *label, uint32_t i, bool active) :
- widget(w),
- menuItem(label),
- index(i) {
- menuItem.set_active(active);
- menuItem.set_draw_as_radio(true);
- menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkMenuItem::onToggle));
- }
-
- SinkInputWidget *widget;
- Gtk::CheckMenuItem menuItem;
- uint32_t index;
- void onToggle();
+ SinkMenuItem(SinkInputWidget *w, const char *label, uint32_t i, bool active) :
+ widget(w),
+ menuItem(label),
+ index(i) {
+ menuItem.set_active(active);
+ menuItem.set_draw_as_radio(true);
+ menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkMenuItem::onToggle));
+ }
+
+ SinkInputWidget *widget;
+ Gtk::CheckMenuItem menuItem;
+ uint32_t index;
+ void onToggle();
};
std::map<uint32_t, SinkMenuItem*> sinkMenuItems;
-
- void clearMenu();
- void buildMenu();
};
#endif
diff --git a/src/sinkwidget.cc b/src/sinkwidget.cc
index 8d21bb0..0c39cae 100644
--- a/src/sinkwidget.cc
+++ b/src/sinkwidget.cc
@@ -22,31 +22,27 @@
#include <config.h>
#endif
+#include <canberra-gtk.h>
+
#include "sinkwidget.h"
#include "i18n.h"
SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- StreamWidget(cobject, x),
- defaultMenuItem("_Default", true){
-
- add_events(Gdk::BUTTON_PRESS_MASK);
-
- defaultMenuItem.set_active(false);
- defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onDefaultToggle));
- menu.append(defaultMenuItem);
- menu.show_all();
+ DeviceWidget(cobject, x) {
}
SinkWidget* SinkWidget::create() {
SinkWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
- x->get_widget_derived("streamWidget", w);
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "deviceWidget");
+ x->get_widget_derived("deviceWidget", w);
return w;
}
void SinkWidget::executeVolumeUpdate() {
pa_operation* o;
+ char dev[64];
+ int playing = 0;
if (!(o = pa_context_set_sink_volume_by_index(get_context(), index, &volume, NULL, NULL))) {
show_error(_("pa_context_set_sink_volume_by_index() failed"));
@@ -54,10 +50,27 @@ void SinkWidget::executeVolumeUpdate() {
}
pa_operation_unref(o);
+
+ ca_context_playing(ca_gtk_context_get(), 2, &playing);
+ if (playing)
+ return;
+
+ snprintf(dev, sizeof(dev), "%lu", (unsigned long) index);
+ ca_context_change_device(ca_gtk_context_get(), dev);
+
+ ca_gtk_play_for_widget(GTK_WIDGET(gobj()),
+ 2,
+ CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"),
+ CA_PROP_EVENT_ID, "audio-volume-change",
+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
+ CA_PROP_CANBERRA_ENABLE, "1",
+ NULL);
+
+ ca_context_change_device(ca_gtk_context_get(), NULL);
}
void SinkWidget::onMuteToggleButton() {
- StreamWidget::onMuteToggleButton();
+ DeviceWidget::onMuteToggleButton();
if (updating)
return;
@@ -71,7 +84,7 @@ void SinkWidget::onMuteToggleButton() {
pa_operation_unref(o);
}
-void SinkWidget::onDefaultToggle() {
+void SinkWidget::onDefaultToggleButton() {
pa_operation* o;
if (updating)
@@ -83,3 +96,26 @@ void SinkWidget::onDefaultToggle() {
}
pa_operation_unref(o);
}
+
+void SinkWidget::onPortChange() {
+ Gtk::TreeModel::iterator iter;
+
+ if (updating)
+ return;
+
+ iter = portList->get_active();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ if (row) {
+ pa_operation* o;
+ Glib::ustring port = row[portModel.name];
+
+ if (!(o = pa_context_set_sink_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) {
+ show_error(_("pa_context_set_sink_port_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+ }
+ }
+}
diff --git a/src/sinkwidget.h b/src/sinkwidget.h
index d5ce315..efabc7c 100644
--- a/src/sinkwidget.h
+++ b/src/sinkwidget.h
@@ -23,9 +23,9 @@
#include "pavucontrol.h"
-#include "streamwidget.h"
+#include "devicewidget.h"
-class SinkWidget : public StreamWidget {
+class SinkWidget : public DeviceWidget {
public:
SinkWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
static SinkWidget* create();
@@ -36,11 +36,12 @@ public:
uint32_t index, monitor_index, card_index;
bool can_decibel;
- Gtk::CheckMenuItem defaultMenuItem;
-
virtual void onMuteToggleButton();
virtual void executeVolumeUpdate();
- virtual void onDefaultToggle();
+ virtual void onDefaultToggleButton();
+
+protected:
+ virtual void onPortChange();
};
#endif
diff --git a/src/sourceoutputwidget.cc b/src/sourceoutputwidget.cc
index eafe620..cb995b5 100644
--- a/src/sourceoutputwidget.cc
+++ b/src/sourceoutputwidget.cc
@@ -29,31 +29,42 @@
#include "i18n.h"
SourceOutputWidget::SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- MinimalStreamWidget(cobject, x),
- mainWindow(NULL),
- titleMenuItem(_("_Move Stream..."), true),
- killMenuItem(_("_Terminate Stream"), true) {
+ StreamWidget(cobject, x) {
- add_events(Gdk::BUTTON_PRESS_MASK);
+ gchar *txt;
+ directionLabel->set_label(txt = g_markup_printf_escaped("<i>%s</i>", _("from")));
+ g_free(txt);
- menu.append(titleMenuItem);
- titleMenuItem.set_submenu(submenu);
-
- menu.append(killMenuItem);
- killMenuItem.signal_activate().connect(sigc::mem_fun(*this, &SourceOutputWidget::onKill));
-}
-
-SourceOutputWidget::~SourceOutputWidget() {
- clearMenu();
+ terminate.set_label(_("Terminate Recording"));
}
-SourceOutputWidget* SourceOutputWidget::create() {
+SourceOutputWidget* SourceOutputWidget::create(MainWindow* mainWindow) {
SourceOutputWidget* w;
Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
x->get_widget_derived("streamWidget", w);
+ w->init(mainWindow);
return w;
}
+SourceOutputWidget::~SourceOutputWidget(void) {
+ clearMenu();
+}
+
+void SourceOutputWidget::setSourceIndex(uint32_t idx) {
+ mSourceIndex = idx;
+
+ if (mpMainWindow->sourceWidgets.count(idx)) {
+ SourceWidget *w = mpMainWindow->sourceWidgets[idx];
+ deviceButton->set_label(w->description.c_str());
+ }
+ else
+ deviceButton->set_label(_("Unknown input"));
+}
+
+uint32_t SourceOutputWidget::sourceIndex() {
+ return mSourceIndex;
+}
+
void SourceOutputWidget::onKill() {
pa_operation* o;
if (!(o = pa_context_kill_source_output(get_context(), index, NULL, NULL))) {
@@ -64,43 +75,45 @@ void SourceOutputWidget::onKill() {
pa_operation_unref(o);
}
-void SourceOutputWidget::clearMenu() {
- while (!sourceMenuItems.empty()) {
- std::map<uint32_t, SourceMenuItem*>::iterator i = sourceMenuItems.begin();
- delete i->second;
- sourceMenuItems.erase(i);
- }
+void SourceOutputWidget::clearMenu() {
+ while (!sourceMenuItems.empty()) {
+ std::map<uint32_t, SourceMenuItem*>::iterator i = sourceMenuItems.begin();
+ delete i->second;
+ sourceMenuItems.erase(i);
+ }
}
void SourceOutputWidget::buildMenu() {
- for (std::map<uint32_t, SourceWidget*>::iterator i = mainWindow->sourceWidgets.begin(); i != mainWindow->sourceWidgets.end(); ++i) {
- SourceMenuItem *m;
- sourceMenuItems[i->second->index] = m = new SourceMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == sourceIndex);
- submenu.append(m->menuItem);
- }
-
- menu.show_all();
-}
-
-void SourceOutputWidget::prepareMenu(void) {
- clearMenu();
- buildMenu();
+ for (std::map<uint32_t, SourceWidget*>::iterator i = mpMainWindow->sourceWidgets.begin(); i != mpMainWindow->sourceWidgets.end(); ++i) {
+ SourceMenuItem *m;
+ sourceMenuItems[i->second->index] = m = new SourceMenuItem(this, i->second->description.c_str(), i->second->index, i->second->index == mSourceIndex);
+ menu.append(m->menuItem);
+ }
+ menu.show_all();
}
void SourceOutputWidget::SourceMenuItem::onToggle() {
+ if (widget->updating)
+ return;
- if (widget->updating)
- return;
+ if (!menuItem.get_active())
+ return;
- if (!menuItem.get_active())
- return;
+ /*if (!mpMainWindow->sourceWidgets.count(widget->index))
+ return;*/
- pa_operation* o;
- if (!(o = pa_context_move_source_output_by_index(get_context(), widget->index, index, NULL, NULL))) {
- show_error(_("pa_context_move_source_output_by_index() failed"));
- return;
- }
+ pa_operation* o;
+ if (!(o = pa_context_move_source_output_by_index(get_context(), widget->index, index, NULL, NULL))) {
+ show_error(_("pa_context_move_source_output_by_index() failed"));
+ return;
+ }
- pa_operation_unref(o);
+ pa_operation_unref(o);
+}
+
+void SourceOutputWidget::onDeviceChangePopup() {
+ clearMenu();
+ buildMenu();
+ menu.popup(1, 0);
}
diff --git a/src/sourceoutputwidget.h b/src/sourceoutputwidget.h
index cdaf28a..0b88d06 100644
--- a/src/sourceoutputwidget.h
+++ b/src/sourceoutputwidget.h
@@ -23,46 +23,49 @@
#include "pavucontrol.h"
-#include "minimalstreamwidget.h"
+#include "streamwidget.h"
class MainWindow;
-class SourceOutputWidget : public MinimalStreamWidget {
+class SourceOutputWidget : public StreamWidget {
public:
SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
- static SourceOutputWidget* create();
- virtual ~SourceOutputWidget();
+ static SourceOutputWidget* create(MainWindow* mainWindow);
+ ~SourceOutputWidget(void);
SourceOutputType type;
- uint32_t index, clientIndex, sourceIndex;
+ uint32_t index, clientIndex;
+ void setSourceIndex(uint32_t idx);
+ uint32_t sourceIndex();
+ virtual void onDeviceChangePopup();
virtual void onKill();
- MainWindow *mainWindow;
- Gtk::Menu submenu;
- Gtk::MenuItem titleMenuItem, killMenuItem;
+private:
+ uint32_t mSourceIndex;
+
+ void clearMenu();
+ void buildMenu();
+
+ Gtk::Menu menu;
struct SourceMenuItem {
- SourceMenuItem(SourceOutputWidget *w, const char *label, uint32_t i, bool active) :
- widget(w),
- menuItem(label),
- index(i) {
- menuItem.set_active(active);
- menuItem.set_draw_as_radio(true);
- menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceMenuItem::onToggle));
- }
-
- SourceOutputWidget *widget;
- Gtk::CheckMenuItem menuItem;
- uint32_t index;
- void onToggle();
+ SourceMenuItem(SourceOutputWidget *w, const char *label, uint32_t i, bool active) :
+ widget(w),
+ menuItem(label),
+ index(i) {
+ menuItem.set_active(active);
+ menuItem.set_draw_as_radio(true);
+ menuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceMenuItem::onToggle));
+ }
+
+ SourceOutputWidget *widget;
+ Gtk::CheckMenuItem menuItem;
+ uint32_t index;
+ void onToggle();
};
std::map<uint32_t, SourceMenuItem*> sourceMenuItems;
-
- void clearMenu();
- void buildMenu();
- virtual void prepareMenu();
};
#endif
diff --git a/src/sourcewidget.cc b/src/sourcewidget.cc
index dc13f7f..2779df4 100644
--- a/src/sourcewidget.cc
+++ b/src/sourcewidget.cc
@@ -27,21 +27,13 @@
#include "i18n.h"
SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- StreamWidget(cobject, x),
- defaultMenuItem(_("_Default"), true){
-
- add_events(Gdk::BUTTON_PRESS_MASK);
-
- defaultMenuItem.set_active(false);
- defaultMenuItem.signal_toggled().connect(sigc::mem_fun(*this, &SourceWidget::onDefaultToggle));
- menu.append(defaultMenuItem);
- menu.show_all();
+ DeviceWidget(cobject, x) {
}
SourceWidget* SourceWidget::create() {
SourceWidget* w;
- Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "streamWidget");
- x->get_widget_derived("streamWidget", w);
+ Glib::RefPtr<Gnome::Glade::Xml> x = Gnome::Glade::Xml::create(GLADE_FILE, "deviceWidget");
+ x->get_widget_derived("deviceWidget", w);
return w;
}
@@ -57,7 +49,7 @@ void SourceWidget::executeVolumeUpdate() {
}
void SourceWidget::onMuteToggleButton() {
- StreamWidget::onMuteToggleButton();
+ DeviceWidget::onMuteToggleButton();
if (updating)
return;
@@ -71,7 +63,7 @@ void SourceWidget::onMuteToggleButton() {
pa_operation_unref(o);
}
-void SourceWidget::onDefaultToggle() {
+void SourceWidget::onDefaultToggleButton() {
pa_operation* o;
if (updating)
@@ -83,3 +75,28 @@ void SourceWidget::onDefaultToggle() {
}
pa_operation_unref(o);
}
+
+void SourceWidget::onPortChange() {
+ Gtk::TreeModel::iterator iter;
+
+ if (updating)
+ return;
+
+ iter = portList->get_active();
+ if (iter)
+ {
+ Gtk::TreeModel::Row row = *iter;
+ if (row)
+ {
+ pa_operation* o;
+ Glib::ustring port = row[portModel.name];
+
+ if (!(o = pa_context_set_source_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) {
+ show_error(_("pa_context_set_source_port_by_index() failed"));
+ return;
+ }
+
+ pa_operation_unref(o);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/sourcewidget.h b/src/sourcewidget.h
index 2fd137e..f0088b4 100644
--- a/src/sourcewidget.h
+++ b/src/sourcewidget.h
@@ -23,9 +23,9 @@
#include "pavucontrol.h"
-#include "streamwidget.h"
+#include "devicewidget.h"
-class SourceWidget : public StreamWidget {
+class SourceWidget : public DeviceWidget {
public:
SourceWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
static SourceWidget* create();
@@ -36,11 +36,12 @@ public:
uint32_t index, card_index;
bool can_decibel;
- Gtk::CheckMenuItem defaultMenuItem;
-
virtual void onMuteToggleButton();
virtual void executeVolumeUpdate();
- virtual void onDefaultToggle();
+ virtual void onDefaultToggleButton();
+
+protected:
+ virtual void onPortChange();
};
#endif
diff --git a/src/streamwidget.cc b/src/streamwidget.cc
index fae605f..2ab5c12 100644
--- a/src/streamwidget.cc
+++ b/src/streamwidget.cc
@@ -23,31 +23,55 @@
#endif
#include "streamwidget.h"
+#include "mainwindow.h"
#include "channelwidget.h"
-/*** StreamWidget ***/
+#include "i18n.h"
+/*** StreamWidget ***/
StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
- MinimalStreamWidget(cobject, x) {
+ MinimalStreamWidget(cobject, x),
+ mpMainWindow(NULL) {
x->get_widget("lockToggleButton", lockToggleButton);
x->get_widget("muteToggleButton", muteToggleButton);
+ x->get_widget("directionLabel", directionLabel);
+ x->get_widget("deviceButton", deviceButton);
+ this->signal_button_press_event().connect(sigc::mem_fun(*this, &StreamWidget::onContextTriggerEvent));
muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton));
+ deviceButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onDeviceChangePopup));
+
+ terminate.set_label(_("Terminate"));
+ terminate.signal_activate().connect(sigc::mem_fun(*this, &StreamWidget::onKill));
+ contextMenu.append(terminate);
+ contextMenu.show_all();
for (unsigned i = 0; i < PA_CHANNELS_MAX; i++)
channelWidgets[i] = NULL;
}
+
+void StreamWidget::init(MainWindow* mainWindow) {
+ mpMainWindow = mainWindow;
+}
+
+bool StreamWidget::onContextTriggerEvent(GdkEventButton* event) {
+ if (GDK_BUTTON_PRESS == event->type && 3 == event->button) {
+ contextMenu.popup(event->button, event->time);
+ return true;
+ }
+ return false;
+}
+
void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) {
channelMap = m;
for (int i = 0; i < m.channels; i++) {
ChannelWidget *cw = channelWidgets[i] = ChannelWidget::create();
- cw->beepDevice = beepDevice;
cw->channel = i;
cw->can_decibel = can_decibel;
- cw->streamWidget = this;
+ cw->minimalStreamWidget = this;
char text[64];
snprintf(text, sizeof(text), "<b>%s</b>", pa_channel_position_to_pretty_string(m.map[i]));
cw->channelLabel->set_markup(text);
@@ -101,15 +125,8 @@ bool StreamWidget::timeoutEvent() {
void StreamWidget::executeVolumeUpdate() {
}
-
-void StreamWidget::setBaseVolume(pa_volume_t v) {
-
- if (channelMap.channels > 0)
- channelWidgets[channelMap.channels-1]->setBaseVolume(v);
+void StreamWidget::onDeviceChangePopup() {
}
-void StreamWidget::setSteps(unsigned n) {
-
- for (int i = 0; i < channelMap.channels; i++)
- channelWidgets[channelMap.channels-1]->setSteps(n);
+void StreamWidget::onKill() {
}
diff --git a/src/streamwidget.h b/src/streamwidget.h
index af5f0b9..215e332 100644
--- a/src/streamwidget.h
+++ b/src/streamwidget.h
@@ -25,17 +25,21 @@
#include "minimalstreamwidget.h"
+class MainWindow;
class ChannelWidget;
class StreamWidget : public MinimalStreamWidget {
public:
StreamWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
+ void init(MainWindow* mainWindow);
void setChannelMap(const pa_channel_map &m, bool can_decibel);
void setVolume(const pa_cvolume &volume, bool force = false);
virtual void updateChannelVolume(int channel, pa_volume_t v);
Gtk::ToggleButton *lockToggleButton, *muteToggleButton;
+ Gtk::Label *directionLabel;
+ Gtk::Button *deviceButton;
pa_channel_map channelMap;
pa_cvolume volume;
@@ -43,14 +47,21 @@ public:
ChannelWidget *channelWidgets[PA_CHANNELS_MAX];
virtual void onMuteToggleButton();
+ virtual void onDeviceChangePopup();
+ virtual bool onContextTriggerEvent(GdkEventButton*);
sigc::connection timeoutConnection;
bool timeoutEvent();
virtual void executeVolumeUpdate();
- virtual void setBaseVolume(pa_volume_t v);
- virtual void setSteps(unsigned n);
+ virtual void onKill();
+
+protected:
+ MainWindow* mpMainWindow;
+
+ Gtk::Menu contextMenu;
+ Gtk::MenuItem terminate;
};
#endif