diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index c3aee6965..b0da1d794 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -114,8 +114,6 @@ bool FFmpegDecoder::Open() native_pix_fmt_ = GetNativePixelFormat(ideal_pix_fmt_); Q_ASSERT(native_pix_fmt_ != PixelFormat::PIX_FMT_INVALID); - - aspect_ratio_ = our_instance->sample_aspect_ratio(); } time_base_ = our_instance->stream()->time_base; @@ -465,6 +463,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled) bool image_is_still = false; ImageStream::Interlacing interlacing = ImageStream::kInterlaceNone; + rational pixel_aspect_ratio; { // Read at least two frames to get more information about this video stream @@ -484,6 +483,10 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled) interlacing = ImageStream::kInterlacedBottomFirst; } } + + pixel_aspect_ratio = av_guess_sample_aspect_ratio(instance.fmt_ctx(), + instance.stream(), + frame); } // Read second frame @@ -527,6 +530,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled) image_stream->set_height(avstream->codecpar->height); image_stream->set_format(GetNativePixelFormat(FFmpegCommon::GetCompatiblePixelFormat(static_cast(avstream->codecpar->format)))); image_stream->set_interlacing(interlacing); + image_stream->set_pixel_aspect_ratio(pixel_aspect_ratio); str = image_stream; @@ -825,7 +829,7 @@ FramePtr FFmpegDecoder::BuffersToNativeFrame(int divider, int width, int height, native_pix_fmt_, divider)); copy->set_timestamp(Timecode::timestamp_to_time(ts, time_base_)); - copy->set_sample_aspect_ratio(aspect_ratio_); + copy->set_sample_aspect_ratio(std::static_pointer_cast(stream())->pixel_aspect_ratio()); copy->allocate(); // Convert frame to RGB/A for the rest of the pipeline @@ -1262,16 +1266,6 @@ void FFmpegDecoderInstance::TruncateCacheRangeTo(const qint64 &t) } } -rational FFmpegDecoderInstance::sample_aspect_ratio() const -{ - return av_guess_sample_aspect_ratio(fmt_ctx_, avstream_, nullptr); -} - -AVStream *FFmpegDecoderInstance::stream() const -{ - return avstream_; -} - FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_index) : fmt_ctx_(nullptr), opts_(nullptr), diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 0904bf9d1..d4118cc2e 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -64,8 +64,15 @@ public: void RemoveFramesBefore(const qint64& t); void TruncateCacheRangeTo(const qint64& t); - rational sample_aspect_ratio() const; - AVStream* stream() const; + AVFormatContext* fmt_ctx() const + { + return fmt_ctx_; + } + + AVStream* stream() const + { + return avstream_; + } void ClearFrameCache(); @@ -191,7 +198,6 @@ private: PixelFormat::Format native_pix_fmt_; rational time_base_; - rational aspect_ratio_; int64_t start_time_; static QHash< Stream*, QList > instance_map_; diff --git a/app/codec/frame.cpp b/app/codec/frame.cpp index 00dcb1806..bc821b038 100644 --- a/app/codec/frame.cpp +++ b/app/codec/frame.cpp @@ -113,7 +113,11 @@ const rational &Frame::sample_aspect_ratio() const void Frame::set_sample_aspect_ratio(const rational &aspect_ratio) { - sample_aspect_ratio_ = aspect_ratio; + if (aspect_ratio.isNull()) { + sample_aspect_ratio_ = 1; + } else { + sample_aspect_ratio_ = aspect_ratio; + } } const rational &Frame::timestamp() const diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp index 5a7650e89..ab8fc311a 100644 --- a/app/codec/oiio/oiiodecoder.cpp +++ b/app/codec/oiio/oiiodecoder.cpp @@ -119,6 +119,9 @@ bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled) image_stream->set_height(in->spec().height); image_stream->set_format(GetFormatFromOIIOBasetype(in->spec())); + // FIXME: Haven't looked, does OIIO report pixel aspect ratio somewhere? + image_stream->set_pixel_aspect_ratio(1); + // Images will always have just one stream image_stream->set_index(0); diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 9b6a1e6f5..295027196 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -36,6 +36,8 @@ set(OLIVE_SOURCES common/qtutils.h common/qtutils.cpp common/range.h + common/ratiodialog.h + common/ratiodialog.cpp common/rational.h common/rational.cpp common/threadedobject.h diff --git a/app/common/ratiodialog.cpp b/app/common/ratiodialog.cpp new file mode 100644 index 000000000..35bf48820 --- /dev/null +++ b/app/common/ratiodialog.cpp @@ -0,0 +1,90 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "ratiodialog.h" + +#include +#include + +OLIVE_NAMESPACE_ENTER + +double GetFloatRatioFromUser(QWidget* parent, + const QString& title, + bool* ok_in) +{ + QString s; + + forever { + bool ok; + + s = QInputDialog::getText(parent, + title, + QCoreApplication::translate("RatioDialog", "Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"), + QLineEdit::Normal, + s, + &ok); + + if (!ok) { + // User cancelled dialog, do nothing + if (ok_in) { + *ok_in = false; + } + return qSNaN(); + } + + QStringList ratio_components = s.split(QRegExp(QStringLiteral(":|;|\\/"))); + + if (ratio_components.size() == 1) { + bool float_ok; + + double flt = ratio_components.at(0).toDouble(&float_ok); + + if (float_ok && flt > 0) { + if (ok_in) { + *ok_in = true; + } + return flt; + } + } else if (ratio_components.size() == 2) { + bool numer_ok, denom_ok; + + double num = ratio_components.at(0).toDouble(&numer_ok); + double den = ratio_components.at(1).toDouble(&denom_ok); + + if (numer_ok + && denom_ok + && num > 0) { + // Exit loop and set this ratio + if (ok_in) { + *ok_in = true; + } + return num / den; + } + } + + QMessageBox::warning(parent, + QCoreApplication::translate("RatioDialog", "Invalid custom ratio"), + QCoreApplication::translate("RatioDialog", "Failed to parse \"%1\" into an aspect ratio. Please format a " + "rational fraction with a ':' or a '/' separator.").arg(s), + QMessageBox::Ok); + } +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/common/ratiodialog.h b/app/common/ratiodialog.h new file mode 100644 index 000000000..306e36d22 --- /dev/null +++ b/app/common/ratiodialog.h @@ -0,0 +1,36 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef RATIODIALOG_H +#define RATIODIALOG_H + +#include + +#include "common/rational.h" + +OLIVE_NAMESPACE_ENTER + +double GetFloatRatioFromUser(QWidget* parent, + const QString& title, + bool* ok_in); + +OLIVE_NAMESPACE_EXIT + +#endif // RATIODIALOG_H diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp index d60e21df1..04212ddf8 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -22,11 +22,13 @@ #include #include +#include #include #include #include namespace OCIO = OCIO_NAMESPACE::v1; +#include "common/ratiodialog.h" #include "project/item/footage/footage.h" #include "project/project.h" #include "undo/undostack.h" @@ -41,6 +43,37 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : int row = 0; + video_layout->addWidget(new QLabel(tr("Pixel Aspect:")), row, 0); + + pixel_aspect_combo_ = new QComboBox(); + video_layout->addWidget(pixel_aspect_combo_, row, 1); + + AddPixelAspectRatio(tr("Square Pixels"), rational(1)); + AddPixelAspectRatio(tr("NTSC Standard"), rational(8, 9)); + AddPixelAspectRatio(tr("NTSC Widescreen"), rational(32, 27)); + AddPixelAspectRatio(tr("PAL Standard"), rational(16, 15)); + AddPixelAspectRatio(tr("PAL Widescreen"), rational(64, 45)); + AddPixelAspectRatio(tr("HD Anamorphic 1080"), rational(4, 3)); + + // Always add custom item last, much of the logic relies on this. Set this to the current AR so + // that if none of the above are ==, it will eventually select this item + AddPixelAspectRatio(QString(), rational()); + UpdateCustomItem(stream->pixel_aspect_ratio()); + + // Determine which index to select on startup + for (int i=0; icount(); i++) { + if (pixel_aspect_combo_->itemData(i).value() == stream->pixel_aspect_ratio()) { + pixel_aspect_combo_->setCurrentIndex(i); + break; + } + } + + // Pick up index signal to query for custom aspect ratio if requested + connect(pixel_aspect_combo_, static_cast(&QComboBox::currentIndexChanged), + this, &VideoStreamProperties::PixelAspectComboBoxChanged); + + row++; + video_layout->addWidget(new QLabel(tr("Interlacing:")), row, 0); video_interlace_combo_ = new QComboBox(); @@ -119,12 +152,15 @@ void VideoStreamProperties::Accept(QUndoCommand *parent) } if (video_premultiply_alpha_->isChecked() != stream_->premultiplied_alpha() - || set_colorspace != stream_->colorspace(false)) { + || set_colorspace != stream_->colorspace(false) + || static_cast(video_interlace_combo_->currentIndex()) != stream_->interlacing() + || pixel_aspect_combo_->currentData().value() != stream_->pixel_aspect_ratio()) { new VideoStreamChangeCommand(stream_, video_premultiply_alpha_->isChecked(), set_colorspace, static_cast(video_interlace_combo_->currentIndex()), + pixel_aspect_combo_->currentData().value(), parent); } @@ -163,16 +199,54 @@ bool VideoStreamProperties::IsImageSequence(ImageStream *stream) return (stream->type() == Stream::kVideo && static_cast(stream)->is_image_sequence()); } +void VideoStreamProperties::AddPixelAspectRatio(const QString &name, const rational &ratio) +{ + pixel_aspect_combo_->addItem(GetPixelAspectRatioItemText(name, ratio), + QVariant::fromValue(ratio)); +} + +QString VideoStreamProperties::GetPixelAspectRatioItemText(const QString &name, const rational &ratio) +{ + return tr("%1 (%2)").arg(name, QString::number(ratio.toDouble(), 'f', 4)); +} + +void VideoStreamProperties::UpdateCustomItem(const rational &ratio) +{ + const int custom_index = pixel_aspect_combo_->count() - 1; + + pixel_aspect_combo_->setItemText(custom_index, + GetPixelAspectRatioItemText(tr("Custom"), ratio)); + pixel_aspect_combo_->setItemData(custom_index, + QVariant::fromValue(ratio)); +} + +void VideoStreamProperties::PixelAspectComboBoxChanged(int index) +{ + // Detect if custom was selected, in which case query what the new AR should be + if (index == pixel_aspect_combo_->count() - 1) { + // Query for custom pixel aspect ratio + bool ok; + + double custom_ratio = GetFloatRatioFromUser(this, tr("Set Custom Pixel Aspect Ratio"), &ok); + + if (ok) { + UpdateCustomItem(rational::fromDouble(custom_ratio)); + } + } +} + VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(ImageStreamPtr stream, bool premultiplied, QString colorspace, ImageStream::Interlacing interlacing, + const rational &pixel_ar, QUndoCommand *parent) : UndoCommand(parent), stream_(stream), new_premultiplied_(premultiplied), new_colorspace_(colorspace), - new_interlacing_(interlacing) + new_interlacing_(interlacing), + new_pixel_ar_(pixel_ar) { } @@ -186,10 +260,12 @@ void VideoStreamProperties::VideoStreamChangeCommand::redo_internal() old_premultiplied_ = stream_->premultiplied_alpha(); old_colorspace_ = stream_->colorspace(false); old_interlacing_ = stream_->interlacing(); + old_pixel_ar_ = stream_->pixel_aspect_ratio(); stream_->set_premultiplied_alpha(new_premultiplied_); stream_->set_colorspace(new_colorspace_); stream_->set_interlacing(new_interlacing_); + stream_->set_pixel_aspect_ratio(new_pixel_ar_); } void VideoStreamProperties::VideoStreamChangeCommand::undo_internal() @@ -197,6 +273,7 @@ void VideoStreamProperties::VideoStreamChangeCommand::undo_internal() stream_->set_premultiplied_alpha(old_premultiplied_); stream_->set_colorspace(old_colorspace_); stream_->set_interlacing(old_interlacing_); + stream_->set_pixel_aspect_ratio(old_pixel_ar_); } VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(VideoStreamPtr video_stream, int64_t start_index, int64_t duration, QUndoCommand *parent) : diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.h b/app/dialog/footageproperties/streamproperties/videostreamproperties.h index 0770d6a07..a1d534cee 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.h +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.h @@ -43,6 +43,12 @@ public: private: static bool IsImageSequence(ImageStream* stream); + void AddPixelAspectRatio(const QString& name, const rational& ratio); + + static QString GetPixelAspectRatioItemText(const QString& name, const rational& ratio); + + void UpdateCustomItem(const rational& ratio); + /** * @brief Attached video stream */ @@ -73,12 +79,18 @@ private: */ IntegerSlider* imgseq_end_time_; + /** + * @brief Sets the pixel aspect ratio of the stream + */ + QComboBox* pixel_aspect_combo_; + class VideoStreamChangeCommand : public UndoCommand { public: VideoStreamChangeCommand(ImageStreamPtr stream, bool premultiplied, QString colorspace, ImageStream::Interlacing interlacing, + const rational& pixel_ar, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -93,10 +105,12 @@ private: bool new_premultiplied_; QString new_colorspace_; ImageStream::Interlacing new_interlacing_; + rational new_pixel_ar_; bool old_premultiplied_; QString old_colorspace_; ImageStream::Interlacing old_interlacing_; + rational old_pixel_ar_; }; @@ -123,6 +137,10 @@ private: int64_t old_duration_; }; + +private slots: + void PixelAspectComboBoxChanged(int index); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index d2e62f46e..56136de0f 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -242,38 +242,53 @@ QIcon Footage::icon() QString Footage::duration() { - if (streams_.isEmpty()) { - return QString(); + // Find longest stream duration + + StreamPtr longest_stream = nullptr; + rational longest; + + foreach (StreamPtr stream, streams_) { + if (stream->type() == Stream::kVideo || stream->type() == Stream::kAudio) { + rational this_stream_dur = Timecode::timestamp_to_time(stream->duration(), + stream->timebase()); + + if (this_stream_dur > longest) { + longest_stream = stream; + longest = this_stream_dur; + } + } } - if (streams_.first()->type() == Stream::kVideo) { - VideoStreamPtr video_stream = std::static_pointer_cast(streams_.first()); + if (longest_stream) { + if (longest_stream->type() == Stream::kVideo) { + VideoStreamPtr video_stream = std::static_pointer_cast(longest_stream); - int64_t duration = video_stream->duration(); - rational frame_rate_timebase = video_stream->frame_rate().flipped(); + int64_t duration = video_stream->duration(); + rational frame_rate_timebase = video_stream->frame_rate().flipped(); - if (video_stream->timebase() != frame_rate_timebase) { - // Convert from timebase to frame rate - rational duration_time = Timecode::timestamp_to_time(duration, video_stream->timebase()); - duration = Timecode::time_to_timestamp(duration_time, frame_rate_timebase); + if (video_stream->timebase() != frame_rate_timebase) { + // Convert from timebase to frame rate + rational duration_time = Timecode::timestamp_to_time(duration, video_stream->timebase()); + duration = Timecode::time_to_timestamp(duration_time, frame_rate_timebase); + } + + return Timecode::timestamp_to_timecode(duration, + frame_rate_timebase, + Core::instance()->GetTimecodeDisplay()); + } else if (longest_stream->type() == Stream::kAudio) { + AudioStreamPtr audio_stream = std::static_pointer_cast(longest_stream); + + // If we're showing in a timecode, we prefer showing audio in seconds instead + Timecode::Display display = Core::instance()->GetTimecodeDisplay(); + if (display == Timecode::kTimecodeDropFrame + || display == Timecode::kTimecodeNonDropFrame) { + display = Timecode::kTimecodeSeconds; + } + + return Timecode::timestamp_to_timecode(longest_stream->duration(), + longest_stream->timebase(), + display); } - - return Timecode::timestamp_to_timecode(duration, - frame_rate_timebase, - Core::instance()->GetTimecodeDisplay()); - } else if (streams_.first()->type() == Stream::kAudio) { - AudioStreamPtr audio_stream = std::static_pointer_cast(streams_.first()); - - // If we're showing in a timecode, we prefer showing audio in seconds instead - Timecode::Display display = Core::instance()->GetTimecodeDisplay(); - if (display == Timecode::kTimecodeDropFrame - || display == Timecode::kTimecodeNonDropFrame) { - display = Timecode::kTimecodeSeconds; - } - - return Timecode::timestamp_to_timecode(streams_.first()->duration(), - streams_.first()->timebase(), - display); } return QString(); @@ -285,15 +300,13 @@ QString Footage::rate() return QString(); } - if (streams_.first()->type() == Stream::kVideo) { - // Return the timebase as a frame rate - VideoStreamPtr video_stream = std::static_pointer_cast(streams_.first()); - + if (HasStreamsOfType(Stream::kVideo)) { + // This is a video editor, prioritize video streams + VideoStreamPtr video_stream = std::static_pointer_cast(get_first_stream_of_type(Stream::kVideo)); return QCoreApplication::translate("Footage", "%1 FPS").arg(video_stream->frame_rate().toDouble()); - } else if (streams_.first()->type() == Stream::kAudio) { - // Return the sample rate + } else if (HasStreamsOfType(Stream::kAudio)) { + // No video streams, return audio AudioStreamPtr audio_stream = std::static_pointer_cast(streams_.first()); - return QCoreApplication::translate("Footage", "%1 Hz").arg(audio_stream->sample_rate()); } diff --git a/app/project/item/footage/imagestream.cpp b/app/project/item/footage/imagestream.cpp index fc74ac6e3..908ac8adc 100644 --- a/app/project/item/footage/imagestream.cpp +++ b/app/project/item/footage/imagestream.cpp @@ -29,7 +29,8 @@ OLIVE_NAMESPACE_ENTER ImageStream::ImageStream() : premultiplied_alpha_(false), - interlacing_(kInterlaceNone) + interlacing_(kInterlaceNone), + pixel_aspect_ratio_(1) { set_type(kImage); } diff --git a/app/project/item/footage/imagestream.h b/app/project/item/footage/imagestream.h index 78a700f77..47f59f3f2 100644 --- a/app/project/item/footage/imagestream.h +++ b/app/project/item/footage/imagestream.h @@ -93,6 +93,23 @@ public: emit ParametersChanged(); } + const rational& pixel_aspect_ratio() const + { + return pixel_aspect_ratio_; + } + + void set_pixel_aspect_ratio(const rational& r) + { + // Auto-correct null aspect ratio to 1:1 + if (r.isNull()) { + pixel_aspect_ratio_ = 1; + } else { + pixel_aspect_ratio_ = r; + } + + emit ParametersChanged(); + } + protected: virtual void FootageSetEvent(Footage*) override; @@ -109,6 +126,8 @@ private: PixelFormat::Format format_; + rational pixel_aspect_ratio_; + private slots: void ColorConfigChanged(); diff --git a/app/render/backend/opengl/openglproxy.cpp b/app/render/backend/opengl/openglproxy.cpp index fff23b55d..1bc204713 100644 --- a/app/render/backend/opengl/openglproxy.cpp +++ b/app/render/backend/opengl/openglproxy.cpp @@ -149,7 +149,7 @@ QVariant OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const Video VideoParams frame_params = frame->video_params(); // Check frame aspect ratio - if (frame->sample_aspect_ratio() != 1 && frame->sample_aspect_ratio() != 0) { + if (frame->sample_aspect_ratio() != 1) { int new_width = frame_params.width(); int new_height = frame_params.height(); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 8d982bd2b..40bf5379a 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -32,6 +32,7 @@ #include "audio/audiomanager.h" #include "common/power.h" +#include "common/ratiodialog.h" #include "common/timecodefunctions.h" #include "config/config.h" #include "project/item/sequence/sequence.h" @@ -397,6 +398,8 @@ FramePtr DecodeCachedImage(const QString &fn, const rational& time) if (frame) { frame->set_timestamp(time); + } else { + qWarning() << "Tried to load cached frame from file but it was null"; } return frame; @@ -743,45 +746,14 @@ void ViewerWidget::ContextMenuSetSafeMargins() void ViewerWidget::ContextMenuSetCustomSafeMargins() { - QString s; + bool ok; - forever { - bool ok; + double new_ratio = GetFloatRatioFromUser(this, + tr("Safe Margins"), + &ok); - s = QInputDialog::getText(this, - tr("Safe Margins"), - tr("Enter custom ratio (e.g. \"4:3\", \"16/9\", etc.):"), - QLineEdit::Normal, - s, - &ok); - - if (!ok) { - // User cancelled dialog, do nothing - return; - } - - QStringList ratio_components = s.split(QRegExp(QStringLiteral(":|;|\\/"))); - - if (ratio_components.size() == 2) { - bool numer_ok, denom_ok; - - double num = ratio_components.at(0).toDouble(&numer_ok); - double den = ratio_components.at(1).toDouble(&denom_ok); - - if (numer_ok - && denom_ok - && num > 0) { - // Exit loop and set this ratio - context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(true, num / den)); - return; - } - } - - QMessageBox::warning(this, - tr("Invalid custom ratio"), - tr("Failed to parse \"%1\" into an aspect ratio. Please format a " - "rational fraction with a ':' or a '/' separator.").arg(s), - QMessageBox::Ok); + if (ok) { + context_menu_widget_->SetSafeMargins(ViewerSafeMarginInfo(true, new_ratio)); } }