/*** Olive - Non-Linear Video Editor Copyright (C) 2021 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 "marker.h" #include #include #include "config/config.h" #include "common/qtutils.h" #include "panel/panelmanager.h" #include "panel/timeline/timeline.h" #include "ui/colorcoding.h" #include "widget/menu/menu.h" #include "widget/menu/menushared.h" #include "widget/timeruler/seekablewidget.h" #include "widget/timelinewidget/timelinewidget.h" namespace olive { Marker::Marker(QWidget *parent) : QWidget(parent), active_(false), drag_allowed_(false), dragging_(false) { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QFontMetrics fm = fontMetrics(); marker_height_ = fm.height(); marker_width_ = QtUtils::QFontMetricsWidth(fm, "H"); resize(marker_width_, marker_height_); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &Marker::customContextMenuRequested, this, &Marker::ShowContextMenu); } void Marker::SetActive(bool active) { active_ = active; // Generaly the same functions delete both markers and timeline blocks. This helps ensure that // markers and timeline blocks can never be selected at the same time if (active) { TimelinePanel *timeline = PanelManager::instance()->MostRecentlyFocused(); if (timeline) { timeline->timeline_widget()->DeselectAll(); } } update(); } bool Marker::active() { return active_; } void Marker::paintEvent(QPaintEvent *event) { QFontMetrics fm = fontMetrics(); int y = marker_height_; int half_width = marker_width_ / 2; int x = half_width; QPainter p(this); if (active_) { p.setPen(Qt::white); } else { p.setPen(Qt::black); } QColor color = ColorCoding::GetColor(marker_color_).toQColor(); p.setBrush(color); p.setRenderHint(QPainter::Antialiasing); int half_marker_height = marker_height_ / 3; QPoint points[] = { QPoint(x, y), QPoint(x - half_width, y - half_marker_height), QPoint(x - half_width, y - marker_height_), QPoint(x + 1 + half_width, y - marker_height_), QPoint(x + 1 + half_width, y - half_marker_height), QPoint(x + 1, y), }; p.drawPolygon(points, 6); if (!name_.isEmpty()) { resize(marker_width_ + fm.horizontalAdvance(name_) + fm.horizontalAdvance(" "), marker_height_); // Draw background rectangle color.setAlphaF(0.5); p.fillRect(x, y, fm.horizontalAdvance(name_) + fm.horizontalAdvance(" ") * 2 + half_width, -marker_height_, color); // Draw text p.drawText(x + marker_width_, y - half_marker_height, name_); } else { resize(marker_width_, marker_height_); } // In case this is a clip marker, redraw the timeline to update the clip marker TimelinePanel* timeline = PanelManager::instance()->MostRecentlyFocused(); if (timeline) { timeline->timeline_widget()->Refresh(); } } void Marker::mousePressEvent(QMouseEvent* e) { // Only select if clicking on the icon and not the label if (e->pos().x() > marker_width_) { return; } if (e->button() == Qt::LeftButton || e->button() == Qt::RightButton) { SeekableParent()->SeekToScreenPoint(this->x() + 4); if (!active_) { if (e->modifiers() != Qt::ShiftModifier) { SeekableParent()->DeselectAllMarkers(); } emit ActiveChanged(true); } else { if (e->modifiers() == Qt::ShiftModifier) { emit ActiveChanged(false); } } update(); } if (e->button() == Qt::LeftButton) { click_position_ = e->globalPos(); marker_start_x_ = x(); drag_allowed_ = true; } } void Marker::mouseMoveEvent(QMouseEvent* e) { if (drag_allowed_) { dragging_ = true; int new_position = marker_start_x_ + e->globalPos().x() - click_position_.x(); rational marker_time = SeekableParent()->ScreenToTime(new_position); if (SeekableParent()->GetSnapService()) { if (Core::instance()->snapping()) { rational movement; SeekableParent()->GetSnapService()->SnapPoint({marker_time}, &movement); if (!movement.isNull()) { marker_time += movement; } new_position = SeekableParent()->TimeToScene(marker_time); } } if (new_position > -marker_width_ / 2 && new_position < SeekableParent()->width() - marker_width_ / 2) { this->move(new_position-2, this->pos().y()); repaint(); } } } void Marker::mouseReleaseEvent(QMouseEvent* e) { if (dragging_) { rational time = SeekableParent()->SceneToTime(this->x() + 4); time = time < 0.0 ? 0.0 : time; emit TimeChanged(TimeRange(time, time)); dragging_ = false; } drag_allowed_ = false; } SeekableWidget* Marker::SeekableParent() { return dynamic_cast(parent()); } void Marker::ShowContextMenu() { // Only show context menu if we clicked on the icon and not the label QRect marker_icon(0, 0, marker_width_, marker_height_); if (!marker_icon.contains(this->mapFromGlobal(QCursor::pos()))) { return; } Menu m(this); // Color menu ColorLabelMenu color_coding_menu; connect(&color_coding_menu, &ColorLabelMenu::ColorSelected, this, &Marker::ColorChanged); m.addMenu(&color_coding_menu); m.addSeparator(); MenuShared::instance()->AddItemsForEditMenu(&m, false); m.addSeparator(); QAction rename; rename.setText(tr("Rename")); m.addAction(&rename); connect(&rename, &QAction::triggered, this, &Marker::Rename); m.exec(QCursor::pos()); } void Marker::Rename() { bool ok; QString marker_name = QInputDialog::getText(this, tr("Set Marker"), tr("Marker name:"), QLineEdit::Normal, name_, &ok); if (ok) { emit NameChanged(marker_name); } } void Marker::SetColor(int c) { marker_color_ = c; update(); } void Marker::SetName(QString s) { name_ = s; update(); } void Marker::SetTime(TimeRange time) { move(SeekableParent()->TimeToScene(time.in())-2, y()); } } // namespace olive