diff options
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | doc/README.html.in | 8 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cardwidget.h | 1 | ||||
-rw-r--r-- | src/channelwidget.cc | 26 | ||||
-rw-r--r-- | src/channelwidget.h | 8 | ||||
-rw-r--r-- | src/devicewidget.cc | 153 | ||||
-rw-r--r-- | src/devicewidget.h | 84 | ||||
-rw-r--r-- | src/mainwindow.cc | 90 | ||||
-rw-r--r-- | src/minimalstreamwidget.cc | 48 | ||||
-rw-r--r-- | src/minimalstreamwidget.h | 14 | ||||
-rw-r--r-- | src/pavucontrol.cc | 14 | ||||
-rw-r--r-- | src/pavucontrol.glade | 228 | ||||
-rw-r--r-- | src/rolewidget.cc | 7 | ||||
-rw-r--r-- | src/rolewidget.h | 1 | ||||
-rw-r--r-- | src/sinkinputwidget.cc | 110 | ||||
-rw-r--r-- | src/sinkinputwidget.h | 49 | ||||
-rw-r--r-- | src/sinkwidget.cc | 62 | ||||
-rw-r--r-- | src/sinkwidget.h | 11 | ||||
-rw-r--r-- | src/sourceoutputwidget.cc | 99 | ||||
-rw-r--r-- | src/sourceoutputwidget.h | 53 | ||||
-rw-r--r-- | src/sourcewidget.cc | 43 | ||||
-rw-r--r-- | src/sourcewidget.h | 11 | ||||
-rw-r--r-- | src/streamwidget.cc | 43 | ||||
-rw-r--r-- | src/streamwidget.h | 15 |
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 <@PACKAGE_BUGREPORT@>, April 2009</address> +<address class="grey">Lennart Poettering <@PACKAGE_BUGREPORT@>, 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"><b>Port:</b></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 |