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
This commit is contained in:
Lennart Poettering 2006-08-07 15:26:52 +00:00
parent a0c6b92eff
commit e634d7a918
2 changed files with 253 additions and 149 deletions

View File

@ -52,8 +52,9 @@ enum SourceType{
}; };
class StreamWidget; class StreamWidget;
class MainWindow;
class ChannelWidget : public Gtk::VBox { class ChannelWidget : public Gtk::EventBox {
public: public:
ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
static ChannelWidget* create(); static ChannelWidget* create();
@ -106,6 +107,7 @@ public:
static SinkWidget* create(); static SinkWidget* create();
SinkType type; SinkType type;
Glib::ustring name;
virtual void onMuteToggleButton(); virtual void onMuteToggleButton();
uint32_t index; uint32_t index;
@ -128,9 +130,37 @@ class SinkInputWidget : public StreamWidget {
public: public:
SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x); SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x);
static SinkInputWidget* create(); static SinkInputWidget* create();
virtual ~SinkInputWidget();
uint32_t index, clientIndex; uint32_t index, clientIndex, sinkIndex;
virtual void executeVolumeUpdate(); 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 { class MainWindow : public Gtk::Window {
@ -154,10 +184,10 @@ public:
Gtk::Label *noStreamsLabel, *noSinksLabel, *noSourcesLabel; Gtk::Label *noStreamsLabel, *noSinksLabel, *noSourcesLabel;
Gtk::ComboBox *sinkTypeComboBox, *sourceTypeComboBox; Gtk::ComboBox *sinkTypeComboBox, *sourceTypeComboBox;
std::map<int, SinkWidget*> sinkWidgets; std::map<uint32_t, SinkWidget*> sinkWidgets;
std::map<int, SourceWidget*> sourceWidgets; std::map<uint32_t, SourceWidget*> sourceWidgets;
std::map<int, SinkInputWidget*> streamWidgets; std::map<uint32_t, SinkInputWidget*> streamWidgets;
std::map<int, char*> clientNames; std::map<uint32_t, char*> clientNames;
SinkType showSinkType; SinkType showSinkType;
SourceType showSourceType; SourceType showSourceType;
@ -185,7 +215,7 @@ void show_error(const char *txt) {
/*** ChannelWidget ***/ /*** ChannelWidget ***/
ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) : ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
Gtk::VBox(cobject), Gtk::EventBox(cobject),
volumeScaleEnabled(true) { volumeScaleEnabled(true) {
x->get_widget("channelLabel", channelLabel); x->get_widget("channelLabel", channelLabel);
@ -227,7 +257,7 @@ void ChannelWidget::onVolumeScaleValueChanged() {
} }
void ChannelWidget::set_sensitive(bool enabled) { void ChannelWidget::set_sensitive(bool enabled) {
Gtk::VBox::set_sensitive(enabled); Gtk::EventBox::set_sensitive(enabled);
channelLabel->set_sensitive(enabled); channelLabel->set_sensitive(enabled);
volumeLabel->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) : 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() { SinkInputWidget* SinkInputWidget::create() {
@ -399,6 +440,53 @@ void SinkInputWidget::executeVolumeUpdate() {
pa_operation_unref(o); 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::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) : MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& x) :
@ -444,8 +532,11 @@ void MainWindow::on_realize() {
} }
MainWindow::~MainWindow() { 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); g_free(i->second);
clientNames.erase(i);
}
} }
void MainWindow::updateSink(const pa_sink_info &info) { 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->type = info.flags & PA_SINK_HARDWARE ? SINK_HARDWARE : SINK_VIRTUAL;
w->name = info.name;
gchar *txt; gchar *txt;
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", info.name)); 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->muteToggleButton->hide();
w->index = info.index; w->index = info.index;
w->clientIndex = info.client; w->clientIndex = info.client;
w->mainWindow = this;
} }
w->sinkIndex = info.sink;
char *txt; char *txt;
if (clientNames.count(info.client)) { if (clientNames.count(info.client)) {
w->boldNameLabel->set_markup(txt = g_markup_printf_escaped("<b>%s</b>", clientNames[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]); g_free(clientNames[info.index]);
clientNames[info.index] = g_strdup(info.name); 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; SinkInputWidget *w = i->second;
if (!w) if (!w)
@ -562,7 +657,7 @@ void MainWindow::updateDeviceVisibility() {
bool is_empty = true; 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; SinkWidget* w = i->second;
if (showSinkType == SINK_ALL || w->type == showSinkType) { if (showSinkType == SINK_ALL || w->type == showSinkType) {
@ -576,7 +671,7 @@ void MainWindow::updateDeviceVisibility() {
is_empty = true; 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; SourceWidget* w = i->second;
if (showSourceType == SOURCE_ALL || w->type == showSourceType) { if (showSourceType == SOURCE_ALL || w->type == showSourceType) {
@ -643,7 +738,8 @@ void MainWindow::onSourceTypeComboBoxChanged() {
} }
static void dec_outstanding(MainWindow *w) { static void dec_outstanding(MainWindow *w) {
assert(n_outstanding > 0); if (n_outstanding <= 0)
return;
if (--n_outstanding <= 0) if (--n_outstanding <= 0)
w->get_window()->set_cursor(); w->get_window()->set_cursor();

View File

@ -596,7 +596,13 @@ Monitor Sources</property>
<property name="urgency_hint">False</property> <property name="urgency_hint">False</property>
<child> <child>
<widget class="GtkVBox" id="streamWidget"> <widget class="GtkEventBox" id="streamWidget">
<property name="visible">True</property>
<property name="visible_window">True</property>
<property name="above_child">False</property>
<child>
<widget class="GtkVBox" id="streamWidget2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="homogeneous">False</property> <property name="homogeneous">False</property>
<property name="spacing">0</property> <property name="spacing">0</property>
@ -795,6 +801,8 @@ Monitor Sources</property>
</child> </child>
</widget> </widget>
</child> </child>
</widget>
</child>
</widget> </widget>
<widget class="GtkWindow" id="channelWindow"> <widget class="GtkWindow" id="channelWindow">