Project items are now represented directly in nodes opening up more versatility and possibilities. This is the first iteration of this and will be buggy. Need to test thoroughly.
675 lines
17 KiB
C++
675 lines
17 KiB
C++
/***
|
|
|
|
Olive - Non-Linear Video Editor
|
|
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
#include "timebasedwidget.h"
|
|
|
|
#include <QInputDialog>
|
|
|
|
#include "common/autoscroll.h"
|
|
#include "common/timecodefunctions.h"
|
|
#include "config/config.h"
|
|
#include "core.h"
|
|
#include "project/item/sequence/sequence.h"
|
|
#include "widget/timelinewidget/timelineundo.h"
|
|
|
|
namespace olive {
|
|
|
|
TimeBasedWidget::TimeBasedWidget(bool ruler_text_visible, bool ruler_cache_status_visible, QWidget *parent) :
|
|
TimelineScaledWidget(parent),
|
|
viewer_node_(nullptr),
|
|
auto_max_scrollbar_(false),
|
|
points_(nullptr),
|
|
toggle_show_all_(false),
|
|
auto_set_timebase_(true)
|
|
{
|
|
ruler_ = new TimeRuler(ruler_text_visible, ruler_cache_status_visible, this);
|
|
connect(ruler_, &TimeRuler::TimeChanged, this, &TimeBasedWidget::SetTimeAndSignal);
|
|
|
|
scrollbar_ = new ResizableTimelineScrollBar(Qt::Horizontal, this);
|
|
connect(scrollbar_, &ResizableScrollBar::ResizeBegan, this, &TimeBasedWidget::ScrollBarResizeBegan);
|
|
connect(scrollbar_, &ResizableScrollBar::ResizeMoved, this, &TimeBasedWidget::ScrollBarResizeMoved);
|
|
|
|
PassWheelEventsToScrollBar(ruler_);
|
|
}
|
|
|
|
void TimeBasedWidget::SetScaleAndCenterOnPlayhead(const double &scale)
|
|
{
|
|
SetScale(scale);
|
|
|
|
// Zoom towards the playhead
|
|
// (using a hacky singleShot so the scroll occurs after the scene and its scrollbars have updated)
|
|
QTimer::singleShot(0, this, &TimeBasedWidget::CenterScrollOnPlayhead);
|
|
}
|
|
|
|
rational TimeBasedWidget::GetTime() const
|
|
{
|
|
return Timecode::timestamp_to_time(ruler()->GetTime(), timebase());
|
|
}
|
|
|
|
const int64_t &TimeBasedWidget::GetTimestamp() const
|
|
{
|
|
return ruler_->GetTime();
|
|
}
|
|
|
|
Sequence *TimeBasedWidget::GetConnectedNode() const
|
|
{
|
|
return viewer_node_;
|
|
}
|
|
|
|
void TimeBasedWidget::ConnectViewerNode(Sequence *node)
|
|
{
|
|
if (viewer_node_ == node) {
|
|
return;
|
|
}
|
|
|
|
if (viewer_node_) {
|
|
DisconnectNodeInternal(viewer_node_);
|
|
|
|
disconnect(viewer_node_, &ViewerOutput::LengthChanged, this, &TimeBasedWidget::UpdateMaximumScroll);
|
|
disconnect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &TimeBasedWidget::SetTimebase);
|
|
|
|
if (auto_set_timebase_) {
|
|
SetTimebase(rational());
|
|
}
|
|
|
|
points_ = nullptr;
|
|
ruler()->ConnectTimelinePoints(nullptr);
|
|
scrollbar_->ConnectTimelinePoints(nullptr);
|
|
}
|
|
|
|
viewer_node_ = node;
|
|
|
|
ConnectedNodeChanged(viewer_node_);
|
|
|
|
if (viewer_node_) {
|
|
connect(viewer_node_, &ViewerOutput::LengthChanged, this, &TimeBasedWidget::UpdateMaximumScroll);
|
|
|
|
if ((points_ = ConnectTimelinePoints())) {
|
|
ruler()->ConnectTimelinePoints(points_);
|
|
scrollbar_->ConnectTimelinePoints(points_);
|
|
}
|
|
|
|
if (auto_set_timebase_) {
|
|
if (!viewer_node_->video_params().time_base().isNull()) {
|
|
SetTimebase(viewer_node_->video_params().time_base());
|
|
} else if (viewer_node_->audio_params().sample_rate() > 0) {
|
|
SetTimebase(viewer_node_->audio_params().time_base());
|
|
} else {
|
|
SetTimebase(rational());
|
|
}
|
|
|
|
connect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &TimeBasedWidget::SetTimebase);
|
|
}
|
|
|
|
ConnectNodeInternal(viewer_node_);
|
|
}
|
|
|
|
UpdateMaximumScroll();
|
|
}
|
|
|
|
void TimeBasedWidget::UpdateMaximumScroll()
|
|
{
|
|
rational length = (viewer_node_) ? viewer_node_->GetLength() : 0;
|
|
|
|
if (auto_max_scrollbar_) {
|
|
scrollbar_->setMaximum(qMax(0, qCeil(TimeToScene(length)) - width()));
|
|
}
|
|
|
|
foreach (TimeBasedView* base, timeline_views_) {
|
|
base->SetEndTime(length);
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::ScrollBarResizeBegan(int current_bar_width, bool top_handle)
|
|
{
|
|
QScrollBar* bar = static_cast<QScrollBar*>(sender());
|
|
|
|
scrollbar_start_width_ = current_bar_width;
|
|
scrollbar_start_value_ = bar->value();
|
|
scrollbar_start_scale_ = GetScale();
|
|
scrollbar_top_handle_ = top_handle;
|
|
}
|
|
|
|
void TimeBasedWidget::ScrollBarResizeMoved(int movement)
|
|
{
|
|
ResizableScrollBar* bar = static_cast<ResizableScrollBar*>(sender());
|
|
|
|
// Negate movement for the top handle
|
|
if (scrollbar_top_handle_) {
|
|
movement = -movement;
|
|
}
|
|
|
|
// The user wants the bar to be this size
|
|
int proposed_size = scrollbar_start_width_ + movement;
|
|
|
|
double ratio = double(scrollbar_start_width_) / double(proposed_size);
|
|
|
|
if (ratio > 0) {
|
|
SetScale(scrollbar_start_scale_ * ratio);
|
|
|
|
if (scrollbar_top_handle_) {
|
|
int viewable_area;
|
|
|
|
if (timeline_views_.isEmpty()) {
|
|
viewable_area = width();
|
|
} else {
|
|
viewable_area = timeline_views_.first()->width();
|
|
}
|
|
|
|
bar->setValue((scrollbar_start_value_ + viewable_area) * ratio - viewable_area);
|
|
} else {
|
|
bar->setValue(scrollbar_start_value_ * ratio);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::PageScrollToPlayhead()
|
|
{
|
|
PageScrollInternal(qRound(TimeToScene(GetTime())), true);
|
|
}
|
|
|
|
void TimeBasedWidget::CatchUpScrollToPlayhead()
|
|
{
|
|
CatchUpScrollToPoint(qRound(TimeToScene(GetTime())));
|
|
}
|
|
|
|
void TimeBasedWidget::CatchUpScrollToPoint(int point)
|
|
{
|
|
PageScrollInternal(point, false);
|
|
}
|
|
|
|
TimeRuler *TimeBasedWidget::ruler() const
|
|
{
|
|
return ruler_;
|
|
}
|
|
|
|
ResizableTimelineScrollBar *TimeBasedWidget::scrollbar() const
|
|
{
|
|
return scrollbar_;
|
|
}
|
|
|
|
void TimeBasedWidget::TimebaseChangedEvent(const rational &timebase)
|
|
{
|
|
TimelineScaledWidget::TimebaseChangedEvent(timebase);
|
|
|
|
ruler_->SetTimebase(timebase);
|
|
scrollbar_->SetTimebase(timebase);
|
|
|
|
emit TimebaseChanged(timebase);
|
|
}
|
|
|
|
void TimeBasedWidget::ScaleChangedEvent(const double &scale)
|
|
{
|
|
TimelineScaledWidget::ScaleChangedEvent(scale);
|
|
|
|
ruler_->SetScale(scale);
|
|
scrollbar_->SetScale(scale);
|
|
|
|
UpdateMaximumScroll();
|
|
|
|
toggle_show_all_ = false;
|
|
}
|
|
|
|
void TimeBasedWidget::SetAutoMaxScrollBar(bool e)
|
|
{
|
|
auto_max_scrollbar_ = e;
|
|
}
|
|
|
|
void TimeBasedWidget::resizeEvent(QResizeEvent *event)
|
|
{
|
|
QWidget::resizeEvent(event);
|
|
|
|
// Update horizontal scrollbar's page step to the width of the panel
|
|
scrollbar()->setPageStep(scrollbar()->width());
|
|
|
|
UpdateMaximumScroll();
|
|
}
|
|
|
|
TimelinePoints *TimeBasedWidget::ConnectTimelinePoints()
|
|
{
|
|
return viewer_node_->timeline_points();
|
|
}
|
|
|
|
Project *TimeBasedWidget::GetTimelinePointsProject()
|
|
{
|
|
return viewer_node_->project();
|
|
}
|
|
|
|
TimelinePoints *TimeBasedWidget::GetConnectedTimelinePoints() const
|
|
{
|
|
return points_;
|
|
}
|
|
|
|
void TimeBasedWidget::ConnectTimelineView(TimeBasedView *base, bool connect_time_change_event)
|
|
{
|
|
if (connect_time_change_event) {
|
|
connect(base, &TimeBasedView::TimeChanged, this, &TimeBasedWidget::SetTimestamp);
|
|
connect(base, &TimeBasedView::TimeChanged, this, &TimeBasedWidget::TimeChanged);
|
|
}
|
|
|
|
timeline_views_.append(base);
|
|
}
|
|
|
|
void TimeBasedWidget::PassWheelEventsToScrollBar(QObject *object)
|
|
{
|
|
wheel_passthrough_objects_.append(object);
|
|
object->installEventFilter(this);
|
|
}
|
|
|
|
void TimeBasedWidget::SetTimestamp(int64_t timestamp)
|
|
{
|
|
if (GetTime() != timestamp) {
|
|
if (UserIsDraggingPlayhead()) {
|
|
// If the user is dragging the playhead, we will simply nudge over and not use autoscroll rules.
|
|
QMetaObject::invokeMethod(this, "CatchUpScrollToPlayhead", Qt::QueuedConnection);
|
|
} else {
|
|
// Otherwise, assume we jumped to this out of nowhere and must now autoscroll
|
|
switch (static_cast<AutoScroll::Method>(Config::Current()["Autoscroll"].toInt())) {
|
|
case AutoScroll::kNone:
|
|
// Do nothing
|
|
break;
|
|
case AutoScroll::kPage:
|
|
QMetaObject::invokeMethod(this, "PageScrollToPlayhead", Qt::QueuedConnection);
|
|
break;
|
|
case AutoScroll::kSmooth:
|
|
QMetaObject::invokeMethod(this, "CenterScrollOnPlayhead", Qt::QueuedConnection);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ruler_->SetTime(timestamp);
|
|
|
|
TimeChangedEvent(timestamp);
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::SetTimebase(const rational &timebase)
|
|
{
|
|
TimelineScaledWidget::SetTimebase(timebase);
|
|
}
|
|
|
|
void TimeBasedWidget::SetScale(const double &scale)
|
|
{
|
|
// Simple QObject slot wrapper around TimelineScaledWidget::SetScale()
|
|
TimelineScaledWidget::SetScale(scale);
|
|
}
|
|
|
|
void TimeBasedWidget::ZoomIn()
|
|
{
|
|
SetScaleAndCenterOnPlayhead(GetScale() * 2);
|
|
}
|
|
|
|
void TimeBasedWidget::ZoomOut()
|
|
{
|
|
SetScaleAndCenterOnPlayhead(GetScale() * 0.5);
|
|
}
|
|
|
|
void TimeBasedWidget::GoToPrevCut()
|
|
{
|
|
// Cuts are only possible in sequences
|
|
Sequence* sequence = dynamic_cast<Sequence*>(viewer_node_);
|
|
|
|
if (!sequence) {
|
|
return;
|
|
}
|
|
|
|
if (GetTimestamp() == 0) {
|
|
return;
|
|
}
|
|
|
|
int64_t closest_cut = 0;
|
|
|
|
foreach (Track* track, sequence->GetTracks()) {
|
|
int64_t this_track_closest_cut = 0;
|
|
|
|
foreach (Block* block, track->Blocks()) {
|
|
int64_t block_out_ts = Timecode::time_to_timestamp(block->out(), timebase());
|
|
|
|
if (block_out_ts < GetTimestamp()) {
|
|
this_track_closest_cut = block_out_ts;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
closest_cut = qMax(closest_cut, this_track_closest_cut);
|
|
}
|
|
|
|
SetTimeAndSignal(closest_cut);
|
|
}
|
|
|
|
void TimeBasedWidget::GoToNextCut()
|
|
{
|
|
// Cuts are only possible in sequences
|
|
Sequence* sequence = dynamic_cast<Sequence*>(viewer_node_);
|
|
|
|
if (!sequence) {
|
|
return;
|
|
}
|
|
|
|
int64_t closest_cut = INT64_MAX;
|
|
|
|
foreach (Track* track, sequence->GetTracks()) {
|
|
int64_t this_track_closest_cut = Timecode::time_to_timestamp(track->track_length(), timebase());
|
|
|
|
if (this_track_closest_cut <= GetTimestamp()) {
|
|
this_track_closest_cut = INT64_MAX;
|
|
}
|
|
|
|
foreach (Block* block, track->Blocks()) {
|
|
int64_t block_in_ts = Timecode::time_to_timestamp(block->in(), timebase());
|
|
|
|
if (block_in_ts > GetTimestamp()) {
|
|
this_track_closest_cut = block_in_ts;
|
|
break;
|
|
}
|
|
}
|
|
|
|
closest_cut = qMin(closest_cut, this_track_closest_cut);
|
|
}
|
|
|
|
if (closest_cut < INT64_MAX) {
|
|
SetTimeAndSignal(closest_cut);
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::GoToStart()
|
|
{
|
|
if (viewer_node_) {
|
|
SetTimeAndSignal(0);
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::PrevFrame()
|
|
{
|
|
if (viewer_node_) {
|
|
SetTimeAndSignal(qMax(static_cast<int64_t>(0), ruler()->GetTime() - 1));
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::NextFrame()
|
|
{
|
|
if (viewer_node_) {
|
|
SetTimeAndSignal(ruler()->GetTime() + 1);
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::GoToEnd()
|
|
{
|
|
if (viewer_node_) {
|
|
SetTimeAndSignal(Timecode::time_to_timestamp(viewer_node_->GetLength(), timebase()));
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::SetTimeAndSignal(const int64_t &t)
|
|
{
|
|
SetTimestamp(t);
|
|
emit TimeChanged(t);
|
|
}
|
|
|
|
void TimeBasedWidget::CenterScrollOnPlayhead()
|
|
{
|
|
scrollbar_->setValue(qRound(TimeToScene(Timecode::timestamp_to_time(ruler_->GetTime(), timebase()))) - scrollbar_->width()/2);
|
|
}
|
|
|
|
void TimeBasedWidget::SetAutoSetTimebase(bool e)
|
|
{
|
|
auto_set_timebase_ = e;
|
|
}
|
|
|
|
void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time)
|
|
{
|
|
if (!points_) {
|
|
return;
|
|
}
|
|
|
|
MultiUndoCommand* command = new MultiUndoCommand();
|
|
|
|
// Enable workarea if it isn't already enabled
|
|
if (!points_->workarea()->enabled()) {
|
|
command->add_child(new WorkareaSetEnabledCommand(GetTimelinePointsProject(), points_, true));
|
|
}
|
|
|
|
// Determine our new range
|
|
rational in_point, out_point;
|
|
|
|
if (m == Timeline::kTrimIn) {
|
|
in_point = time;
|
|
|
|
if (!points_->workarea()->enabled() || points_->workarea()->out() < in_point) {
|
|
out_point = TimelineWorkArea::kResetOut;
|
|
} else {
|
|
out_point = points_->workarea()->out();
|
|
}
|
|
} else {
|
|
out_point = time;
|
|
|
|
if (!points_->workarea()->enabled() || points_->workarea()->in() > out_point) {
|
|
in_point = TimelineWorkArea::kResetIn;
|
|
} else {
|
|
in_point = points_->workarea()->in();
|
|
}
|
|
}
|
|
|
|
// Set workarea
|
|
command->add_child(new WorkareaSetRangeCommand(GetTimelinePointsProject(), points_, TimeRange(in_point, out_point)));
|
|
|
|
Core::instance()->undo_stack()->push(command);
|
|
}
|
|
|
|
void TimeBasedWidget::ResetPoint(Timeline::MovementMode m)
|
|
{
|
|
if (!points_ || !points_->workarea()->enabled()) {
|
|
return;
|
|
}
|
|
|
|
TimeRange r = points_->workarea()->range();
|
|
|
|
if (m == Timeline::kTrimIn) {
|
|
r.set_in(TimelineWorkArea::kResetIn);
|
|
} else {
|
|
r.set_out(TimelineWorkArea::kResetOut);
|
|
}
|
|
|
|
Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(GetTimelinePointsProject(), points_, r));
|
|
}
|
|
|
|
void TimeBasedWidget::PageScrollInternal(QScrollBar *bar, int maximum, int screen_position, bool whole_page_scroll)
|
|
{
|
|
int viewport_padding = maximum / 16;
|
|
|
|
if (whole_page_scroll) {
|
|
if (screen_position < bar->value()) {
|
|
// Anchor the playhead to the RIGHT of where we scroll to
|
|
bar->setValue(screen_position - maximum + viewport_padding);
|
|
} else if (screen_position > bar->value() + maximum) {
|
|
// Anchor the playhead to the LEFT of where we scroll to
|
|
bar->setValue(screen_position - viewport_padding);
|
|
}
|
|
} else {
|
|
// Just jump in increments
|
|
if (screen_position < bar->value() + viewport_padding) {
|
|
bar->setValue(bar->value() - viewport_padding);
|
|
} else if (screen_position > bar->value() + maximum - viewport_padding) {
|
|
bar->setValue(bar->value() + viewport_padding);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::PageScrollInternal(int screen_position, bool whole_page_scroll)
|
|
{
|
|
PageScrollInternal(scrollbar(), ruler()->width(), screen_position, whole_page_scroll);
|
|
}
|
|
|
|
bool TimeBasedWidget::UserIsDraggingPlayhead() const
|
|
{
|
|
if (ruler_->IsDraggingPlayhead()) {
|
|
return true;
|
|
}
|
|
|
|
foreach (TimeBasedView* view, timeline_views_) {
|
|
if (view->IsDraggingPlayhead()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TimeBasedWidget::SetInAtPlayhead()
|
|
{
|
|
SetPoint(Timeline::kTrimIn, GetTime());
|
|
}
|
|
|
|
void TimeBasedWidget::SetOutAtPlayhead()
|
|
{
|
|
SetPoint(Timeline::kTrimOut, GetTime());
|
|
}
|
|
|
|
void TimeBasedWidget::ResetIn()
|
|
{
|
|
ResetPoint(Timeline::kTrimIn);
|
|
}
|
|
|
|
void TimeBasedWidget::ResetOut()
|
|
{
|
|
ResetPoint(Timeline::kTrimOut);
|
|
}
|
|
|
|
void TimeBasedWidget::ClearInOutPoints()
|
|
{
|
|
if (!points_) {
|
|
return;
|
|
}
|
|
|
|
|
|
Core::instance()->undo_stack()->push(new WorkareaSetEnabledCommand(GetTimelinePointsProject(), points_, false));
|
|
}
|
|
|
|
void TimeBasedWidget::SetMarker()
|
|
{
|
|
if (!points_) {
|
|
return;
|
|
}
|
|
|
|
bool ok;
|
|
QString marker_name;
|
|
|
|
if (Config::Current()[QStringLiteral("SetNameWithMarker")].toBool()) {
|
|
marker_name = QInputDialog::getText(this, tr("Set Marker"), tr("Marker name:"), QLineEdit::Normal, QString(), &ok);
|
|
} else {
|
|
ok = true;
|
|
}
|
|
|
|
if (ok) {
|
|
Core::instance()->undo_stack()->push(new MarkerAddCommand(GetConnectedNode()->project(),
|
|
points_->markers(), TimeRange(GetTime(), GetTime()), marker_name));
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::ToggleShowAll()
|
|
{
|
|
if (!GetConnectedNode()) {
|
|
return;
|
|
}
|
|
|
|
if (toggle_show_all_) {
|
|
SetScale(toggle_show_all_old_scale_);
|
|
scrollbar_->setValue(toggle_show_all_old_scroll_);
|
|
|
|
// Don't have to set toggle_show_all_ because SetScale() will automatically set it to false
|
|
} else {
|
|
int w;
|
|
|
|
if (timeline_views_.isEmpty()) {
|
|
w = width();
|
|
} else {
|
|
w = timeline_views_.first()->width();
|
|
}
|
|
|
|
|
|
|
|
toggle_show_all_old_scale_ = GetScale();
|
|
toggle_show_all_old_scroll_ = scrollbar_->value();
|
|
|
|
SetScaleFromDimensions(w, GetConnectedNode()->GetLength().toDouble());
|
|
scrollbar_->setValue(0);
|
|
|
|
// Must explicitly do this because SetScale() will automatically set this to false
|
|
toggle_show_all_ = true;
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::GoToIn()
|
|
{
|
|
if (viewer_node_) {
|
|
if (points_ && points_->workarea()->enabled()) {
|
|
SetTimeAndSignal(Timecode::time_to_timestamp(points_->workarea()->in(), timebase()));
|
|
} else {
|
|
GoToStart();
|
|
}
|
|
}
|
|
}
|
|
|
|
void TimeBasedWidget::GoToOut()
|
|
{
|
|
if (viewer_node_) {
|
|
if (points_ && points_->workarea()->enabled()) {
|
|
SetTimeAndSignal(Timecode::time_to_timestamp(points_->workarea()->out(), timebase()));
|
|
} else {
|
|
GoToEnd();
|
|
}
|
|
}
|
|
}
|
|
|
|
TimeBasedWidget::MarkerAddCommand::MarkerAddCommand(Project *project, TimelineMarkerList *marker_list, const TimeRange &range, const QString &name) :
|
|
project_(project),
|
|
marker_list_(marker_list),
|
|
range_(range),
|
|
name_(name)
|
|
{
|
|
}
|
|
|
|
Project *TimeBasedWidget::MarkerAddCommand::GetRelevantProject() const
|
|
{
|
|
return project_;
|
|
}
|
|
|
|
void TimeBasedWidget::MarkerAddCommand::redo()
|
|
{
|
|
added_marker_ = marker_list_->AddMarker(range_, name_);
|
|
}
|
|
|
|
void TimeBasedWidget::MarkerAddCommand::undo()
|
|
{
|
|
marker_list_->RemoveMarker(added_marker_);
|
|
}
|
|
|
|
bool TimeBasedWidget::eventFilter(QObject *object, QEvent *event)
|
|
{
|
|
if (wheel_passthrough_objects_.contains(object) && event->type() == QEvent::Wheel) {
|
|
QCoreApplication::sendEvent(scrollbar(), event);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|