diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/pavucontrol.cc | 128 | ||||
-rw-r--r-- | src/pavucontrol.glade | 270 |
2 files changed, 251 insertions, 147 deletions
diff --git a/src/pavucontrol.cc b/src/pavucontrol.cc index 224be50..87fc18f 100644 --- a/src/pavucontrol.cc +++ b/src/pavucontrol.cc @@ -52,8 +52,9 @@ enum SourceType{ }; class StreamWidget; +class MainWindow; -class ChannelWidget : public Gtk::VBox { +class ChannelWidget : public Gtk::EventBox { public: ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); static ChannelWidget* create(); @@ -106,6 +107,7 @@ public: static SinkWidget* create(); SinkType type; + Glib::ustring name; virtual void onMuteToggleButton(); uint32_t index; @@ -128,9 +130,37 @@ class SinkInputWidget : public StreamWidget { public: SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); static SinkInputWidget* create(); + virtual ~SinkInputWidget(); - uint32_t index, clientIndex; + uint32_t index, clientIndex, sinkIndex; virtual void executeVolumeUpdate(); + + MainWindow *mainWindow; + Gtk::Menu menu, submenu; + Gtk::MenuItem titleMenuItem; + + struct SinkMenuItem { + SinkMenuItem(SinkInputWidget *w, const char *label, uint32_t i, bool active) : + widget(w), + menuItem(label), + index(i) { + menuItem.set_active(active); + 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(); + +protected: + virtual bool on_button_press_event(GdkEventButton* event); }; class MainWindow : public Gtk::Window { @@ -154,10 +184,10 @@ public: Gtk::Label *noStreamsLabel, *noSinksLabel, *noSourcesLabel; Gtk::ComboBox *sinkTypeComboBox, *sourceTypeComboBox; - std::map<int, SinkWidget*> sinkWidgets; - std::map<int, SourceWidget*> sourceWidgets; - std::map<int, SinkInputWidget*> streamWidgets; - std::map<int, char*> clientNames; + std::map<uint32_t, SinkWidget*> sinkWidgets; + std::map<uint32_t, SourceWidget*> sourceWidgets; + std::map<uint32_t, SinkInputWidget*> streamWidgets; + std::map<uint32_t, char*> clientNames; SinkType showSinkType; SourceType showSourceType; @@ -166,7 +196,7 @@ public: virtual void onSourceTypeComboBoxChanged(); void updateDeviceVisibility(); - + protected: virtual void on_realize(); }; @@ -185,7 +215,7 @@ void show_error(const char *txt) { /*** ChannelWidget ***/ ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) : - Gtk::VBox(cobject), + Gtk::EventBox(cobject), volumeScaleEnabled(true) { x->get_widget("channelLabel", channelLabel); @@ -227,7 +257,7 @@ void ChannelWidget::onVolumeScaleValueChanged() { } void ChannelWidget::set_sensitive(bool enabled) { - Gtk::VBox::set_sensitive(enabled); + Gtk::EventBox::set_sensitive(enabled); channelLabel->set_sensitive(enabled); volumeLabel->set_sensitive(enabled); @@ -378,7 +408,18 @@ void SourceWidget::onMuteToggleButton() { } SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) : - StreamWidget(cobject, x) { + StreamWidget(cobject, x), + mainWindow(NULL), + titleMenuItem("Move Stream...") { + + add_events(Gdk::BUTTON_PRESS_MASK); + + menu.append(titleMenuItem); + titleMenuItem.set_submenu(submenu); +} + +SinkInputWidget::~SinkInputWidget() { + clearMenu(); } SinkInputWidget* SinkInputWidget::create() { @@ -399,6 +440,53 @@ void SinkInputWidget::executeVolumeUpdate() { pa_operation_unref(o); } +bool SinkInputWidget::on_button_press_event(GdkEventButton* event) { + if (StreamWidget::on_button_press_event(event)) + return TRUE; + + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { + clearMenu(); + buildMenu(); + menu.popup(event->button, event->time); + return TRUE; + } + + return FALSE; +} + +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->name.c_str(), i->second->index, i->second->index == sinkIndex); + submenu.append(m->menuItem); + } + + menu.show_all(); +} + +void SinkInputWidget::SinkMenuItem::onToggle() { + + if (!menuItem.get_active()) + return; + + pa_operation* o; + if (!(o = pa_context_move_sink_input_by_index(context, widget->index, index, NULL, NULL))) { + show_error("pa_context_move_sink_input_by_index() failed"); + return; + } + + pa_operation_unref(o); +} + /*** MainWindow ***/ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) : @@ -425,7 +513,7 @@ MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade: sinkTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSinkTypeComboBoxChanged)); sourceTypeComboBox->signal_changed().connect(sigc::mem_fun(*this, &MainWindow::onSourceTypeComboBoxChanged)); - + Gdk::Color c("white"); titleEventBox->modify_bg(Gtk::STATE_NORMAL, c); } @@ -444,8 +532,11 @@ void MainWindow::on_realize() { } MainWindow::~MainWindow() { - for (std::map<int, char*>::iterator i = clientNames.begin(); i != clientNames.end(); ++i) + while (!clientNames.empty()) { + std::map<uint32_t, char*>::iterator i = clientNames.begin(); g_free(i->second); + clientNames.erase(i); + } } void MainWindow::updateSink(const pa_sink_info &info) { @@ -461,6 +552,7 @@ void MainWindow::updateSink(const pa_sink_info &info) { } w->type = info.flags & PA_SINK_HARDWARE ? SINK_HARDWARE : SINK_VIRTUAL; + w->name = info.name; gchar *txt; w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", info.name)); @@ -512,8 +604,11 @@ void MainWindow::updateSinkInput(const pa_sink_input_info &info) { w->muteToggleButton->hide(); w->index = info.index; w->clientIndex = info.client; + w->mainWindow = this; } + w->sinkIndex = info.sink; + char *txt; if (clientNames.count(info.client)) { w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", clientNames[info.client])); @@ -536,7 +631,7 @@ void MainWindow::updateClient(const pa_client_info &info) { g_free(clientNames[info.index]); clientNames[info.index] = g_strdup(info.name); - for (std::map<int, SinkInputWidget*>::iterator i = streamWidgets.begin(); i != streamWidgets.end(); ++i) { + for (std::map<uint32_t, SinkInputWidget*>::iterator i = streamWidgets.begin(); i != streamWidgets.end(); ++i) { SinkInputWidget *w = i->second; if (!w) @@ -562,7 +657,7 @@ void MainWindow::updateDeviceVisibility() { bool is_empty = true; - for (std::map<int, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) { + for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) { SinkWidget* w = i->second; if (showSinkType == SINK_ALL || w->type == showSinkType) { @@ -576,7 +671,7 @@ void MainWindow::updateDeviceVisibility() { is_empty = true; - for (std::map<int, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) { + for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) { SourceWidget* w = i->second; if (showSourceType == SOURCE_ALL || w->type == showSourceType) { @@ -643,7 +738,8 @@ void MainWindow::onSourceTypeComboBoxChanged() { } static void dec_outstanding(MainWindow *w) { - assert(n_outstanding > 0); + if (n_outstanding <= 0) + return; if (--n_outstanding <= 0) w->get_window()->set_cursor(); diff --git a/src/pavucontrol.glade b/src/pavucontrol.glade index f56ad0b..ef395c9 100644 --- a/src/pavucontrol.glade +++ b/src/pavucontrol.glade @@ -596,41 +596,79 @@ Monitor Sources</property> <property name="urgency_hint">False</property> <child> - <widget class="GtkVBox" id="streamWidget"> + <widget class="GtkEventBox" id="streamWidget"> <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> + <property name="visible_window">True</property> + <property name="above_child">False</property> <child> - <widget class="GtkVBox" id="vbox26"> - <property name="border_width">12</property> + <widget class="GtkVBox" id="streamWidget2"> <property name="visible">True</property> <property name="homogeneous">False</property> - <property name="spacing">6</property> + <property name="spacing">0</property> <child> - <widget class="GtkHBox" id="hbox1"> + <widget class="GtkVBox" id="vbox26"> + <property name="border_width">12</property> <property name="visible">True</property> <property name="homogeneous">False</property> - <property name="spacing">0</property> + <property name="spacing">6</property> <child> - <widget class="GtkLabel" id="boldNameLabel"> + <widget class="GtkHBox" id="hbox1"> <property name="visible">True</property> - <property name="label" translatable="yes"></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="boldNameLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="nameLabel"> + <property name="visible">True</property> + <property name="label" translatable="yes">Stream Title</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_MIDDLE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -640,69 +678,73 @@ Monitor Sources</property> </child> <child> - <widget class="GtkLabel" id="nameLabel"> - <property name="visible">True</property> - <property name="label" translatable="yes">Stream Title</property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_MIDDLE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="streamHBox"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">6</property> - - <child> - <widget class="GtkVBox" id="vbox29"> + <widget class="GtkHBox" id="streamHBox"> <property name="visible">True</property> - <property name="homogeneous">True</property> + <property name="homogeneous">False</property> <property name="spacing">6</property> <child> - <widget class="GtkToggleButton" id="lockToggleButton"> + <widget class="GtkVBox" id="vbox29"> <property name="visible">True</property> - <property name="tooltip" translatable="yes">Lock Channels Together</property> - <property name="can_focus">True</property> - <property name="relief">GTK_RELIEF_NONE</property> - <property name="focus_on_click">True</property> - <property name="active">True</property> - <property name="inconsistent">False</property> + <property name="homogeneous">True</property> + <property name="spacing">6</property> <child> - <widget class="GtkImage" id="image18"> + <widget class="GtkToggleButton" id="lockToggleButton"> <property name="visible">True</property> - <property name="stock">gtk-dialog-authentication</property> - <property name="icon_size">1</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> + <property name="tooltip" translatable="yes">Lock Channels Together</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NONE</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + + <child> + <widget class="GtkImage" id="image18"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-authentication</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkToggleButton" id="muteToggleButton"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Lock Channels Together</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NONE</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + + <child> + <widget class="GtkImage" id="image20"> + <property name="visible">True</property> + <property name="icon_size">1</property> + <property name="icon_name">audio-volume-muted</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> </child> </widget> <packing> @@ -713,31 +755,23 @@ Monitor Sources</property> </child> <child> - <widget class="GtkToggleButton" id="muteToggleButton"> + <widget class="GtkVBox" id="channelsVBox"> <property name="visible">True</property> - <property name="tooltip" translatable="yes">Lock Channels Together</property> - <property name="can_focus">True</property> - <property name="relief">GTK_RELIEF_NONE</property> - <property name="focus_on_click">True</property> - <property name="active">True</property> - <property name="inconsistent">False</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> <child> - <widget class="GtkImage" id="image20"> - <property name="visible">True</property> - <property name="icon_size">1</property> - <property name="icon_name">audio-volume-muted</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - </widget> + <placeholder/> + </child> + + <child> + <placeholder/> </child> </widget> <packing> <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> + <property name="expand">True</property> + <property name="fill">True</property> </packing> </child> </widget> @@ -747,27 +781,6 @@ Monitor Sources</property> <property name="fill">False</property> </packing> </child> - - <child> - <widget class="GtkVBox" id="channelsVBox"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">6</property> - - <child> - <placeholder/> - </child> - - <child> - <placeholder/> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> </widget> <packing> <property name="padding">0</property> @@ -775,23 +788,18 @@ Monitor Sources</property> <property name="fill">False</property> </packing> </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - <child> - <widget class="GtkHSeparator" id="hseparator2"> - <property name="visible">True</property> + <child> + <widget class="GtkHSeparator" id="hseparator2"> + <property name="visible">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> </child> </widget> </child> |