summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2006-08-07 15:26:52 +0000
committerLennart Poettering <lennart@poettering.net>2006-08-07 15:26:52 +0000
commite634d7a9189097a83b84a23d329b5f1634c73e01 (patch)
tree494306fd3321007275ad97ccf6aabe5c746f1498
parenta0c6b92eff4853d7148756567193c8fccbdc4b05 (diff)
add popup menu to stream list, allowing "hot" moving of playback streams between sinks
git-svn-id: file:///home/lennart/svn/public/pavucontrol/trunk@41 c17c95f2-f111-0410-90bf-f30a9569010c
-rw-r--r--src/pavucontrol.cc128
-rw-r--r--src/pavucontrol.glade270
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>