diff options
| author | Carl Hetherington <cth@carlh.net> | 2024-03-09 00:11:38 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2024-03-09 00:11:38 +0100 |
| commit | 80313b07095814f0178be70bc0651c1e052decea (patch) | |
| tree | 5ea002e29340ee342344affce277aa0cd941d7db | |
| parent | 82f87c7711fb664b06b04d44792ed3820b3d1e01 (diff) | |
| parent | 04b5957318df591f56e0a5d39720df143dc8230d (diff) | |
Merge branch 'main' into v2.17.x
| -rw-r--r-- | cscript | 29 | ||||
| -rw-r--r-- | src/lib/dcp_content.cc | 8 | ||||
| -rw-r--r-- | src/lib/email.cc (renamed from src/lib/emailer.cc) | 31 | ||||
| -rw-r--r-- | src/lib/email.h (renamed from src/lib/emailer.h) | 7 | ||||
| -rw-r--r-- | src/lib/kdm_cli.cc | 4 | ||||
| -rw-r--r-- | src/lib/kdm_with_metadata.cc | 19 | ||||
| -rw-r--r-- | src/lib/send_notification_email_job.cc | 4 | ||||
| -rw-r--r-- | src/lib/send_problem_report_job.cc | 6 | ||||
| -rw-r--r-- | src/lib/util.cc | 29 | ||||
| -rw-r--r-- | src/lib/util.h | 2 | ||||
| -rw-r--r-- | src/lib/wscript | 2 | ||||
| -rw-r--r-- | src/tools/dcpomatic.cc | 6 | ||||
| -rw-r--r-- | src/wx/audio_panel.cc | 1 | ||||
| -rw-r--r-- | src/wx/dkdm_dialog.cc | 2 | ||||
| -rw-r--r-- | src/wx/dolby_doremi_certificate_panel.cc | 10 | ||||
| -rw-r--r-- | src/wx/full_config_dialog.cc | 6 | ||||
| -rw-r--r-- | src/wx/kdm_dialog.cc | 2 | ||||
| -rw-r--r-- | src/wx/timeline.cc | 45 | ||||
| -rw-r--r-- | src/wx/timeline.h | 3 | ||||
| -rw-r--r-- | test/util_test.cc | 11 |
20 files changed, 179 insertions, 48 deletions
@@ -38,6 +38,9 @@ for v in ['22.04']: for v in ['23.04', '23.10']: deb_build_depends[v] = copy.deepcopy(deb_build_depends_base) deb_build_depends[v].extend(['libssh-dev', 'python3.11']) +for v in ['24.04']: + deb_build_depends[v] = copy.deepcopy(deb_build_depends_base) + deb_build_depends[v].extend(['libssh-dev', 'python3.12']) for v in ['9', '10']: deb_build_depends[v] = copy.deepcopy(deb_build_depends_base) deb_build_depends[v].extend(['libssh-gcrypt-dev', 'python']) @@ -185,6 +188,30 @@ deb_depends['23.10'].extend(['libboost-filesystem1.74.0', 'libwxgtk3.2-1', 'libwxgtk-gl3.2-1']) +def debs(boost, icu, x264): + output = copy.deepcopy(deb_depends_base) + output.extend(['libboost-filesystem' + boost, + 'libboost-thread' + boost, + 'libboost-regex' + boost, + 'libboost-date-time' + boost, + 'libcairomm-1.0-1v5', + 'libpangomm-1.4-1v5', + 'libxml++2.6-2v5', + 'libzip4', + 'libicu' + icu, + 'libnettle8', + 'libssh-4', + 'libx264-' + x264, + 'libcurl4', + 'libpulse0', + 'libxerces-c3.2', + 'libnanomsg5', + 'libwxgtk3.2-1', + 'libwxgtk-gl3.2-1']) + return output + +deb_depends['24.04'] = debs(boost='1.83.0', icu='74', x264='164') + deb_depends['9'] = copy.deepcopy(deb_depends_base) deb_depends['9'].extend(['libboost-filesystem1.62.0', 'libboost-thread1.62.0', @@ -731,7 +758,7 @@ def package_debian(target, cpu, version, options): target.set('CDIST_CONFIGURE', '"' + configure_options(target, options, for_package=True) + '"') target.set('CDIST_PACKAGE', f'dcpomatic{suffix}') - target.set('CDIST_WX_VERSION', "3.2" if target.version in ("23.04", "23.10") else "3.1") + target.set('CDIST_WX_VERSION', "3.2" if target.version in ("23.04", "23.10", "24.04") else "3.1") if not target.debug: target.set('CDIST_DEBUG_PACKAGE_FLAG', '--no-ddebs') diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc index 378ba1882..c459a9ece 100644 --- a/src/lib/dcp_content.cc +++ b/src/lib/dcp_content.cc @@ -723,6 +723,14 @@ DCPContent::can_reference_audio (shared_ptr<const Film> film, string& why_not) c return false; } + if (audio && audio->stream()) { + auto const channels = audio->stream()->channels(); + if (channels != film->audio_channels()) { + why_not = String::compose(_("it has a different number of audio channels than the project; set the project to have %1 channels."), channels); + return false; + } + } + auto part = [](shared_ptr<const Content> c) { return static_cast<bool>(c->audio) && !c->audio->mapping().mapped_output_channels().empty(); }; diff --git a/src/lib/emailer.cc b/src/lib/email.cc index f580e3c56..8557b40e0 100644 --- a/src/lib/emailer.cc +++ b/src/lib/email.cc @@ -21,7 +21,7 @@ #include "compose.hpp" #include "config.h" -#include "emailer.h" +#include "email.h" #include "exceptions.h" #include <curl/curl.h> #include <boost/algorithm/string.hpp> @@ -39,7 +39,7 @@ using std::vector; using dcp::ArrayData; -Emailer::Emailer(string from, vector<string> to, string subject, string body) +Email::Email(string from, vector<string> to, string subject, string body) : _from (from) , _to (to) , _subject (subject) @@ -51,7 +51,7 @@ Emailer::Emailer(string from, vector<string> to, string subject, string body) string -Emailer::fix (string s) const +Email::fix(string s) const { boost::algorithm::replace_all (s, "\n", "\r\n"); boost::algorithm::replace_all (s, "\0", " "); @@ -60,24 +60,24 @@ Emailer::fix (string s) const void -Emailer::add_cc (string cc) +Email::add_cc(string cc) { _cc.push_back (cc); } void -Emailer::add_bcc (string bcc) +Email::add_bcc(string bcc) { _bcc.push_back (bcc); } void -Emailer::add_attachment (boost::filesystem::path file, string name, string mime_type) +Email::add_attachment(boost::filesystem::path file, string name, string mime_type) { Attachment a; - a.file = file; + a.file = dcp::ArrayData(file); a.name = name; a.mime_type = mime_type; _attachments.push_back (a); @@ -87,19 +87,19 @@ Emailer::add_attachment (boost::filesystem::path file, string name, string mime_ static size_t curl_data_shim (void* ptr, size_t size, size_t nmemb, void* userp) { - return reinterpret_cast<Emailer*>(userp)->get_data (ptr, size, nmemb); + return reinterpret_cast<Email*>(userp)->get_data (ptr, size, nmemb); } static int curl_debug_shim (CURL* curl, curl_infotype type, char* data, size_t size, void* userp) { - return reinterpret_cast<Emailer*>(userp)->debug (curl, type, data, size); + return reinterpret_cast<Email*>(userp)->debug (curl, type, data, size); } size_t -Emailer::get_data (void* ptr, size_t size, size_t nmemb) +Email::get_data(void* ptr, size_t size, size_t nmemb) { size_t const t = min (_email.length() - _offset, size * nmemb); memcpy (ptr, _email.substr (_offset, t).c_str(), t); @@ -109,7 +109,7 @@ Emailer::get_data (void* ptr, size_t size, size_t nmemb) void -Emailer::send (string server, int port, EmailProtocol protocol, string user, string password) +Email::send(string server, int port, EmailProtocol protocol, string user, string password) { char date_buffer[128]; time_t now = time (0); @@ -171,8 +171,7 @@ Emailer::send (string server, int port, EmailProtocol protocol, string user, str } bio = BIO_push (b64, bio); - ArrayData data (i.file); - BIO_write (bio, data.data(), data.size()); + BIO_write(bio, i.file.data(), i.file.size()); (void) BIO_flush (bio); char* out; @@ -247,7 +246,7 @@ Emailer::send (string server, int port, EmailProtocol protocol, string user, str string -Emailer::address_list(vector<string> addresses) +Email::address_list(vector<string> addresses) { string o; for (auto i: addresses) { @@ -259,7 +258,7 @@ Emailer::address_list(vector<string> addresses) int -Emailer::debug (CURL *, curl_infotype type, char* data, size_t size) +Email::debug(CURL *, curl_infotype type, char* data, size_t size) { if (type == CURLINFO_TEXT) { _notes += string (data, size); @@ -273,7 +272,7 @@ Emailer::debug (CURL *, curl_infotype type, char* data, size_t size) string -Emailer::encode_rfc1342 (string subject) +Email::encode_rfc1342(string subject) { auto b64 = BIO_new(BIO_f_base64()); if (!b64) { diff --git a/src/lib/emailer.h b/src/lib/email.h index 78942ad1e..36398bfd8 100644 --- a/src/lib/emailer.h +++ b/src/lib/email.h @@ -23,13 +23,14 @@ #include <boost/scoped_array.hpp> -class Emailer +class Email { public: - Emailer(std::string from, std::vector<std::string> to, std::string subject, std::string body); + Email(std::string from, std::vector<std::string> to, std::string subject, std::string body); void add_cc (std::string cc); void add_bcc (std::string bcc); + /** Add attachment, copying the contents of the file into memory */ void add_attachment (boost::filesystem::path file, std::string name, std::string mime_type); void send (std::string server, int port, EmailProtocol protocol, std::string user = "", std::string password = ""); @@ -61,7 +62,7 @@ private: std::vector<std::string> _bcc; struct Attachment { - boost::filesystem::path file; + dcp::ArrayData file; std::string name; std::string mime_type; }; diff --git a/src/lib/kdm_cli.cc b/src/lib/kdm_cli.cc index 4e3f9ccb7..e4fabe1a6 100644 --- a/src/lib/kdm_cli.cc +++ b/src/lib/kdm_cli.cc @@ -27,7 +27,7 @@ #include "cinema.h" #include "config.h" #include "dkdm_wrapper.h" -#include "emailer.h" +#include "email.h" #include "exceptions.h" #include "film.h" #include "kdm_with_metadata.h" @@ -609,7 +609,7 @@ try if (list_cinemas) { auto cinemas = Config::instance()->cinemas (); for (auto i: cinemas) { - out (String::compose("%1 (%2)", i->name, Emailer::address_list (i->emails))); + out (String::compose("%1 (%2)", i->name, Email::address_list(i->emails))); } return {}; } diff --git a/src/lib/kdm_with_metadata.cc b/src/lib/kdm_with_metadata.cc index 10054f01e..f7ff84435 100644 --- a/src/lib/kdm_with_metadata.cc +++ b/src/lib/kdm_with_metadata.cc @@ -23,7 +23,7 @@ #include "config.h" #include "cross.h" #include "dcpomatic_log.h" -#include "emailer.h" +#include "email.h" #include "kdm_with_metadata.h" #include "screen.h" #include "util.h" @@ -238,14 +238,13 @@ send_emails ( auto subject = substitute_variables(config->kdm_subject()); auto body = substitute_variables(config->kdm_email()); - string screens; + vector<string> screens; for (auto kdm: kdms_for_cinema) { - auto screen_name = kdm->get('s'); - if (screen_name) { - screens += *screen_name + ", "; + if (auto screen_name = kdm->get('s')) { + screens.push_back(*screen_name); } } - boost::algorithm::replace_all (body, "$SCREENS", screens.substr (0, screens.length() - 2)); + boost::algorithm::replace_all(body, "$SCREENS", screen_names_to_string(screens)); auto emails = first->emails(); std::copy(extra_addresses.begin(), extra_addresses.end(), std::back_inserter(emails)); @@ -253,7 +252,7 @@ send_emails ( continue; } - Emailer email (config->kdm_from(), { emails.front() }, subject, body); + Email email(config->kdm_from(), { emails.front() }, subject, body); /* Use CC for the second and subsequent email addresses, so we seem less spammy (#2310) */ for (auto cc = std::next(emails.begin()); cc != emails.end(); ++cc) { @@ -268,8 +267,9 @@ send_emails ( } email.add_attachment (zip_file, container_name_format.get(first->name_values(), ".zip"), "application/zip"); + dcp::filesystem::remove(zip_file); - auto log_details = [](Emailer& email) { + auto log_details = [](Email& email) { dcpomatic_log->log("Email content follows", LogEntry::TYPE_DEBUG_EMAIL); dcpomatic_log->log(email.email(), LogEntry::TYPE_DEBUG_EMAIL); dcpomatic_log->log("Email session follows", LogEntry::TYPE_DEBUG_EMAIL); @@ -279,13 +279,10 @@ send_emails ( try { email.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password()); } catch (...) { - dcp::filesystem::remove(zip_file); log_details (email); throw; } log_details (email); - - dcp::filesystem::remove(zip_file); } } diff --git a/src/lib/send_notification_email_job.cc b/src/lib/send_notification_email_job.cc index f40e8fefa..a2f3016f9 100644 --- a/src/lib/send_notification_email_job.cc +++ b/src/lib/send_notification_email_job.cc @@ -22,7 +22,7 @@ #include "send_notification_email_job.h" #include "exceptions.h" #include "config.h" -#include "emailer.h" +#include "email.h" #include "compose.hpp" #include "i18n.h" @@ -71,7 +71,7 @@ SendNotificationEmailJob::run () } set_progress_unknown (); - Emailer email (config->notification_from(), { config->notification_to() }, config->notification_subject(), _body); + Email email(config->notification_from(), { config->notification_to() }, config->notification_subject(), _body); for (auto i: config->notification_cc()) { email.add_cc (i); } diff --git a/src/lib/send_problem_report_job.cc b/src/lib/send_problem_report_job.cc index 34822b156..9569aca3b 100644 --- a/src/lib/send_problem_report_job.cc +++ b/src/lib/send_problem_report_job.cc @@ -26,7 +26,7 @@ #include "film.h" #include "log.h" #include "version.h" -#include "emailer.h" +#include "email.h" #include "environment_info.h" #include <libxml++/libxml++.h> @@ -108,8 +108,8 @@ SendProblemReportJob::run () body += "---<8----\n"; } - Emailer emailer (_from, {"carl@dcpomatic.com"}, "DCP-o-matic problem report", body); - emailer.send ("main.carlh.net", 2525, EmailProtocol::STARTTLS); + Email email(_from, {"carl@dcpomatic.com"}, "DCP-o-matic problem report", body); + email.send("main.carlh.net", 2525, EmailProtocol::STARTTLS); set_progress (1); set_state (FINISHED_OK); diff --git a/src/lib/util.cc b/src/lib/util.cc index 01a7f0248..ef15b90e5 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -1120,7 +1120,6 @@ word_wrap(string input, int columns) } - #ifdef DCPOMATIC_GROK void setup_grok_library_path() @@ -1147,3 +1146,31 @@ setup_grok_library_path() setenv("LD_LIBRARY_PATH", new_path.c_str(), 1); } #endif + +string +screen_names_to_string(vector<string> names) +{ + if (names.empty()) { + return {}; + } + + auto number = [](string const& s) { + return s.find_first_not_of("0123456789") == string::npos; + }; + + if (std::find_if(names.begin(), names.end(), [number](string const& s) { return !number(s); }) == names.end()) { + std::sort(names.begin(), names.end(), [](string const& a, string const& b) { + return dcp::raw_convert<int>(a) < dcp::raw_convert<int>(b); + }); + } else { + std::sort(names.begin(), names.end()); + } + + string result; + for (auto const& name: names) { + result += name + ", "; + } + + return result.substr(0, result.length() - 2); +} + diff --git a/src/lib/util.h b/src/lib/util.h index b85cf0a33..4f64369d3 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -114,4 +114,6 @@ number_attribute(cxml::ConstNodePtr node, std::string name1, std::string name2) return *value; } +extern std::string screen_names_to_string(std::vector<std::string> names); + #endif diff --git a/src/lib/wscript b/src/lib/wscript index 5bae3e0d1..878b503a8 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -87,7 +87,7 @@ sources = """ dkdm_recipient.cc dkdm_wrapper.cc dolby_cp750.cc - emailer.cc + email.cc empty.cc encoder.cc encode_server.cc diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index f65037c2f..d516c6f5b 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -72,7 +72,7 @@ #include "lib/dcpomatic_log.h" #include "lib/dcpomatic_socket.h" #include "lib/dkdm_wrapper.h" -#include "lib/emailer.h" +#include "lib/email.h" #include "lib/encode_server_finder.h" #include "lib/exceptions.h" #include "lib/ffmpeg_encoder.h" @@ -1146,9 +1146,9 @@ private: error_dialog (this, _("You must enter a valid email address when sending translations, " "otherwise the DCP-o-matic maintainers cannot credit you or contact you with questions.")); } else { - Emailer emailer(dialog.email(), { "carl@dcpomatic.com" }, "DCP-o-matic translations", body); + Email email(dialog.email(), { "carl@dcpomatic.com" }, "DCP-o-matic translations", body); try { - emailer.send ("main.carlh.net", 2525, EmailProtocol::STARTTLS); + email.send("main.carlh.net", 2525, EmailProtocol::STARTTLS); } catch (NetworkError& e) { error_dialog (this, _("Could not send translations"), std_to_wx(e.what())); } diff --git a/src/wx/audio_panel.cc b/src/wx/audio_panel.cc index d7deeec41..34923fb02 100644 --- a/src/wx/audio_panel.cc +++ b/src/wx/audio_panel.cc @@ -196,6 +196,7 @@ AudioPanel::film_changed (FilmProperty property) case FilmProperty::AUDIO_PROCESSOR: _mapping->set_output_channels (_parent->film()->audio_output_names ()); setup_peak (); + setup_sensitivity(); break; case FilmProperty::VIDEO_FRAME_RATE: setup_description (); diff --git a/src/wx/dkdm_dialog.cc b/src/wx/dkdm_dialog.cc index 8da3090c5..82c2e3598 100644 --- a/src/wx/dkdm_dialog.cc +++ b/src/wx/dkdm_dialog.cc @@ -178,7 +178,7 @@ DKDMDialog::make_clicked () return; } - auto result = _output->make (kdms, film->name(), bind(&DKDMDialog::confirm_overwrite, this, _1)); + auto result = _output->make(kdms, film->dcp_name(), bind(&DKDMDialog::confirm_overwrite, this, _1)); if (result.first) { JobManager::instance()->add (result.first); } diff --git a/src/wx/dolby_doremi_certificate_panel.cc b/src/wx/dolby_doremi_certificate_panel.cc index d290535c2..4d8845402 100644 --- a/src/wx/dolby_doremi_certificate_panel.cc +++ b/src/wx/dolby_doremi_certificate_panel.cc @@ -117,11 +117,21 @@ static void try_ims(vector<Location>& locations, string prefix, string serial) { locations.push_back({ + String::compose("%1%2xxx/Dolby-IMS1000-%3.dcicerts.zip", prefix, serial.substr(0, 3), serial), + String::compose("Dolby-IMS1000-%1.cert.sha256.pem", serial) + }); + + locations.push_back({ String::compose("%1%2xxx/Dolby-IMS2000-%3.dcicerts.zip", prefix, serial.substr(0, 3), serial), String::compose("Dolby-IMS2000-%1.cert.sha256.pem", serial) }); locations.push_back({ + String::compose("%1%2xxx/cert_Dolby-IMS3000-%3-SMPTE.zip", prefix, serial.substr(0, 3), serial), + String::compose("cert_Dolby-IMS3000-%1-SMPTE.pem", serial) + }); + + locations.push_back({ String::compose("%1%2xxx/ims-%3.dcicerts.zip", prefix, serial.substr(0, 3), serial), String::compose("ims-%1.cert.sha256.pem", serial) }); diff --git a/src/wx/full_config_dialog.cc b/src/wx/full_config_dialog.cc index 66dbae63e..e3ea91224 100644 --- a/src/wx/full_config_dialog.cc +++ b/src/wx/full_config_dialog.cc @@ -52,7 +52,7 @@ #include "lib/config.h" #include "lib/cross.h" #include "lib/dcp_content_type.h" -#include "lib/emailer.h" +#include "lib/email.h" #include "lib/exceptions.h" #include "lib/filter.h" #include "lib/log.h" @@ -983,7 +983,7 @@ private: return; } - Emailer emailer( + Email email( wx_to_std(dialog.from()), { wx_to_std(dialog.to()) }, wx_to_std(_("DCP-o-matic test email")), @@ -991,7 +991,7 @@ private: ); auto config = Config::instance(); try { - emailer.send(config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password()); + email.send(config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password()); } catch (NetworkError& e) { error_dialog(_panel, std_to_wx(e.summary()), std_to_wx(e.detail().get_value_or(""))); return; diff --git a/src/wx/kdm_dialog.cc b/src/wx/kdm_dialog.cc index c88a1ac40..5ab13b4ce 100644 --- a/src/wx/kdm_dialog.cc +++ b/src/wx/kdm_dialog.cc @@ -227,7 +227,7 @@ KDMDialog::make_clicked () return; } - auto result = _output->make (kdms, film->name(), bind (&KDMDialog::confirm_overwrite, this, _1)); + auto result = _output->make(kdms, film->dcp_name(), bind (&KDMDialog::confirm_overwrite, this, _1)); if (result.first) { JobManager::instance()->add (result.first); } diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc index 4683769d4..38e9de4ee 100644 --- a/src/wx/timeline.cc +++ b/src/wx/timeline.cc @@ -109,6 +109,7 @@ Timeline::Timeline(wxWindow* parent, ContentPanel* cp, shared_ptr<Film> film, Fi _main_canvas->Bind (wxEVT_RIGHT_DOWN, boost::bind (&Timeline::right_down, this, _1)); _main_canvas->Bind (wxEVT_MOTION, boost::bind (&Timeline::mouse_moved, this, _1)); _main_canvas->Bind (wxEVT_SIZE, boost::bind (&Timeline::resized, this)); + _main_canvas->Bind (wxEVT_MOUSEWHEEL, boost::bind(&Timeline::mouse_wheel_turned, this, _1)); _main_canvas->Bind (wxEVT_SCROLLWIN_TOP, boost::bind (&Timeline::scrolled, this, _1)); _main_canvas->Bind (wxEVT_SCROLLWIN_BOTTOM, boost::bind (&Timeline::scrolled, this, _1)); _main_canvas->Bind (wxEVT_SCROLLWIN_LINEUP, boost::bind (&Timeline::scrolled, this, _1)); @@ -133,6 +134,50 @@ Timeline::Timeline(wxWindow* parent, ContentPanel* cp, shared_ptr<Film> film, Fi void +Timeline::mouse_wheel_turned(wxMouseEvent& event) +{ + auto const rotation = event.GetWheelRotation(); + + if (event.ControlDown()) { + /* On my mouse one click of the scroll wheel is 120, and it's -ve when + * scrolling the wheel towards me. + */ + auto const scale = rotation > 0 ? + (1.0 / (rotation / 90.0)) : + (-rotation / 90.0); + + int before_start_x; + int before_start_y; + _main_canvas->GetViewStart(&before_start_x, &before_start_y); + + auto const before_pps = _pixels_per_second.get_value_or(1); + auto const before_pos = _last_mouse_wheel_x && *_last_mouse_wheel_x == event.GetX() ? + *_last_mouse_wheel_time : + (before_start_x * _x_scroll_rate + event.GetX()) / before_pps; + + set_pixels_per_second(before_pps * scale); + setup_scrollbars(); + + auto after_left = std::max(0.0, before_pos * _pixels_per_second.get_value_or(1) - event.GetX()); + _main_canvas->Scroll(after_left / _x_scroll_rate, before_start_y); + _labels_canvas->Scroll(0, before_start_y); + Refresh(); + + if (!_last_mouse_wheel_x || *_last_mouse_wheel_x != event.GetX()) { + _last_mouse_wheel_x = event.GetX(); + _last_mouse_wheel_time = before_pos; + } + } else if (event.ShiftDown()) { + int before_start_x; + int before_start_y; + _main_canvas->GetViewStart(&before_start_x, &before_start_y); + auto const width = _main_canvas->GetSize().GetWidth(); + _main_canvas->Scroll(std::max(0.0, before_start_x - rotation * 100.0 / width), before_start_y); + } +} + + +void Timeline::update_playhead () { Refresh (); diff --git a/src/wx/timeline.h b/src/wx/timeline.h index 2485e835f..621609fa7 100644 --- a/src/wx/timeline.h +++ b/src/wx/timeline.h @@ -110,6 +110,7 @@ private: void set_pixels_per_track (int h); void zoom_all (); void update_playhead (); + void mouse_wheel_turned(wxMouseEvent& event); std::shared_ptr<TimelineView> event_to_view (wxMouseEvent &); TimelineContentViewList selected_views () const; @@ -143,6 +144,8 @@ private: int _pixels_per_track; bool _first_resize; wxTimer _timer; + boost::optional<int> _last_mouse_wheel_x; + boost::optional<double> _last_mouse_wheel_time; static double const _minimum_pixels_per_second; static int const _minimum_pixels_per_track; diff --git a/test/util_test.cc b/test/util_test.cc index 49d0b3bc2..afcc4cfc9 100644 --- a/test/util_test.cc +++ b/test/util_test.cc @@ -155,3 +155,14 @@ BOOST_AUTO_TEST_CASE(word_wrap_test) BOOST_CHECK(word_wrap("hello this is a longer bit of text and it should be word-wrapped", 31) == string{"hello this is a longer bit of \ntext and it should be word-\nwrapped\n"}); BOOST_CHECK_EQUAL(word_wrap("hellocan'twrapthissadly", 5), "hello\ncan't\nwrapt\nhissa\ndly\n"); } + + +BOOST_AUTO_TEST_CASE(screen_names_to_string_test) +{ + BOOST_CHECK_EQUAL(screen_names_to_string({"1", "2", "3"}), "1, 2, 3"); + BOOST_CHECK_EQUAL(screen_names_to_string({"3", "2", "1"}), "1, 2, 3"); + BOOST_CHECK_EQUAL(screen_names_to_string({"39", "3", "10", "1", "2"}), "1, 2, 3, 10, 39"); + BOOST_CHECK_EQUAL(screen_names_to_string({"Sheila", "Fred", "Jim"}), "Fred, Jim, Sheila"); + BOOST_CHECK_EQUAL(screen_names_to_string({"Sheila", "Fred", "Jim", "1"}), "1, Fred, Jim, Sheila"); +} + |
