summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/ardour3_styles.rc.in10
-rw-r--r--gtk2_ardour/editor.cc8
-rw-r--r--gtk2_ardour/editor_canvas_events.cc12
-rw-r--r--gtk2_ardour/editor_summary.cc88
-rw-r--r--gtk2_ardour/editor_videotimeline.cc10
-rw-r--r--gtk2_ardour/export_video_infobox.cc9
-rw-r--r--gtk2_ardour/export_video_infobox.h1
-rw-r--r--gtk2_ardour/gain_meter.cc34
-rw-r--r--gtk2_ardour/mixer_strip.cc6
-rw-r--r--gtk2_ardour/sfdb_freesound_mootcher.cc210
-rw-r--r--gtk2_ardour/sfdb_freesound_mootcher.h34
-rw-r--r--gtk2_ardour/sfdb_ui.cc213
-rw-r--r--gtk2_ardour/sfdb_ui.h14
-rw-r--r--gtk2_ardour/time_axis_view.cc4
-rw-r--r--libs/ardour/ardour/rc_configuration_vars.h1
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/keyboard.h5
-rw-r--r--libs/gtkmm2ext/keyboard.cc5
17 files changed, 430 insertions, 234 deletions
diff --git a/gtk2_ardour/ardour3_styles.rc.in b/gtk2_ardour/ardour3_styles.rc.in
index 0872fc9141..da636eefd1 100644
--- a/gtk2_ardour/ardour3_styles.rc.in
+++ b/gtk2_ardour/ardour3_styles.rc.in
@@ -736,6 +736,11 @@ style "midi_track_metrics" = "midi_track_base"
font_name = "@FONT_TINY@"
}
+style "audiomidi_track_metrics" = "midi_track_base"
+{
+ font_name = "@FONT_TINY@"
+}
+
style "audio_bus_metrics" = "audio_bus_base"
{
font_name = "@FONT_TINY@"
@@ -751,6 +756,11 @@ style "midi_track_metrics_inactive" = "track_controls_inactive"
font_name = "@FONT_TINY@"
}
+style "audiomidi_track_metrics_inactive" = "track_controls_inactive"
+{
+ font_name = "@FONT_TINY@"
+}
+
style "audio_bus_metrics_inactive" = "track_controls_inactive"
{
font_name = "@FONT_TINY@"
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index 761404ba18..d3e3ae9828 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -3125,12 +3125,12 @@ Editor::convert_drop_to_paths (
*/
string txt = data.get_text();
- const char* p;
+ char* p;
const char* q;
- p = (const char *) malloc (txt.length() + 1);
- txt.copy (const_cast<char *> (p), txt.length(), 0);
- const_cast<char*>(p)[txt.length()] = '\0';
+ p = (char *) malloc (txt.length() + 1);
+ txt.copy (p, txt.length(), 0);
+ p[txt.length()] = '\0';
while (p)
{
diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc
index fd44ed3f2e..79cc1e066f 100644
--- a/gtk2_ardour/editor_canvas_events.cc
+++ b/gtk2_ardour/editor_canvas_events.cc
@@ -66,17 +66,17 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
retry:
switch (direction) {
case GDK_SCROLL_UP:
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
//for mouse-wheel zoom, force zoom-focus to mouse
Editing::ZoomFocus temp_focus = zoom_focus;
zoom_focus = Editing::ZoomFocusMouse;
temporal_zoom_step (false);
zoom_focus = temp_focus;
return true;
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
direction = GDK_SCROLL_LEFT;
goto retry;
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
if (!current_stepping_trackview) {
step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
std::pair<TimeAxisView*, int> const p = trackview_by_y_position (ev->y + vertical_adjustment.get_value() - canvas_timebars_vsize);
@@ -95,17 +95,17 @@ Editor::track_canvas_scroll (GdkEventScroll* ev)
break;
case GDK_SCROLL_DOWN:
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
//for mouse-wheel zoom, force zoom-focus to mouse
Editing::ZoomFocus temp_focus = zoom_focus;
zoom_focus = Editing::ZoomFocusMouse;
temporal_zoom_step (true);
zoom_focus = temp_focus;
return true;
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
direction = GDK_SCROLL_RIGHT;
goto retry;
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
if (!current_stepping_trackview) {
step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
std::pair<TimeAxisView*, int> const p = trackview_by_y_position (ev->y + vertical_adjustment.get_value() - canvas_timebars_vsize);
diff --git a/gtk2_ardour/editor_summary.cc b/gtk2_ardour/editor_summary.cc
index 4e148e2e7b..0dc728d18e 100644
--- a/gtk2_ardour/editor_summary.cc
+++ b/gtk2_ardour/editor_summary.cc
@@ -623,45 +623,55 @@ EditorSummary::on_scroll_event (GdkEventScroll* ev)
double x = xr.first;
double y = yr.first;
- double amount = 8;
-
- if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
- amount = 64;
- } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
- amount = 1;
- }
-
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
-
- /* secondary-wheel == left-right scrolling */
-
- if (ev->direction == GDK_SCROLL_UP) {
- x -= amount;
- } else if (ev->direction == GDK_SCROLL_DOWN) {
- x += amount;
- }
-
- } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
-
- /* primary-wheel == zoom */
-
- if (ev->direction == GDK_SCROLL_UP) {
- _editor->temporal_zoom_step (false);
- } else {
- _editor->temporal_zoom_step (true);
- }
-
- } else {
-
- if (ev->direction == GDK_SCROLL_DOWN) {
- y += amount;
- } else if (ev->direction == GDK_SCROLL_UP) {
- y -= amount;
- } else if (ev->direction == GDK_SCROLL_LEFT) {
- x -= amount;
- } else if (ev->direction == GDK_SCROLL_RIGHT) {
- x += amount;
- }
+ switch (ev->direction) {
+ case GDK_SCROLL_UP:
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
+ x -= 64;
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
+ _editor->temporal_zoom_step (false);
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
+ yr.first += 4;
+ yr.second -= 4;
+ set_editor (xr, yr);
+ return true;
+ } else {
+ y -= 8;
+ }
+ break;
+ case GDK_SCROLL_DOWN:
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
+ x += 64;
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
+ _editor->temporal_zoom_step (true);
+ } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
+ yr.first -= 4;
+ yr.second += 4;
+ set_editor (xr, yr);
+ return true;
+ } else {
+ y += 8;
+ }
+ break;
+ case GDK_SCROLL_LEFT:
+ if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
+ x -= 64;
+ } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
+ x -= 1;
+ } else {
+ x -= 8;
+ }
+ break;
+ case GDK_SCROLL_RIGHT:
+ if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
+ x += 64;
+ } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
+ x += 1;
+ } else {
+ x += 8;
+ }
+ break;
+ default:
+ break;
}
set_editor (x, y);
diff --git a/gtk2_ardour/editor_videotimeline.cc b/gtk2_ardour/editor_videotimeline.cc
index fa42211d19..7395d767c7 100644
--- a/gtk2_ardour/editor_videotimeline.cc
+++ b/gtk2_ardour/editor_videotimeline.cc
@@ -36,6 +36,7 @@
#include "export_video_infobox.h"
#include "interthread_progress_window.h"
+#include "pbd/openuri.h"
#include "i18n.h"
using namespace std;
@@ -125,10 +126,17 @@ Editor::export_video ()
{
if (ARDOUR::Config->get_show_video_export_info()) {
ExportVideoInfobox infobox (_session);
- infobox.run();
+ Gtk::ResponseType rv = (Gtk::ResponseType) infobox.run();
if (infobox.show_again()) {
ARDOUR::Config->set_show_video_export_info(false);
}
+ switch (rv) {
+ case GTK_RESPONSE_YES:
+ PBD::open_uri (ARDOUR::Config->get_reference_manual_url() + "/video-timeline/operations/#export");
+ break;
+ default:
+ break;
+ }
}
ExportVideoDialog dialog (*this, _session);
Gtk::ResponseType r = (Gtk::ResponseType) dialog.run();
diff --git a/gtk2_ardour/export_video_infobox.cc b/gtk2_ardour/export_video_infobox.cc
index 92180462d4..fbe28bd821 100644
--- a/gtk2_ardour/export_video_infobox.cc
+++ b/gtk2_ardour/export_video_infobox.cc
@@ -43,7 +43,11 @@ ExportVideoInfobox::ExportVideoInfobox (Session* s)
l = manage (new Label (_("<b>Video Export Info</b>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
l->set_use_markup ();
vbox->pack_start (*l, false, true);
- l = manage (new Label (_("Ardour video export is not recommended for mastering!\nWhile 'ffmpeg' (which is used by ardour) can produce high-quality files, this export lacks the possibility to tweak many settings. We recommend to use 'winff', 'devede' or 'dvdauthor' to mux & master. Nevertheless this video-export comes in handy to do quick snapshots, intermediates, dailies or online videos.\n\nThe soundtrack is created from the master-bus of the current Ardour session.\n\nThe video soure defaults to the file used in the video timeline, which may not the best quality to start with, you should the original video file.\n\nIf the export-range is longer than the original video, black video frames are prefixed and/or appended. This process may fail with non-standard pixel-aspect-ratios.\n\nThe file-format is determined by the extension that you choose for the output file (.avi, .mov, .flv, .ogv,...)\nNote: not all combinations of format+codec+settings produce files which are according so spec. e.g. flv files require sample-rates of 22.1kHz or 44.1kHz, mpeg containers can not be used with ac3 audio-codec, etc. If in doubt, use one of the built-in presets."), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
+ l = manage (new Label (
+ string_compose(
+ _("Video encoding is a non-trivial task with many details.\n\nPlease see the manual at %1/video-timeline/operations/#export.\n\nOpen Manual in Browser? "),
+ Config->get_reference_manual_url()
+ ), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
l->set_size_request(700,-1);
l->set_line_wrap();
vbox->pack_start (*l, false, true,4);
@@ -56,7 +60,8 @@ ExportVideoInfobox::ExportVideoInfobox (Session* s)
showagain_checkbox.set_active(false);
show_all_children ();
- add_button (Stock::OK, RESPONSE_ACCEPT);
+ add_button (Stock::YES, RESPONSE_YES);
+ add_button (Stock::NO, RESPONSE_NO);
}
ExportVideoInfobox::~ExportVideoInfobox ()
diff --git a/gtk2_ardour/export_video_infobox.h b/gtk2_ardour/export_video_infobox.h
index 6bd6c64533..d3dadf1c2a 100644
--- a/gtk2_ardour/export_video_infobox.h
+++ b/gtk2_ardour/export_video_infobox.h
@@ -38,7 +38,6 @@ class ExportVideoInfobox : public ArdourDialog
bool show_again () { return showagain_checkbox.get_active(); }
private:
- //void on_show ();
Gtk::CheckButton showagain_checkbox;
};
diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc
index 8a8d54d3c4..99f1f75f0e 100644
--- a/gtk2_ardour/gain_meter.cc
+++ b/gtk2_ardour/gain_meter.cc
@@ -48,6 +48,8 @@
#include "ardour/session.h"
#include "ardour/route.h"
#include "ardour/meter.h"
+#include "ardour/audio_track.h"
+#include "ardour/midi_track.h"
#include "i18n.h"
@@ -1151,11 +1153,43 @@ GainMeterBase::level_meter_button_press (GdkEventButton* ev)
void
GainMeter::meter_configuration_changed (ChanCount c)
{
+ int type = 0;
_types.clear ();
for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
if (c.get (*i) > 0) {
_types.push_back (*i);
+ type |= 1 << (*i);
+ }
+ }
+
+ if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
+ && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0
+ ) {
+ if (_route->active()) {
+ set_meter_strip_name ("AudioBusMetrics");
+ } else {
+ set_meter_strip_name ("AudioBusMetricsInactive");
+ }
+ }
+ else if (type == (1 << DataType::AUDIO)) {
+ if (_route->active()) {
+ set_meter_strip_name ("AudioTrackMetrics");
+ } else {
+ set_meter_strip_name ("AudioTrackMetricsInactive");
+ }
+ }
+ else if (type == (1 << DataType::MIDI)) {
+ if (_route->active()) {
+ set_meter_strip_name ("MidiTrackMetrics");
+ } else {
+ set_meter_strip_name ("MidiTrackMetricsInactive");
+ }
+ } else {
+ if (_route->active()) {
+ set_meter_strip_name ("AudioMidiTrackMetrics");
+ } else {
+ set_meter_strip_name ("AudioMidiTrackMetricsInactive");
}
}
diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc
index 79d4b224f6..2ee3faf4e7 100644
--- a/gtk2_ardour/mixer_strip.cc
+++ b/gtk2_ardour/mixer_strip.cc
@@ -1665,28 +1665,22 @@ MixerStrip::reset_strip_style ()
if (is_midi_track()) {
if (_route->active()) {
set_name ("MidiTrackStripBase");
- gpm.set_meter_strip_name ("MidiTrackMetrics");
} else {
set_name ("MidiTrackStripBaseInactive");
- gpm.set_meter_strip_name ("MidiTrackMetricsInactive");
}
gpm.set_fader_name ("MidiTrackFader");
} else if (is_audio_track()) {
if (_route->active()) {
set_name ("AudioTrackStripBase");
- gpm.set_meter_strip_name ("AudioTrackMetrics");
} else {
set_name ("AudioTrackStripBaseInactive");
- gpm.set_meter_strip_name ("AudioTrackMetricsInactive");
}
gpm.set_fader_name ("AudioTrackFader");
} else {
if (_route->active()) {
set_name ("AudioBusStripBase");
- gpm.set_meter_strip_name ("AudioBusMetrics");
} else {
set_name ("AudioBusStripBaseInactive");
- gpm.set_meter_strip_name ("AudioBusMetricsInactive");
}
gpm.set_fader_name ("AudioBusFader");
diff --git a/gtk2_ardour/sfdb_freesound_mootcher.cc b/gtk2_ardour/sfdb_freesound_mootcher.cc
index e46fb12391..8e57d9a0c4 100644
--- a/gtk2_ardour/sfdb_freesound_mootcher.cc
+++ b/gtk2_ardour/sfdb_freesound_mootcher.cc
@@ -53,6 +53,9 @@
#include "i18n.h"
#include "ardour/audio_library.h"
+#include "ardour/rc_configuration.h"
+#include "pbd/pthread_utils.h"
+#include "gui_thread.h"
using namespace PBD;
@@ -63,9 +66,12 @@ static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour
Mootcher::Mootcher()
: curl(curl_easy_init())
{
- std::string path;
- path = Glib::get_home_dir() + "/Freesound/";
- changeWorkingDir ( path.c_str() );
+ cancel_download_btn.set_label (_("Cancel"));
+ progress_hbox.pack_start (progress_bar, true, true);
+ progress_hbox.pack_end (cancel_download_btn, false, false);
+ progress_bar.show();
+ cancel_download_btn.show();
+ cancel_download_btn.signal_clicked().connect(sigc::mem_fun (*this, &Mootcher::cancelDownload));
};
//------------------------------------------------------------------------
Mootcher:: ~Mootcher()
@@ -74,31 +80,25 @@ Mootcher:: ~Mootcher()
}
//------------------------------------------------------------------------
-void Mootcher::changeWorkingDir(const char *saveLocation)
-{
- basePath = saveLocation;
-#ifdef __WIN32__
- std::string replace = "/";
- size_t pos = basePath.find("\\");
- while( pos != std::string::npos ){
- basePath.replace(pos, 1, replace);
- pos = basePath.find("\\");
- }
-#endif
- //
- size_t pos2 = basePath.find_last_of("/");
- if(basePath.length() != (pos2+1)) basePath += "/";
-}
void Mootcher::ensureWorkingDir ()
{
- std::string p = Glib::build_filename (basePath, "snd");
+ std::string p = ARDOUR::Config->get_freesound_download_dir();
if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
if (g_mkdir_with_parents (p.c_str(), 0775) != 0) {
PBD::error << "Unable to create Mootcher working dir" << endmsg;
}
}
+ basePath = p;
+#ifdef __WIN32__
+ std::string replace = "/";
+ size_t pos = basePath.find("\\");
+ while( pos != std::string::npos ){
+ basePath.replace(pos, 1, replace);
+ pos = basePath.find("\\");
+ }
+#endif
}
@@ -121,7 +121,8 @@ size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void
//------------------------------------------------------------------------
-std::string Mootcher::sortMethodString(enum sortMethod sort) {
+std::string Mootcher::sortMethodString(enum sortMethod sort)
+{
// given a sort type, returns the string value to be passed to the API to
// sort the results in the requested way.
@@ -202,6 +203,18 @@ std::string Mootcher::doRequest(std::string uri, std::string params)
}
+std::string Mootcher::searchSimilar(std::string id)
+{
+ std::string params = "";
+
+ params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";
+ params += "&num_results=100";
+
+ return doRequest("/sounds/" + id + "/similar", params);
+}
+
+//------------------------------------------------------------------------
+
std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort)
{
std::string params = "";
@@ -264,7 +277,7 @@ std::string Mootcher::getSoundResourceFile(std::string ID)
// get the file name and size from xml file
if (name) {
- audioFileName = basePath + "snd/" + ID + "-" + name->child("text")->content();
+ audioFileName = Glib::build_filename (basePath, ID + "-" + name->child("text")->content());
//store all the tags in the database
XMLNode *tags = freesound->child("tags");
@@ -296,10 +309,61 @@ int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)
};
//------------------------------------------------------------------------
-std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller)
+
+void *
+Mootcher::threadFunc() {
+CURLcode res;
+
+ res = curl_easy_perform (curl);
+ fclose (theFile);
+ curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+
+ if (res != CURLE_OK) {
+ /* it's not an error if the user pressed the stop button */
+ if (res != CURLE_ABORTED_BY_CALLBACK) {
+ error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;
+ }
+ remove ( (audioFileName+".part").c_str() );
+ } else {
+ rename ( (audioFileName+".part").c_str(), audioFileName.c_str() );
+ // now download the tags &c.
+ getSoundResourceFile(ID);
+ }
+
+ return (void *) res;
+}
+
+void
+Mootcher::doneWithMootcher()
+{
+
+ // update the sound info pane if the selection in the list box is still us
+ sfb->refresh_display(ID, audioFileName);
+
+ delete this; // this should be OK to do as long as Progress and Finished signals are always received in the order in which they are emitted
+}
+
+static void *
+freesound_download_thread_func(void *arg)
+{
+ Mootcher *thisMootcher = (Mootcher *) arg;
+ void *res;
+
+ // std::cerr << "freesound_download_thread_func(" << arg << ")" << std::endl;
+ res = thisMootcher->threadFunc();
+
+ thisMootcher->Finished(); /* EMIT SIGNAL */
+ return res;
+}
+
+
+//------------------------------------------------------------------------
+
+bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID)
{
ensureWorkingDir();
- std::string audioFileName = basePath + "snd/" + ID + "-" + originalFileName;
+ ID = theID;
+ audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);
// check to see if audio file already exists
FILE *testFile = g_fopen(audioFileName.c_str(), "r");
@@ -307,29 +371,31 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID,
fseek (testFile , 0 , SEEK_END);
if (ftell (testFile) > 256) {
fclose (testFile);
- return audioFileName;
+ return true;
}
- // else file was small, probably an error, delete it and try again
+ // else file was small, probably an error, delete it
fclose(testFile);
remove( audioFileName.c_str() );
}
+ return false;
+}
- if (!curl) {
- return "";
- }
- // if already cancelling a previous download, bail out here ( this can happen b/c getAudioFile gets called by various UI update funcs )
- if ( caller->freesound_download_cancel ) {
- return "";
+bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller)
+{
+ ensureWorkingDir();
+ ID = theID;
+ audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);
+
+ if (!curl) {
+ return false;
}
-
// now download the actual file
- FILE* theFile;
- theFile = g_fopen( audioFileName.c_str(), "wb" );
+ theFile = g_fopen( (audioFileName + ".part").c_str(), "wb" );
if (!theFile) {
- return "";
+ return false;
}
// create the download url
@@ -340,57 +406,59 @@ std::string Mootcher::getAudioFile(std::string originalFileName, std::string ID,
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);
- /* hack to get rid of the barber-pole stripes */
- caller->freesound_progress_bar.hide();
- caller->freesound_progress_bar.show();
-
std::string prog;
prog = string_compose (_("%1"), originalFileName);
- caller->freesound_progress_bar.set_text(prog);
+ progress_bar.set_text(prog);
+
+ Gtk::VBox *freesound_vbox = dynamic_cast<Gtk::VBox *> (caller->notebook.get_nth_page(2));
+ freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK);
+ progress_hbox.show();
+ cancel_download = false;
+ sfb = caller;
curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
- curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, caller);
+ curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this);
- CURLcode res = curl_easy_perform(curl);
- fclose(theFile);
+ Progress.connect(*this, invalidator (*this), boost::bind(&Mootcher::updateProgress, this, _1, _2), gui_context());
+ Finished.connect(*this, invalidator (*this), boost::bind(&Mootcher::doneWithMootcher, this), gui_context());
+ pthread_t freesound_download_thread;
+ pthread_create_and_store("freesound_import", &freesound_download_thread, freesound_download_thread_func, this);
- curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
- caller->freesound_progress_bar.set_fraction(0.0);
- caller->freesound_progress_bar.set_text("");
-
- if( res != 0 ) {
- /* it's not an error if the user pressed the stop button */
- if (res != CURLE_ABORTED_BY_CALLBACK) {
- error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;
+ return true;
+}
+
+//---------
+
+void
+Mootcher::updateProgress(double dlnow, double dltotal)
+{
+ if (dltotal > 0) {
+ double fraction = dlnow / dltotal;
+ // std::cerr << "progress idle: " << progress->bar->get_text() << ". " << progress->dlnow << " / " << progress->dltotal << " = " << fraction << std::endl;
+ if (fraction > 1.0) {
+ fraction = 1.0;
+ } else if (fraction < 0.0) {
+ fraction = 0.0;
}
- remove( audioFileName.c_str() );
- return "";
- } else {
- // now download the tags &c.
- getSoundResourceFile(ID);
+ progress_bar.set_fraction(fraction);
}
-
- return audioFileName;
}
-//---------
-int Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/)
+int
+Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/)
{
+ // It may seem curious to pass a pointer to an instance of an object to a static
+ // member function, but we can't use a normal member function as a curl progress callback,
+ // and we want access to some private members of Mootcher.
-SoundFileBrowser *sfb = (SoundFileBrowser *) caller;
- //XXX I hope it's OK to do GTK things in this callback. Otherwise
- // I'll have to do stuff like in interthread_progress_window.
- if (sfb->freesound_download_cancel) {
- return -1;
- }
-
+ Mootcher *thisMootcher = (Mootcher *) caller;
- sfb->freesound_progress_bar.set_fraction(dlnow/dltotal);
- /* Make sure the progress widget gets updated */
- while (Glib::MainContext::get_default()->iteration (false)) {
- /* do nothing */
+ if (thisMootcher->cancel_download) {
+ return -1;
}
+
+ thisMootcher->Progress(dlnow, dltotal); /* EMIT SIGNAL */
return 0;
}
diff --git a/gtk2_ardour/sfdb_freesound_mootcher.h b/gtk2_ardour/sfdb_freesound_mootcher.h
index 7e39ba4ae4..48fb11b638 100644
--- a/gtk2_ardour/sfdb_freesound_mootcher.h
+++ b/gtk2_ardour/sfdb_freesound_mootcher.h
@@ -65,18 +65,31 @@ enum sortMethod {
};
-class Mootcher
+class Mootcher: public sigc::trackable, public PBD::ScopedConnectionList
{
public:
Mootcher();
~Mootcher();
- std::string getAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller);
+ bool checkAudioFile(std::string originalFileName, std::string ID);
+ bool fetchAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller);
std::string searchText(std::string query, int page, std::string filter, enum sortMethod sort);
+ std::string searchSimilar(std::string id);
+ void * threadFunc();
+ SoundFileBrowser *sfb;
+ std::string audioFileName;
+ std::string ID;
+
+ /** signal emitted when mootcher reports progress updates during download.
+ * The parameters are current and total numbers of bytes downloaded.
+ */
+ PBD::Signal2<void, double, double> Progress;
+ /** signal emitted when the mootcher has finished downloading. */
+ PBD::Signal0<void> Finished;
+
private:
- void changeWorkingDir(const char *saveLocation);
void ensureWorkingDir();
std::string doRequest(std::string uri, std::string params);
@@ -90,6 +103,21 @@ private:
CURL *curl;
char errorBuffer[CURL_ERROR_SIZE]; // storage for cUrl error message
+ FILE* theFile;
+
+ void updateProgress(double dlnow, double dltotal);
+ void doneWithMootcher();
+
+ Gtk::HBox progress_hbox;
+ Gtk::ProgressBar progress_bar;
+ Gtk::Button cancel_download_btn;
+
+ bool cancel_download;
+ void cancelDownload() {
+ cancel_download = true;
+ progress_hbox.hide();
+ }
+
std::string basePath;
std::string xmlLocation;
};
diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc
index 85b4f7d7ef..e8db9d4939 100644
--- a/gtk2_ardour/sfdb_ui.cc
+++ b/gtk2_ardour/sfdb_ui.cc
@@ -80,6 +80,7 @@ using namespace Editing;
using std::string;
string SoundFileBrowser::persistent_folder;
+typedef TreeView::Selection::ListHandle_Path ListPath;
static ImportMode
string2importmode (string str)
@@ -512,8 +513,6 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi
found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
- freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked));
-
notebook.append_page (*vbox, _("Search Tags"));
#ifdef FREESOUND
@@ -555,9 +554,9 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi
freesound_more_btn.set_label(_("More"));
freesound_more_btn.set_sensitive(false);
- passbox->pack_end (freesound_stop_btn, false, false);
- freesound_stop_btn.set_label(_("Stop"));
- freesound_stop_btn.set_sensitive(false);
+ passbox->pack_start (freesound_similar_btn, false, false);
+ freesound_similar_btn.set_label(_("Similar"));
+ freesound_similar_btn.set_sensitive(false);
scroll = manage(new ScrolledWindow);
scroll->add(freesound_list_view);
@@ -566,9 +565,8 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi
vbox = manage(new VBox);
vbox->set_spacing (3);
vbox->pack_start (*passbox, PACK_SHRINK);
- vbox->pack_start (freesound_progress_bar, PACK_SHRINK);
vbox->pack_start (*scroll);
-
+
freesound_list_view.append_column(_("ID") , freesound_list_columns.id);
freesound_list_view.append_column(_("Filename"), freesound_list_columns.filename);
// freesound_list_view.append_column(_("URI") , freesound_list_columns.uri);
@@ -577,20 +575,22 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi
freesound_list_view.append_column(_("Samplerate"), freesound_list_columns.smplrate);
freesound_list_view.append_column(_("License"), freesound_list_columns.license);
freesound_list_view.get_column(0)->set_alignment(0.5);
- freesound_list_view.get_column(1)->set_expand(true);
+ freesound_list_view.get_column(1)->set_expand(true); // filename
+ freesound_list_view.get_column(1)->set_resizable(true); // filename
freesound_list_view.get_column(2)->set_alignment(0.5);
freesound_list_view.get_column(3)->set_alignment(0.5);
freesound_list_view.get_column(4)->set_alignment(0.5);
freesound_list_view.get_column(5)->set_alignment(0.5);
freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
+ freesound_list_view.set_tooltip_column(1);
freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
freesound_more_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_more_clicked));
- freesound_stop_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_stop_clicked));
+ freesound_similar_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_similar_clicked));
notebook.append_page (*vbox, _("Search Freesound"));
#endif
@@ -787,7 +787,7 @@ SoundFileBrowser::found_list_view_selected ()
} else {
string file;
- TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
+ ListPath rows = found_list_view.get_selection()->get_selected_rows ();
if (!rows.empty()) {
TreeIter iter = found_list->get_iter(*rows.begin());
@@ -803,55 +803,6 @@ SoundFileBrowser::found_list_view_selected ()
}
void
-SoundFileBrowser::freesound_list_view_selected ()
-{
- freesound_download_cancel = false;
- freesound_stop_btn.set_sensitive(true);
-
-#ifdef FREESOUND
- if (!reset_options ()) {
- set_action_sensitive (false);
- } else {
- Mootcher mootcher;
- string file;
-
- TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
-
- if (!rows.empty()) {
- TreeIter iter = freesound_list->get_iter(*rows.begin());
-
- string id = (*iter)[freesound_list_columns.id];
- string uri = (*iter)[freesound_list_columns.uri];
- string ofn = (*iter)[freesound_list_columns.filename];
-
- // download the sound file
- GdkCursor *prev_cursor;
- prev_cursor = gdk_window_get_cursor (get_window()->gobj());
- gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
- gdk_flush();
-
- file = mootcher.getAudioFile(ofn, id, uri, this);
-
- gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
-
- if (file != "") {
- chooser.set_filename (file);
- set_action_sensitive (true);
- }
- } else {
- set_action_sensitive (false);
- }
-
- freesound_progress_bar.set_text(
- string_compose(P_("found %1 match", "found %1 matches", matches), matches));
-
- preview.setup_labels (file);
- }
-#endif
- freesound_stop_btn.set_sensitive(false);
-}
-
-void
SoundFileBrowser::found_search_clicked ()
{
string tag_string = found_entry.get_text ();
@@ -875,6 +826,91 @@ SoundFileBrowser::found_search_clicked ()
}
}
+
+std::string
+SoundFileBrowser::freesound_get_audio_file(Gtk::TreeIter iter)
+{
+
+ Mootcher *mootcher = new Mootcher;
+ std::string file;
+
+ string id = (*iter)[freesound_list_columns.id];
+ string uri = (*iter)[freesound_list_columns.uri];
+ string ofn = (*iter)[freesound_list_columns.filename];
+
+ if (mootcher->checkAudioFile(ofn, id)) {
+ // file already exists, no need to download it again
+ file = mootcher->audioFileName;
+ delete mootcher;
+ (*iter)[freesound_list_columns.started] = false;
+ return file;
+ }
+ if (!(*iter)[freesound_list_columns.started]) {
+ // start downloading the sound file
+ (*iter)[freesound_list_columns.started] = true;
+ mootcher->fetchAudioFile(ofn, id, uri, this);
+ }
+ return "";
+}
+
+void
+SoundFileBrowser::freesound_list_view_selected ()
+{
+
+ if (!reset_options ()) {
+ set_action_sensitive (false);
+ } else {
+ std::string file;
+ ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
+ for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
+ file = freesound_get_audio_file (freesound_list->get_iter(*i));
+ }
+
+ switch (rows.size()) {
+ case 0:
+ // nothing selected
+ freesound_similar_btn.set_sensitive(false);
+ set_action_sensitive (false);
+ break;
+ case 1:
+ // exactly one item selected
+ if (file != "") {
+ // file exists on disk already
+ chooser.set_filename (file);
+ preview.setup_labels (file);
+ set_action_sensitive (true);
+ }
+ freesound_similar_btn.set_sensitive(true);
+ break;
+ default:
+ // multiple items selected
+ preview.setup_labels ("");
+ freesound_similar_btn.set_sensitive(false);
+ break;
+ }
+
+ }
+}
+
+void
+SoundFileBrowser::refresh_display(std::string ID, std::string file)
+{
+ // called when the mootcher has finished downloading a file
+ ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
+ if (rows.size() == 1) {
+ // there's a single item selected in the freesound list
+ //XXX make a function to be used to construct the actual file name both here and in the mootcher
+ Gtk::TreeIter row = freesound_list->get_iter(*rows.begin());
+ std::string selected_ID = (*row)[freesound_list_columns.id];
+ if (ID == selected_ID) {
+ // the selected item in the freesound list is the item that has just finished downloading
+ chooser.set_filename(file);
+ preview.setup_labels (file);
+ set_action_sensitive (true);
+ }
+ }
+}
+
void
SoundFileBrowser::freesound_search_clicked ()
{
@@ -895,18 +931,32 @@ SoundFileBrowser::freesound_more_clicked ()
}
void
-SoundFileBrowser::freesound_stop_clicked ()
+SoundFileBrowser::freesound_similar_clicked ()
{
- freesound_download_cancel = true;
+ ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
+ if (rows.size() == 1) {
+ Mootcher mootcher;
+ string id;
+ Gtk::TreeIter iter = freesound_list->get_iter(*rows.begin());
+ id = (*iter)[freesound_list_columns.id];
+ freesound_list->clear();
+
+ GdkCursor *prev_cursor;
+ prev_cursor = gdk_window_get_cursor (get_window()->gobj());
+ gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
+ gdk_flush();
+
+ std::string theString = mootcher.searchSimilar(id);
+
+ gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
+ handle_freesound_results(theString);
+ }
}
-
void
SoundFileBrowser::freesound_search()
{
-#ifdef FREESOUND
Mootcher mootcher;
- freesound_list_view.get_column(1)->set_sizing(TREE_VIEW_COLUMN_GROW_ONLY);
string search_string = freesound_entry.get_text ();
enum sortMethod sort_method = (enum sortMethod) freesound_sort.get_active_row_number();
@@ -914,7 +964,6 @@ SoundFileBrowser::freesound_search()
GdkCursor *prev_cursor;
prev_cursor = gdk_window_get_cursor (get_window()->gobj());
gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
- freesound_progress_bar.set_fraction(0.0);
gdk_flush();
std::string theString = mootcher.searchText(
@@ -929,7 +978,11 @@ SoundFileBrowser::freesound_search()
);
gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
+ handle_freesound_results(theString);
+}
+void
+SoundFileBrowser::handle_freesound_results(std::string theString) {
XMLTree doc;
doc.read_buffer( theString );
XMLNode *root = doc.root();
@@ -1065,14 +1118,6 @@ SoundFileBrowser::freesound_search()
matches++;
}
}
-
- if (matches == 0) {
- freesound_progress_bar.set_text(_("Search returned no results."));
- } else {
- freesound_progress_bar.set_text(string_compose(P_("Found %1 match", "Found %1 matches", matches), matches));
- }
- freesound_list_view.get_column(1)->set_sizing(TREE_VIEW_COLUMN_AUTOSIZE);
-#endif
}
vector<string>
@@ -1093,9 +1138,7 @@ SoundFileBrowser::get_paths ()
}
}
- } else if (n==1){
-
- typedef TreeView::Selection::ListHandle_Path ListPath;
+ } else if (n == 1) {
ListPath rows = found_list_view.get_selection()->get_selected_rows ();
for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
@@ -1106,28 +1149,12 @@ SoundFileBrowser::get_paths ()
}
} else {
#ifdef FREESOUND
- typedef TreeView::Selection::ListHandle_Path ListPath;
- Mootcher mootcher;
-
ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
- TreeIter iter = freesound_list->get_iter(*i);
- string id = (*iter)[freesound_list_columns.id];
- string uri = (*iter)[freesound_list_columns.uri];
- string ofn = (*iter)[freesound_list_columns.filename];
-
- GdkCursor *prev_cursor;
- prev_cursor = gdk_window_get_cursor (get_window()->gobj());
- gdk_window_set_cursor (get_window()->gobj(), gdk_cursor_new(GDK_WATCH));
- gdk_flush();
-
- string str = mootcher.getAudioFile(ofn, id, uri, this);
+ string str = freesound_get_audio_file (freesound_list->get_iter(*i));
if (str != "") {
results.push_back (str);
}
-
- gdk_window_set_cursor (get_window()->gobj(), prev_cursor);
-
}
#endif
}
diff --git a/gtk2_ardour/sfdb_ui.h b/gtk2_ardour/sfdb_ui.h
index bed800ad73..5d8decddf4 100644
--- a/gtk2_ardour/sfdb_ui.h
+++ b/gtk2_ardour/sfdb_ui.h
@@ -131,6 +131,7 @@ class SoundFileBrowser : public ArdourWindow
Gtk::TreeModelColumn<std::string> filesize;
Gtk::TreeModelColumn<std::string> smplrate;
Gtk::TreeModelColumn<std::string> license;
+ Gtk::TreeModelColumn<bool> started;
FreesoundColumns() {
add(id);
@@ -140,6 +141,7 @@ class SoundFileBrowser : public ArdourWindow
add(filesize);
add(smplrate);
add(license);
+ add(started);
}
};
@@ -150,8 +152,9 @@ class SoundFileBrowser : public ArdourWindow
Glib::RefPtr<Gtk::ListStore> freesound_list;
Gtk::Button freesound_more_btn;
- Gtk::Button freesound_stop_btn;
+ Gtk::Button freesound_similar_btn;
+ void handle_freesound_results(std::string theString);
public:
SoundFileBrowser (std::string title, ARDOUR::Session* _s, bool persistent);
virtual ~SoundFileBrowser ();
@@ -177,11 +180,10 @@ class SoundFileBrowser : public ArdourWindow
Gtk::Button freesound_search_btn;
Gtk::TreeView freesound_list_view;
- Gtk::ProgressBar freesound_progress_bar;
-
- bool freesound_download_cancel;
+ Gtk::Notebook notebook;
void freesound_search();
+ void refresh_display(std::string ID, std::string file);
protected:
bool resetting_ourselves;
@@ -203,7 +205,6 @@ class SoundFileBrowser : public ArdourWindow
static std::string persistent_folder;
- Gtk::Notebook notebook;
GainMeter* gm;
Gtk::VBox meter_packer;
@@ -224,10 +225,11 @@ class SoundFileBrowser : public ArdourWindow
void freesound_list_view_activated (const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*);
void freesound_search_clicked ();
void freesound_more_clicked ();
- void freesound_stop_clicked ();
+ void freesound_similar_clicked ();
int freesound_page;
void chooser_file_activated ();
+ std::string freesound_get_audio_file(Gtk::TreeIter iter);
bool on_audio_filter (const Gtk::FileFilter::Info& filter_info);
bool on_midi_filter (const Gtk::FileFilter::Info& filter_info);
diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc
index 8d2276bfb9..08b00ec7fa 100644
--- a/gtk2_ardour/time_axis_view.cc
+++ b/gtk2_ardour/time_axis_view.cc
@@ -310,7 +310,7 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
{
switch (ev->direction) {
case GDK_SCROLL_UP:
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
/* See Editor::_stepping_axis_view for notes on this hack */
Editor& e = dynamic_cast<Editor&> (_editor);
if (!e.stepping_axis_view ()) {
@@ -325,7 +325,7 @@ TimeAxisView::controls_ebox_scroll (GdkEventScroll* ev)
break;
case GDK_SCROLL_DOWN:
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
/* See Editor::_stepping_axis_view for notes on this hack */
Editor& e = dynamic_cast<Editor&> (_editor);
if (!e.stepping_axis_view ()) {
diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h
index f19f8fe808..d8440f24f3 100644
--- a/libs/ardour/ardour/rc_configuration_vars.h
+++ b/libs/ardour/ardour/rc_configuration_vars.h
@@ -192,6 +192,7 @@ CONFIG_VARIABLE (bool, sound_midi_notes, "sound-midi-notes", false)
CONFIG_VARIABLE (bool, use_plugin_own_gui, "use-plugin-own-gui", true)
CONFIG_VARIABLE (uint32_t, max_recent_sessions, "max-recent-sessions", 10)
CONFIG_VARIABLE (double, automation_thinning_factor, "automation-thinning-factor", 20.0)
+CONFIG_VARIABLE (std::string, freesound_download_dir, "freesound-download-dir", Glib::get_home_dir() + "/Freesound/snd")
/* denormal management */
diff --git a/libs/gtkmm2ext/gtkmm2ext/keyboard.h b/libs/gtkmm2ext/gtkmm2ext/keyboard.h
index 909f791403..84988e1525 100644
--- a/libs/gtkmm2ext/gtkmm2ext/keyboard.h
+++ b/libs/gtkmm2ext/gtkmm2ext/keyboard.h
@@ -59,6 +59,11 @@ class Keyboard : public sigc::trackable, PBD::Stateful
static uint32_t GainFineScaleModifier;
static uint32_t GainExtraFineScaleModifier;
+ // Modifiers for scroll wheel
+ static uint32_t ScrollZoomVerticalModifier;
+ static uint32_t ScrollZoomHorizontalModifier;
+ static uint32_t ScrollHorizontalModifier;
+
static const char* primary_modifier_name ();
static const char* secondary_modifier_name ();
static const char* tertiary_modifier_name ();
diff --git a/libs/gtkmm2ext/keyboard.cc b/libs/gtkmm2ext/keyboard.cc
index 9966972edb..f694471d9a 100644
--- a/libs/gtkmm2ext/keyboard.cc
+++ b/libs/gtkmm2ext/keyboard.cc
@@ -94,6 +94,11 @@ const char* Keyboard::rangeselect_modifier_name() { return S_("Key|Shift"); }
guint Keyboard::GainFineScaleModifier = Keyboard::PrimaryModifier;
guint Keyboard::GainExtraFineScaleModifier = Keyboard::SecondaryModifier;
+guint Keyboard::ScrollZoomVerticalModifier = Keyboard::SecondaryModifier;
+guint Keyboard::ScrollZoomHorizontalModifier = Keyboard::PrimaryModifier;
+guint Keyboard::ScrollHorizontalModifier = Keyboard::TertiaryModifier;
+
+
Keyboard* Keyboard::_the_keyboard = 0;
Gtk::Window* Keyboard::current_window = 0;
bool Keyboard::_some_magic_widget_has_focus = false;