From e0c299d979888d4f7ab36720703f62edd327e2a1 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 28 Feb 2009 17:11:41 +0000 Subject: [PATCH 1/6] Initial support for changing card profiles. This hijacks the Output Devices tab to display things for now. I'm currently undecided as to whether a new tab or more intellegent handling display of the Card Widget is best. --- src/pavucontrol.cc | 301 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 292 insertions(+), 9 deletions(-) diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index 2bf3463..9634b52 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -152,6 +153,59 @@ public: virtual void executeVolumeUpdate(); }; +class CardWidget : public Gtk::VBox { +public: + CardWidget(BaseObjectType* cobject, const Glib::RefPtr& x); + static CardWidget* create(); + virtual ~CardWidget(); + + Gtk::Label *nameLabel, *boldNameLabel; + Gtk::ToggleButton *streamToggleButton; + Gtk::Menu menu; + Gtk::Image *iconImage; + Glib::ustring name; + uint32_t index; + bool updating; + + void onStreamToggleButton(); + void onMenuDeactivated(); + void popupMenuPosition(int& x, int& y, bool& push_in); + + std::map profiles; + Glib::ustring activeProfile; + bool hasSinks; + bool hasSources; + + MainWindow *mainWindow; + Gtk::Menu submenu; + Gtk::MenuItem profilesMenuItem; + + struct ProfileMenuItem { + ProfileMenuItem(CardWidget *w, const char *label, Glib::ustring profile, bool active) : + widget(w), + menuItem(label), + profile(profile) { + menuItem.set_active(active); + menuItem.set_draw_as_radio(true); + menuItem.signal_toggled().connect(sigc::mem_fun(*this, &ProfileMenuItem::onToggle)); + } + + CardWidget *widget; + Gtk::CheckMenuItem menuItem; + Glib::ustring profile; + void onToggle(); + }; + + std::map profileMenuItems; + + void clearMenu(); + void buildMenu(); + virtual void prepareMenu(void); + +protected: + virtual bool on_button_press_event(GdkEventButton* event); +}; + class SinkWidget : public StreamWidget { public: SinkWidget(BaseObjectType* cobject, const Glib::RefPtr& x); @@ -160,7 +214,7 @@ public: SinkType type; Glib::ustring description; Glib::ustring name; - uint32_t index, monitor_index; + uint32_t index, monitor_index, card_index; bool can_decibel; Gtk::CheckMenuItem defaultMenuItem; @@ -178,7 +232,7 @@ public: SourceType type; Glib::ustring name; Glib::ustring description; - uint32_t index; + uint32_t index, card_index; bool can_decibel; Gtk::CheckMenuItem defaultMenuItem; @@ -284,6 +338,7 @@ public: static MainWindow* create(); virtual ~MainWindow(); + void updateCard(const pa_card_info &info); void updateSink(const pa_sink_info &info); void updateSource(const pa_source_info &info); void updateSinkInput(const pa_sink_input_info &info); @@ -293,6 +348,7 @@ public: void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v); void updateRole(const pa_ext_stream_restore_info &info); + void removeCard(uint32_t index); void removeSink(uint32_t index); void removeSource(uint32_t index); void removeSinkInput(uint32_t index); @@ -304,6 +360,7 @@ public: Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel; Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox; + std::map cardWidgets; std::map sinkWidgets; std::map sourceWidgets; std::map sinkInputWidgets; @@ -434,6 +491,121 @@ void ChannelWidget::set_sensitive(bool enabled) { volumeScale->set_sensitive(enabled); } + + +/*** CardWidget ***/ +CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : + Gtk::VBox(cobject), + profilesMenuItem(_("_Set Profile..."), true) { + + x->get_widget("nameLabel", nameLabel); + x->get_widget("boldNameLabel", boldNameLabel); + x->get_widget("streamToggle", streamToggleButton); + x->get_widget("iconImage", iconImage); + + streamToggleButton->set_active(false); + streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &CardWidget::onStreamToggleButton)); + menu.signal_deactivate().connect(sigc::mem_fun(*this, &CardWidget::onMenuDeactivated)); + + menu.append(profilesMenuItem); + profilesMenuItem.set_submenu(submenu); +} + +CardWidget::~CardWidget() { + clearMenu(); +} + +CardWidget* CardWidget::create() { + CardWidget* w; + Glib::RefPtr x = Gnome::Glade::Xml::create(GLADE_FILE, "minimalStreamWidget"); + x->get_widget_derived("minimalStreamWidget", w); + return w; +} + +void CardWidget::prepareMenu() { + clearMenu(); + buildMenu(); +} + +void CardWidget::clearMenu() { + + while (!profileMenuItems.empty()) { + std::map::iterator i = profileMenuItems.begin(); + delete i->second; + profileMenuItems.erase(i); + } +} + +void CardWidget::buildMenu() { + int num = 0; + for (std::map::iterator i = profiles.begin(); i != profiles.end(); ++i) { + ProfileMenuItem *m; + profileMenuItems[num++] = m = new ProfileMenuItem(this, i->second.c_str(), i->first.c_str(), i->first == activeProfile); + submenu.append(m->menuItem); + } + + menu.show_all(); +} + +void CardWidget::ProfileMenuItem::onToggle() { + + if (widget->updating) + return; + + if (!menuItem.get_active()) + return; + + pa_operation* o; + if (!(o = pa_context_set_card_profile_by_index(context, widget->index, profile.c_str(), NULL, NULL))) { + show_error(_("pa_context_set_card_profile_by_index() failed")); + return; + } + + pa_operation_unref(o); +} + + +void CardWidget::onMenuDeactivated(void) { + streamToggleButton->set_active(false); +} + +void CardWidget::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 CardWidget::onStreamToggleButton(void) { + if (streamToggleButton->get_active()) { + prepareMenu(); + menu.popup(sigc::mem_fun(*this, &CardWidget::popupMenuPosition), 0, gtk_get_current_event_time()); + } +} + +bool CardWidget::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; +} + + /*** MinimalStreamWidget ***/ MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : Gtk::VBox(cobject), @@ -1005,6 +1177,50 @@ MainWindow::~MainWindow() { } } +void MainWindow::updateCard(const pa_card_info &info) { + CardWidget *w; + bool is_new = false; + + if (cardWidgets.count(info.index)) + w = cardWidgets[info.index]; + else { + cardWidgets[info.index] = w = CardWidget::create(); + sinksVBox->pack_start(*w, false, false, 0); + //sourcesVBox->pack_start(*w, false, false, 0); + w->index = info.index; + is_new = true; + } + + w->updating = true; + + if (NULL != info.proplist && pa_proplist_contains(info.proplist, "alsa.card_name")) + w->name = pa_proplist_gets(info.proplist, "alsa.card_name"); + else + w->name = info.name; + + w->boldNameLabel->set_text("Card: "); + gchar *txt; + w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", w->name.c_str())); + g_free(txt); + + w->iconImage->set_from_icon_name("audio-card", Gtk::ICON_SIZE_SMALL_TOOLBAR); + + w->hasSinks = w->hasSources = false; + w->profiles.clear(); + for (int i=0; ihasSinks = w->hasSinks || (info.profiles[i].n_sinks > 0); + w->hasSources = w->hasSources || (info.profiles[i].n_sources > 0); + w->profiles.insert(std::pair(info.profiles[i].name, info.profiles[i].description)); + } + w->activeProfile = info.active_profile->name; + //w->defaultMenuItem.set_active(w->name == defaultSinkName); + + w->updating = false; + + if (is_new) + updateDeviceVisibility(); +} + void MainWindow::updateSink(const pa_sink_info &info) { SinkWidget *w; bool is_new = false; @@ -1023,6 +1239,7 @@ void MainWindow::updateSink(const pa_sink_info &info) { w->updating = true; + w->card_index = info.card; w->name = info.name; w->description = info.description; w->type = info.flags & PA_SINK_HARDWARE ? SINK_HARDWARE : SINK_VIRTUAL; @@ -1165,6 +1382,7 @@ void MainWindow::updateSource(const pa_source_info &info) { w->updating = true; + w->card_index = info.card; w->name = info.name; w->description = info.description; w->type = info.monitor_of_sink != PA_INVALID_INDEX ? SOURCE_MONITOR : (info.flags & PA_SOURCE_HARDWARE ? SOURCE_HARDWARE : SOURCE_VIRTUAL); @@ -1486,6 +1704,8 @@ void MainWindow::updateDeviceVisibility() { void MainWindow::reallyUpdateDeviceVisibility() { bool is_empty = true; + std::set visible_cards; + std::set::iterator it_card; for (std::map::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) { SinkInputWidget* w = i->second; @@ -1523,6 +1743,7 @@ void MainWindow::reallyUpdateDeviceVisibility() { noRecsLabel->hide(); is_empty = true; + visible_cards.clear(); for (std::map::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) { SinkWidget* w = i->second; @@ -1530,6 +1751,18 @@ void MainWindow::reallyUpdateDeviceVisibility() { if (showSinkType == SINK_ALL || w->type == showSinkType) { w->show(); is_empty = false; + if (w->card_index != PA_INVALID_INDEX) + visible_cards.insert(w->card_index); + } else + w->hide(); + } + + for (std::map::iterator i = cardWidgets.begin(); i != cardWidgets.end(); ++i) { + CardWidget* w = i->second; + + if (true || (!visible_cards.count(w->index) && w->hasSinks)) { + w->show(); + is_empty = false; } else w->hide(); } @@ -1570,6 +1803,15 @@ void MainWindow::reallyUpdateDeviceVisibility() { recsVBox->show(); } +void MainWindow::removeCard(uint32_t index) { + if (!cardWidgets.count(index)) + return; + + delete cardWidgets[index]; + cardWidgets.erase(index); + updateDeviceVisibility(); +} + void MainWindow::removeSink(uint32_t index) { if (!sinkWidgets.count(index)) return; @@ -1655,6 +1897,25 @@ static void dec_outstanding(MainWindow *w) { w->get_window()->set_cursor(); } +void card_cb(pa_context *, const pa_card_info *i, int eol, void *userdata) { + MainWindow *w = static_cast(userdata); + + if (eol < 0) { + if (pa_context_errno(context) == PA_ERR_NOENTITY) + return; + + show_error(_("Card callback failure")); + return; + } + + if (eol > 0) { + dec_outstanding(w); + return; + } + + w->updateCard(*i); +} + void sink_cb(pa_context *, const pa_sink_info *i, int eol, void *userdata) { MainWindow *w = static_cast(userdata); @@ -1883,13 +2144,28 @@ void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, break; case PA_SUBSCRIPTION_EVENT_SERVER: { - pa_operation *o; - if (!(o = pa_context_get_server_info(c, server_info_cb, w))) { - show_error(_("pa_context_get_server_info() failed")); - return; + pa_operation *o; + if (!(o = pa_context_get_server_info(c, server_info_cb, w))) { + show_error(_("pa_context_get_server_info() failed")); + return; + } + pa_operation_unref(o); } - pa_operation_unref(o); - } + break; + + case PA_SUBSCRIPTION_EVENT_CARD: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) + w->removeCard(index); + else { + pa_operation *o; + if (!(o = pa_context_get_card_info_by_index(c, index, card_cb, w))) { + show_error(_("pa_context_get_card_info_by_index() failed")); + return; + } + pa_operation_unref(o); + } + break; + } } @@ -1916,7 +2192,8 @@ void context_state_callback(pa_context *c, void *userdata) { PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| PA_SUBSCRIPTION_MASK_CLIENT| - PA_SUBSCRIPTION_MASK_SERVER), NULL, NULL))) { + PA_SUBSCRIPTION_MASK_SERVER| + PA_SUBSCRIPTION_MASK_CARD), NULL, NULL))) { show_error(_("pa_context_subscribe() failed")); return; } @@ -1934,6 +2211,12 @@ void context_state_callback(pa_context *c, void *userdata) { } pa_operation_unref(o); + if (!(o = pa_context_get_card_info_list(c, card_cb, w))) { + show_error(_("pa_context_get_card_info_list() failed")); + return; + } + pa_operation_unref(o); + if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) { show_error(_("pa_context_get_sink_info_list() failed")); return; From 3372cd120cdeca82a1f9d89930e9e1fe3323ee21 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 28 Feb 2009 17:35:36 +0000 Subject: [PATCH 2/6] Silence some warnings --- src/pavucontrol.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index 9634b52..ae0e464 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -470,7 +470,7 @@ void ChannelWidget::onVolumeScaleValueChanged() { ca_context_cancel(ca_gtk_context_get(), 2); - int r = ca_gtk_play_for_widget(GTK_WIDGET(volumeScale->gobj()), + 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", @@ -1207,7 +1207,7 @@ void MainWindow::updateCard(const pa_card_info &info) { w->hasSinks = w->hasSources = false; w->profiles.clear(); - for (int i=0; ihasSinks = w->hasSinks || (info.profiles[i].n_sinks > 0); w->hasSources = w->hasSources || (info.profiles[i].n_sources > 0); w->profiles.insert(std::pair(info.profiles[i].name, info.profiles[i].description)); From 1f1c8c85762910ff0b031aa8ee3eec2a7f74df7d Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 28 Feb 2009 18:01:07 +0000 Subject: [PATCH 3/6] Create a new 'Configuration' tab Move the card listing into this tab. --- src/pavucontrol.cc | 46 +- src/pavucontrol.glade | 2032 ++++++++++++++++------------------------- 2 files changed, 826 insertions(+), 1252 deletions(-) diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index ae0e464..c9c5ae2 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -24,7 +24,6 @@ #include #include -#include #include #include @@ -356,8 +355,8 @@ public: void removeClient(uint32_t index); Gtk::Notebook *notebook; - Gtk::VBox *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox; - Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel; + Gtk::VBox *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox, *cardsVBox; + Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel, *noCardsLabel; Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox; std::map cardWidgets; @@ -1126,10 +1125,12 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtrget_widget("cardsVBox", cardsVBox); x->get_widget("streamsVBox", streamsVBox); x->get_widget("recsVBox", recsVBox); x->get_widget("sinksVBox", sinksVBox); x->get_widget("sourcesVBox", sourcesVBox); + x->get_widget("noCardsLabel", noCardsLabel); x->get_widget("noStreamsLabel", noStreamsLabel); x->get_widget("noRecsLabel", noRecsLabel); x->get_widget("noSinksLabel", noSinksLabel); @@ -1140,6 +1141,7 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtrget_widget("sourceTypeComboBox", sourceTypeComboBox); x->get_widget("notebook", notebook); + cardsVBox->set_reallocate_redraws(true); sourcesVBox->set_reallocate_redraws(true); streamsVBox->set_reallocate_redraws(true); recsVBox->set_reallocate_redraws(true); @@ -1185,8 +1187,7 @@ void MainWindow::updateCard(const pa_card_info &info) { w = cardWidgets[info.index]; else { cardWidgets[info.index] = w = CardWidget::create(); - sinksVBox->pack_start(*w, false, false, 0); - //sourcesVBox->pack_start(*w, false, false, 0); + cardsVBox->pack_start(*w, false, false, 0); w->index = info.index; is_new = true; } @@ -1198,7 +1199,7 @@ void MainWindow::updateCard(const pa_card_info &info) { else w->name = info.name; - w->boldNameLabel->set_text("Card: "); + w->boldNameLabel->set_text(""); gchar *txt; w->nameLabel->set_markup(txt = g_markup_printf_escaped("%s", w->name.c_str())); g_free(txt); @@ -1299,6 +1300,7 @@ void MainWindow::createMonitorStreamForSource(uint32_t source_idx) { char t[16]; pa_buffer_attr attr; pa_sample_spec ss; + return; ss.channels = 1; ss.format = PA_SAMPLE_FLOAT32; @@ -1331,6 +1333,7 @@ void MainWindow::createMonitorStreamForSinkInput(uint32_t sink_input_idx, uint32 pa_buffer_attr attr; pa_sample_spec ss; uint32_t monitor_source_idx; + return; ss.channels = 1; ss.format = PA_SAMPLE_FLOAT32; @@ -1704,8 +1707,6 @@ void MainWindow::updateDeviceVisibility() { void MainWindow::reallyUpdateDeviceVisibility() { bool is_empty = true; - std::set visible_cards; - std::set::iterator it_card; for (std::map::iterator i = sinkInputWidgets.begin(); i != sinkInputWidgets.end(); ++i) { SinkInputWidget* w = i->second; @@ -1743,7 +1744,6 @@ void MainWindow::reallyUpdateDeviceVisibility() { noRecsLabel->hide(); is_empty = true; - visible_cards.clear(); for (std::map::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) { SinkWidget* w = i->second; @@ -1751,18 +1751,6 @@ void MainWindow::reallyUpdateDeviceVisibility() { if (showSinkType == SINK_ALL || w->type == showSinkType) { w->show(); is_empty = false; - if (w->card_index != PA_INVALID_INDEX) - visible_cards.insert(w->card_index); - } else - w->hide(); - } - - for (std::map::iterator i = cardWidgets.begin(); i != cardWidgets.end(); ++i) { - CardWidget* w = i->second; - - if (true || (!visible_cards.count(w->index) && w->hasSinks)) { - w->show(); - is_empty = false; } else w->hide(); } @@ -1774,6 +1762,20 @@ void MainWindow::reallyUpdateDeviceVisibility() { is_empty = true; + for (std::map::iterator i = cardWidgets.begin(); i != cardWidgets.end(); ++i) { + CardWidget* w = i->second; + + w->show(); + is_empty = false; + } + + if (is_empty) + noCardsLabel->show(); + else + noCardsLabel->hide(); + + is_empty = true; + for (std::map::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) { SourceWidget* w = i->second; @@ -1801,6 +1803,8 @@ void MainWindow::reallyUpdateDeviceVisibility() { streamsVBox->show(); recsVBox->hide(); recsVBox->show(); + cardsVBox->hide(); + cardsVBox->show(); } void MainWindow::removeCard(uint32_t index) { diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade index bcf4ff3..6f8fe60 100644 --- a/src/pavucontrol.glade +++ b/src/pavucontrol.glade @@ -1,1243 +1,813 @@ - - - + - - - Volume Control - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 500 - 400 - True - False - multimedia-volume-control - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - 12 - True - False - 12 - - - - True - True - True - False - GTK_POS_TOP - False - False - - - - True - False - 0 - - - - 12 - True - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_SHADOW_NONE - GTK_CORNER_TOP_LEFT - - - - True - GTK_SHADOW_NONE - - - - True - False - 0 - - - - True - False - <i>No application is currently playing audio.</i> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 16 - 16 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - - - - 0 - True - True - - - - - - True - 0.5 - 0.5 - 1 - 1 - 0 - 12 - 12 - 12 - - - - True - False - 6 - - - - True - <b>_Show:</b> - True - True - GTK_JUSTIFY_LEFT - False - False - 1 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - All Streams + + + + Volume Control + 500 + 400 + multimedia-volume-control + + + True + 12 + 12 + + + True + True + False + + + True + + + True + True + 12 + never + automatic + + + True + none + + + True + + + True + False + 16 + 16 + <i>No application is currently playing audio.</i> + True + + + 0 + + + + + + + + + 0 + + + + + True + 12 + 12 + 12 + + + True + 6 + + + True + 1 + <b>_Show:</b> + True + True + + + 0 + + + + + True + All Streams Applications Virtual Streams - False - True - - - 0 - False - True - - - - - - - 0 - False - False - - - - - False - True - - - - - - True - _Playback - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - True - False - 0 - - - - 12 - True - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_SHADOW_NONE - GTK_CORNER_TOP_LEFT - - - - True - GTK_SHADOW_NONE - - - - True - False - 0 - - - - True - False - <i>No application is currently recording audio.</i> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 16 - 16 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - - - - 0 - True - True - - - - - - True - 0.5 - 0.5 - 1 - 1 - 0 - 12 - 12 - 12 - - - - True - False - 6 - - - - True - <b>_Show:</b> - True - True - GTK_JUSTIFY_LEFT - False - False - 1 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - All Streams + + + False + 1 + + + + + + + False + False + 1 + + + + + + + True + _Playback + True + + + False + tab + + + + + True + + + True + True + 12 + never + automatic + + + True + none + + + True + + + True + False + 16 + 16 + <i>No application is currently recording audio.</i> + True + + + 0 + + + + + + + + + 0 + + + + + True + 12 + 12 + 12 + + + True + 6 + + + True + 1 + <b>_Show:</b> + True + True + + + 0 + + + + + True + All Streams Applications Virtual Streams - False - True - - - 0 - False - True - - - - - - - 0 - False - False - - - - - False - True - - - - - - True - _Recording - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - True - False - 0 - - - - 12 - True - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_SHADOW_NONE - GTK_CORNER_TOP_LEFT - - - - True - GTK_SHADOW_NONE - - - - True - False - 0 - - - - True - False - <i>No output devices available</i> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 16 - 16 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - - - - 0 - True - True - - - - - - True - 0.5 - 0.5 - 1 - 1 - 0 - 12 - 12 - 12 - - - - True - False - 6 - - - - True - <b>S_how:</b> - True - True - GTK_JUSTIFY_LEFT - False - False - 1 - 0.5 - 0 - 0 - sinkTypeComboBox - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - All Output Devices + + + False + 1 + + + + + + + False + False + 1 + + + + + 1 + + + + + True + _Recording + True + + + 1 + False + tab + + + + + True + + + True + True + 12 + never + automatic + + + True + none + + + True + + + True + False + 16 + 16 + <i>No output devices available</i> + True + + + 0 + + + + + + + + + 0 + + + + + True + 12 + 12 + 12 + + + True + 6 + + + True + 1 + <b>S_how:</b> + True + True + sinkTypeComboBox + + + 0 + + + + + True + All Output Devices Hardware Output Devices Virtual Output Devices - False - True - - - 0 - False - True - - - - - - - 0 - False - True - - - - - False - True - - - - - - True - _Output Devices - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - - True - False - 0 - - - - 12 - True - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_SHADOW_NONE - GTK_CORNER_TOP_LEFT - - - - True - GTK_SHADOW_NONE - - - - True - False - 0 - - - - True - False - <i>No input devices available</i> - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 16 - 16 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - - - - 0 - True - True - - - - - - True - 0.5 - 0.5 - 1 - 1 - 0 - 12 - 12 - 12 - - - - True - False - 6 - - - - True - <b>Sho_w:</b> - True - True - GTK_JUSTIFY_LEFT - False - False - 1 - 0.5 - 0 - 0 - sourceTypeComboBox - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - - True - All Input Devices + + + False + 1 + + + + + + + False + 1 + + + + + 2 + + + + + True + _Output Devices + True + + + 2 + False + tab + + + + + True + + + True + True + 12 + never + automatic + + + True + none + + + True + + + True + False + 16 + 16 + <i>No input devices available</i> + True + + + 0 + + + + + + + + + 0 + + + + + True + 12 + 12 + 12 + + + True + 6 + + + True + 1 + <b>Sho_w:</b> + True + True + sourceTypeComboBox + + + 0 + + + + + True + All Input Devices All Except Monitors Hardware Input Devices Virtual Input Devices Monitors - False - True - - - 0 - False - True - - - - - - - 0 - False - True - - - - - False - True - - - - - - True - _Input Devices - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - tab - - - - - 0 - True - True - - - - - - - - True - window1 - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - True - True - False - - - - True - False - 0 - - - - 12 - True - False - 6 - - - - True - False - 6 - - - - True - gtk-missing-image - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - True - - - - - - True - False - 0 - - - - True - - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - True - - - - - - True - Stream Title - False - True - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_MIDDLE - -1 - False - 0 - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - False - 3 - - - - True - Mute audio - True - GTK_RELIEF_NONE - True - False - False - - - - True - 1 - audio-volume-muted - 0.5 - 0.5 - 0 - 0 - - - - - 0 - False - False - - - - - - True - Lock channels together - True - GTK_RELIEF_NONE - True - True - False - - - - True - 1 - stock_lock - 0.5 - 0.5 - 0 - 0 - - - - - 0 - False - False - - - - - - True - Open menu - True - GTK_RELIEF_NONE - True - False - False - - - - True - GTK_ARROW_DOWN - GTK_SHADOW_OUT - 0.5 - 0.5 - 0 - 0 - - - - - 0 - False - False - - - - - 0 - False - True - - - - - 0 - False - False - - - - - - True - False - 6 - - - - - - - - - - - 0 - False - False - - - - - 0 - False - False - - - - - - True - - - 0 - False - False - - - - - - - - - - True - window2 - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - True - False - 6 - - - - True - <b>left-front</b> - False - True - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - 15 - False - 0 - - - 0 - False - False - - - - - - True - True - False - GTK_POS_TOP - 0 - GTK_UPDATE_CONTINUOUS - False - 44.2408370972 0 100 5 0 0 - - - 0 - True - True - - - - - - True - 50% - False - False - GTK_JUSTIFY_LEFT - False - False - 1 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - 9 - False - 0 - - - 0 - False - False - - - - - - - - True - window1 - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - True - False - - - - True - True - False - - - - True - False - 0 - - - - 12 - True - False - 6 - - - - True - False - 6 - - - - True - gtk-missing-image - 4 - 0 - 0.5 - 0 - 0 - - - 0 - False - True - - - - - - True - False - 0 - - - - True - - False - True - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - True - - - - - - True - Stream Title - False - True - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_MIDDLE - -1 - False - 0 - - - 0 - True - True - - - - - 0 - True - True - - - - - - True - True - GTK_RELIEF_NONE - True - False - False - - - - True - GTK_ARROW_DOWN - GTK_SHADOW_OUT - 0.5 - 0.5 - 0 - 0 - - - - - 0 - False - True - - - - - 0 - False - False - - - - - 0 - False - False - - - - - - True - - - 0 - False - False - - - - - - - - + + + False + 1 + + + + + + + False + 1 + + + + + 3 + + + + + True + _Input Devices + True + + + 3 + False + tab + + + + + True + vertical + + + True + True + automatic + automatic + + + True + queue + + + True + vertical + + + True + False + 16 + 16 + <i>No cards available for configuration</i> + True + + + 0 + + + + + + + + + 0 + + + + + 4 + + + + + True + _Configutation + True + + + 4 + False + tab + + + + + 0 + + + + + + + True + window1 + + + True + + + True + + + True + 12 + 6 + + + True + 6 + + + True + gtk-missing-image + 4 + + + False + 0 + + + + + True + + + True + True + + + False + 0 + + + + + True + 0 + Stream Title + True + middle + + + 1 + + + + + 1 + + + + + True + 3 + + + True + True + False + Mute audio + none + + + True + audio-volume-muted + 1 + + + + + False + False + 0 + + + + + True + True + False + Lock channels together + none + True + + + True + stock_lock + 1 + + + + + False + False + 1 + + + + + True + True + False + Open menu + none + + + True + down + + + + + False + False + 2 + + + + + False + 2 + + + + + False + False + 0 + + + + + True + 6 + + + + + + + + + False + False + 1 + + + + + False + False + 0 + + + + + True + + + False + False + 1 + + + + + + + + + True + window2 + + + True + 6 + + + True + 0 + <b>left-front</b> + True + 15 + + + False + False + 0 + + + + + True + True + 44.2408370972 0 100 5 0 0 + 0 + False + + + 1 + + + + + True + 1 + 50% + 9 + + + False + False + 2 + + + + + + + True + window1 + + + True + + + True + + + True + 12 + 6 + + + True + 6 + + + True + 0 + gtk-missing-image + 4 + + + False + 0 + + + + + True + + + True + True + + + False + 0 + + + + + True + 0 + Stream Title + True + middle + + + 1 + + + + + 1 + + + + + True + True + False + none + + + True + down + + + + + False + 2 + + + + + False + False + 0 + + + + + False + False + 0 + + + + + True + + + False + False + 1 + + + + + + + From 6a76183326925dd4910d780183937b4c58492ea7 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 28 Feb 2009 19:51:08 +0000 Subject: [PATCH 4/6] Add a new widget for the card configuration and use it. This change allows the card profiles to be shown in a combo box. This is more natural and obvious than hiding things in the submenu. --- src/pavucontrol.cc | 171 +++++++++++++++--------------------------- src/pavucontrol.glade | 117 +++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 111 deletions(-) diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index c9c5ae2..8ccd2b0 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -156,7 +156,6 @@ class CardWidget : public Gtk::VBox { public: CardWidget(BaseObjectType* cobject, const Glib::RefPtr& x); static CardWidget* create(); - virtual ~CardWidget(); Gtk::Label *nameLabel, *boldNameLabel; Gtk::ToggleButton *streamToggleButton; @@ -166,43 +165,33 @@ public: uint32_t index; bool updating; - void onStreamToggleButton(); - void onMenuDeactivated(); - void popupMenuPosition(int& x, int& y, bool& push_in); - std::map profiles; Glib::ustring activeProfile; bool hasSinks; bool hasSources; - MainWindow *mainWindow; - Gtk::Menu submenu; - Gtk::MenuItem profilesMenuItem; + void prepareMenu(); - struct ProfileMenuItem { - ProfileMenuItem(CardWidget *w, const char *label, Glib::ustring profile, bool active) : - widget(w), - menuItem(label), - profile(profile) { - menuItem.set_active(active); - menuItem.set_draw_as_radio(true); - menuItem.signal_toggled().connect(sigc::mem_fun(*this, &ProfileMenuItem::onToggle)); - } +protected: + virtual void onProfileChange(); - CardWidget *widget; - Gtk::CheckMenuItem menuItem; - Glib::ustring profile; - void onToggle(); - }; + //Tree model columns: + class ModelColumns : public Gtk::TreeModel::ColumnRecord + { + public: - std::map profileMenuItems; + ModelColumns() + { add(name); add(desc); } - void clearMenu(); - void buildMenu(); - virtual void prepareMenu(void); + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn desc; + }; -protected: - virtual bool on_button_press_event(GdkEventButton* event); + ModelColumns profileModel; + + //Child widgets: + Gtk::ComboBox *profileList; + Glib::RefPtr treeModel; }; class SinkWidget : public StreamWidget { @@ -494,114 +483,72 @@ void ChannelWidget::set_sensitive(bool enabled) { /*** CardWidget ***/ CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : - Gtk::VBox(cobject), - profilesMenuItem(_("_Set Profile..."), true) { + Gtk::VBox(cobject) { x->get_widget("nameLabel", nameLabel); x->get_widget("boldNameLabel", boldNameLabel); - x->get_widget("streamToggle", streamToggleButton); + x->get_widget("profileList", profileList); x->get_widget("iconImage", iconImage); - streamToggleButton->set_active(false); - streamToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &CardWidget::onStreamToggleButton)); - menu.signal_deactivate().connect(sigc::mem_fun(*this, &CardWidget::onMenuDeactivated)); - - menu.append(profilesMenuItem); - profilesMenuItem.set_submenu(submenu); -} - -CardWidget::~CardWidget() { - clearMenu(); + profileList->signal_changed().connect( sigc::mem_fun(*this, &CardWidget::onProfileChange)); } CardWidget* CardWidget::create() { CardWidget* w; - Glib::RefPtr x = Gnome::Glade::Xml::create(GLADE_FILE, "minimalStreamWidget"); - x->get_widget_derived("minimalStreamWidget", w); + Glib::RefPtr x = Gnome::Glade::Xml::create(GLADE_FILE, "cardWidget"); + x->get_widget_derived("cardWidget", w); return w; } + void CardWidget::prepareMenu() { - clearMenu(); - buildMenu(); -} + int idx = 0; + int active_idx = -1; -void CardWidget::clearMenu() { + //m_refTreeModel = Gtk::TreeStore::create(m_Columns); + treeModel = Gtk::ListStore::create(profileModel); + profileList->set_model(treeModel); - while (!profileMenuItems.empty()) { - std::map::iterator i = profileMenuItems.begin(); - delete i->second; - profileMenuItems.erase(i); - } -} - -void CardWidget::buildMenu() { - int num = 0; + //Fill the ComboBox's Tree Model: for (std::map::iterator i = profiles.begin(); i != profiles.end(); ++i) { - ProfileMenuItem *m; - profileMenuItems[num++] = m = new ProfileMenuItem(this, i->second.c_str(), i->first.c_str(), i->first == activeProfile); - submenu.append(m->menuItem); + Gtk::TreeModel::Row row = *(treeModel->append()); + row[profileModel.name] = i->first; + row[profileModel.desc] = i->second; + if (i->first == activeProfile) + active_idx = idx; + idx++; } - menu.show_all(); + //Add the model columns to the Combo (which is a kind of view), + //rendering them in the default way: + profileList->pack_start(profileModel.desc); + if (active_idx >= 0) + profileList->set_active(active_idx); } -void CardWidget::ProfileMenuItem::onToggle() { +void CardWidget::onProfileChange() { + Gtk::TreeModel::iterator iter; - if (widget->updating) + if (updating) return; - if (!menuItem.get_active()) - return; + iter = profileList->get_active(); + if (iter) + { + Gtk::TreeModel::Row row = *iter; + if (row) + { + pa_operation* o; + Glib::ustring profile = row[profileModel.name]; - pa_operation* o; - if (!(o = pa_context_set_card_profile_by_index(context, widget->index, profile.c_str(), NULL, NULL))) { - show_error(_("pa_context_set_card_profile_by_index() failed")); - return; + if (!(o = pa_context_set_card_profile_by_index(context, index, profile.c_str(), NULL, NULL))) { + show_error(_("pa_context_set_card_profile_by_index() failed")); + return; + } + + pa_operation_unref(o); + } } - - pa_operation_unref(o); -} - - -void CardWidget::onMenuDeactivated(void) { - streamToggleButton->set_active(false); -} - -void CardWidget::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 CardWidget::onStreamToggleButton(void) { - if (streamToggleButton->get_active()) { - prepareMenu(); - menu.popup(sigc::mem_fun(*this, &CardWidget::popupMenuPosition), 0, gtk_get_current_event_time()); - } -} - -bool CardWidget::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; } @@ -1218,6 +1165,8 @@ void MainWindow::updateCard(const pa_card_info &info) { w->updating = false; + w->prepareMenu(); + if (is_new) updateDeviceVisibility(); } diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade index 6f8fe60..01ef9d4 100644 --- a/src/pavucontrol.glade +++ b/src/pavucontrol.glade @@ -810,4 +810,121 @@ Monitors + + True + window1 + + + True + + + True + vertical + + + True + 12 + vertical + 6 + + + True + 6 + + + True + 0 + gtk-missing-image + 4 + + + False + 0 + + + + + True + + + True + True + + + False + 0 + + + + + True + 0 + Stream Title + True + middle + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + True + + + True + 0 + Profile + + + 0 + + + + + True + + + 1 + + + + + 1 + + + + + False + False + 0 + + + + + True + + + False + False + 1 + + + + + + + From 188ee8c2c23f48b80e3403908167124af71d1916 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 28 Feb 2009 19:52:17 +0000 Subject: [PATCH 5/6] Typo: spell configuration right --- src/pavucontrol.glade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade index 01ef9d4..552a841 100644 --- a/src/pavucontrol.glade +++ b/src/pavucontrol.glade @@ -459,7 +459,7 @@ Monitors True - _Configutation + _Configuration True From d816be58d6eaf53b17f52ed5162033d65659932e Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sat, 28 Feb 2009 20:37:44 +0000 Subject: [PATCH 6/6] Fix the number of outstanding operations. This uses an incrementing rather than fixed value as it more obviously reflects what's going on. --- src/pavucontrol.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index 8ccd2b0..4a13fcd 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -2152,49 +2152,57 @@ void context_state_callback(pa_context *c, void *userdata) { } pa_operation_unref(o); + // Keep track of the outstanding callbacks for UI tweaks + n_outstanding = 0; + if (!(o = pa_context_get_server_info(c, server_info_cb, w))) { show_error(_("pa_context_get_server_info() failed")); return; } pa_operation_unref(o); + n_outstanding++; if (!(o = pa_context_get_client_info_list(c, client_cb, w))) { show_error(_("pa_context_client_info_list() failed")); return; } pa_operation_unref(o); + n_outstanding++; if (!(o = pa_context_get_card_info_list(c, card_cb, w))) { show_error(_("pa_context_get_card_info_list() failed")); return; } pa_operation_unref(o); + n_outstanding++; if (!(o = pa_context_get_sink_info_list(c, sink_cb, w))) { show_error(_("pa_context_get_sink_info_list() failed")); return; } pa_operation_unref(o); + n_outstanding++; if (!(o = pa_context_get_source_info_list(c, source_cb, w))) { show_error(_("pa_context_get_source_info_list() failed")); return; } pa_operation_unref(o); + n_outstanding++; if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, w))) { show_error(_("pa_context_get_sink_input_info_list() failed")); return; } pa_operation_unref(o); + n_outstanding++; if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, w))) { show_error(_("pa_context_get_source_output_info_list() failed")); return; } pa_operation_unref(o); - - n_outstanding = 6; + n_outstanding++; /* This call is not always supported */ if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, w))) {