File indexing completed on 2025-02-23 04:35:41
0001 /* 0002 SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "waveformwidget.h" 0008 0009 #include "appglobal.h" 0010 #include "application.h" 0011 #include "scconfig.h" 0012 #include "core/subtitleline.h" 0013 #include "videoplayer/videoplayer.h" 0014 #include "actions/useraction.h" 0015 #include "actions/useractionnames.h" 0016 #include "gui/treeview/lineswidget.h" 0017 #include "gui/waveform/wavebuffer.h" 0018 #include "gui/waveform/waverenderer.h" 0019 #include "gui/waveform/zoombuffer.h" 0020 0021 #include <QRect> 0022 #include <QPainter> 0023 #include <QPaintEvent> 0024 #include <QPolygon> 0025 #include <QRegion> 0026 #include <QThread> 0027 0028 #include <QProgressBar> 0029 #include <QLabel> 0030 #include <QMenu> 0031 #include <QBoxLayout> 0032 #include <QToolButton> 0033 #include <QScrollBar> 0034 #include <QPropertyAnimation> 0035 #include <QDebug> 0036 0037 #include <KLocalizedString> 0038 0039 using namespace SubtitleComposer; 0040 0041 #define ZOOM_MIN (1 << 3) 0042 0043 WaveformWidget::WaveformWidget(QWidget *parent) 0044 : QWidget(parent), 0045 m_mediaFile(QString()), 0046 m_streamIndex(-1), 0047 m_subtitle(nullptr), 0048 m_timeStart(0.), 0049 m_timeCurrent(0.), 0050 m_timeEnd(WaveBuffer::MAX_WINDOW_ZOOM()), 0051 m_zoom(1 << 6), 0052 m_RMBDown(false), 0053 m_MMBDown(false), 0054 m_scrollBar(nullptr), 0055 m_scrollAnimation(nullptr), 0056 m_autoScroll(true), 0057 m_autoScrollPause(false), 0058 m_hoverScrollAmount(.0), 0059 m_waveformGraphics(new WaveRenderer(this)), 0060 m_progressWidget(new QWidget(this)), 0061 m_visibleLinesDirty(true), 0062 m_draggedLine(nullptr), 0063 m_widgetLayout(nullptr), 0064 m_translationMode(false), 0065 m_showTranslation(false), 0066 m_wfBuffer(new WaveBuffer(this)), 0067 m_zoomData(nullptr), 0068 m_zoomDataLen(0) 0069 { 0070 m_widgetLayout = new QBoxLayout(QBoxLayout::LeftToRight); 0071 m_widgetLayout->setContentsMargins(0, 0, 0, 0); 0072 m_widgetLayout->setSpacing(0); 0073 0074 m_waveformGraphics->installEventFilter(this); 0075 m_widgetLayout->addWidget(m_waveformGraphics); 0076 0077 connect(m_wfBuffer->zoomBuffer(), &ZoomBuffer::zoomedBufferReady, m_waveformGraphics, QOverload<>::of(&QWidget::update)); 0078 0079 m_scrollBar = new QScrollBar(Qt::Vertical, this); 0080 m_scrollBar->setPageStep(windowSize()); 0081 m_scrollBar->setRange(0, windowSize()); 0082 m_scrollBar->installEventFilter(this); 0083 m_widgetLayout->addWidget(m_scrollBar); 0084 0085 m_scrollAnimation = new QPropertyAnimation(m_scrollBar, QByteArrayLiteral("value"), this); 0086 m_scrollAnimation->setDuration(150); 0087 0088 m_btnZoomOut = createToolButton(QStringLiteral(ACT_WAVEFORM_ZOOM_OUT)); 0089 m_btnZoomIn = createToolButton(QStringLiteral(ACT_WAVEFORM_ZOOM_IN)); 0090 m_btnAutoScroll = createToolButton(QStringLiteral(ACT_WAVEFORM_AUTOSCROLL)); 0091 0092 QHBoxLayout *toolbarLayout = new QHBoxLayout(); 0093 toolbarLayout->setContentsMargins(0, 0, 0, 0); 0094 toolbarLayout->setSpacing(2); 0095 toolbarLayout->addWidget(m_btnZoomOut); 0096 toolbarLayout->addWidget(m_btnZoomIn); 0097 toolbarLayout->addSpacerItem(new QSpacerItem(2, 2, QSizePolicy::Preferred, QSizePolicy::Preferred)); 0098 toolbarLayout->addWidget(m_btnAutoScroll); 0099 toolbarLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Preferred)); 0100 0101 m_toolbar = new QWidget(this); 0102 m_toolbar->setLayout(toolbarLayout); 0103 0104 QBoxLayout *mainLayout = new QVBoxLayout(this); 0105 mainLayout->setContentsMargins(0, 0, 0, 0); 0106 mainLayout->setSpacing(5); 0107 mainLayout->addWidget(m_toolbar); 0108 mainLayout->addLayout(m_widgetLayout); 0109 0110 setMinimumWidth(300); 0111 0112 // Progress Bar 0113 m_progressWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); 0114 m_progressWidget->hide(); 0115 0116 QLabel *label = new QLabel(i18n("Generating waveform"), m_progressWidget); 0117 0118 m_progressBar = new QProgressBar(m_progressWidget); 0119 m_progressBar->setFormat(i18nc("%p is the percent value, % is the percent sign", "%p%")); 0120 m_progressBar->setMinimumWidth(300); 0121 m_progressBar->setTextVisible(true); 0122 0123 QLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight, m_progressWidget); 0124 layout->setContentsMargins(1, 0, 1, 0); 0125 layout->setSpacing(1); 0126 layout->addWidget(label); 0127 layout->addWidget(m_progressBar); 0128 0129 connect(m_scrollBar, &QScrollBar::valueChanged, this, &WaveformWidget::onScrollBarValueChanged); 0130 0131 connect(VideoPlayer::instance(), &VideoPlayer::positionChanged, this, &WaveformWidget::onPlayerPositionChanged); 0132 0133 m_hoverScrollTimer.setInterval(50); 0134 m_hoverScrollTimer.setSingleShot(false); 0135 connect(&m_hoverScrollTimer, &QTimer::timeout, this, &WaveformWidget::onHoverScrollTimeout); 0136 0137 connect(app(), &Application::actionsReady, this, &WaveformWidget::updateActions); 0138 connect(m_wfBuffer, &WaveBuffer::waveformUpdated, this, [this]() { 0139 onWaveformResize(m_waveformGraphics->span()); 0140 }); 0141 } 0142 0143 void 0144 WaveformWidget::updateActions() 0145 { 0146 const Application *app = SubtitleComposer::app(); 0147 0148 const quint32 span = m_waveformGraphics->span(); 0149 const quint32 maxZoom = span ? m_wfBuffer->lengthSamples() / span : 0; 0150 0151 m_btnZoomIn->setDefaultAction(app->action(ACT_WAVEFORM_ZOOM_IN)); 0152 m_btnZoomIn->setEnabled(m_zoom > ZOOM_MIN); 0153 0154 m_btnZoomOut->setDefaultAction(app->action(ACT_WAVEFORM_ZOOM_OUT)); 0155 m_btnZoomOut->setEnabled(m_zoom < maxZoom); 0156 0157 QAction *action = app->action(ACT_WAVEFORM_AUTOSCROLL); 0158 action->setChecked(m_autoScroll); 0159 m_btnAutoScroll->setDefaultAction(action); 0160 m_btnAutoScroll->setEnabled(m_wfBuffer->waveformDuration() > 0); 0161 } 0162 0163 WaveformWidget::~WaveformWidget() 0164 { 0165 clearAudioStream(); 0166 } 0167 0168 double 0169 WaveformWidget::windowSizeInner(double *autoScrollPadding) const 0170 { 0171 const double winSize = windowSize(); 0172 const double scrollPad = winSize * double(SCConfig::wfAutoscrollPadding()) / 100.; 0173 if(autoScrollPadding) 0174 *autoScrollPadding = scrollPad; 0175 const double innerSize = winSize - 2. * scrollPad; 0176 return qMax(innerSize, 1.); 0177 } 0178 0179 void 0180 WaveformWidget::setZoom(quint32 val) 0181 { 0182 const quint32 span = m_waveformGraphics->span(); 0183 if(span) { 0184 const quint32 maxZoom = m_wfBuffer->lengthSamples() / span; 0185 if(maxZoom && val > maxZoom) 0186 val = maxZoom; 0187 } 0188 if(val < ZOOM_MIN) 0189 val = ZOOM_MIN; 0190 0191 if(m_zoom == val) 0192 return; 0193 0194 m_wfBuffer->zoomBuffer()->setZoomScale(val); 0195 0196 const quint32 samRt = m_wfBuffer->sampleRate(); 0197 if(samRt) { 0198 const qint32 msSpanOld = m_zoom * span * 1000 / samRt; 0199 m_zoom = val; 0200 0201 const quint32 msSpanNew = val * span * 1000 / samRt; 0202 m_timeStart.shift((msSpanOld - qint32(msSpanNew)) / 2); 0203 m_timeEnd = m_timeStart.shifted(msSpanNew); 0204 handleTimeUpdate(msSpanNew); 0205 } else { 0206 m_zoom = val; 0207 } 0208 0209 updateActions(); 0210 } 0211 0212 void 0213 WaveformWidget::handleTimeUpdate(quint32 msSpan) 0214 { 0215 // make sure start time is good 0216 const quint32 msDuration = m_wfBuffer->waveformDuration() * 1000; 0217 const quint32 maxTime = msDuration < msSpan ? 0 : msDuration - msSpan; 0218 if(m_timeStart.toMillis() > maxTime) { 0219 m_timeStart.setMillisTime(maxTime); 0220 m_timeEnd.setMillisTime(maxTime + msSpan); 0221 } 0222 0223 QSignalBlocker s(m_scrollBar); 0224 m_scrollBar->setPageStep(msSpan); 0225 m_scrollBar->setRange(0, maxTime); 0226 m_scrollBar->setValue(m_timeStart.toMillis()); 0227 0228 const quint16 chans = m_wfBuffer->channels(); 0229 if(chans) { 0230 m_wfBuffer->zoomBuffer()->setZoomScale(m_zoom); 0231 if(!m_zoomData) 0232 m_zoomData = new WaveZoomData *[chans]; 0233 m_wfBuffer->zoomBuffer()->zoomedBuffer(m_timeStart.toMillis(), m_timeEnd.toMillis(), m_zoomData, &m_zoomDataLen); 0234 } 0235 0236 m_visibleLinesDirty = true; 0237 m_waveformGraphics->update(); 0238 } 0239 0240 void 0241 WaveformWidget::onWaveformResize(quint32 span) 0242 { 0243 const quint32 samRt = m_wfBuffer->sampleRate(); 0244 if(samRt) { 0245 const double windowSize = m_zoom * span * 1000 / samRt; 0246 m_timeEnd = m_timeStart.shifted(windowSize); 0247 handleTimeUpdate(windowSize); 0248 updateActions(); 0249 } else { 0250 m_visibleLinesDirty = true; 0251 m_waveformGraphics->update(); 0252 } 0253 } 0254 0255 void 0256 WaveformWidget::onWaveformRotate(bool vertical) 0257 { 0258 if(vertical) { 0259 m_widgetLayout->setDirection(QBoxLayout::LeftToRight); 0260 m_scrollBar->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); 0261 m_scrollBar->setOrientation(Qt::Vertical); 0262 } else { 0263 m_widgetLayout->setDirection(QBoxLayout::TopToBottom); 0264 m_scrollBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); 0265 m_scrollBar->setOrientation(Qt::Horizontal); 0266 } 0267 } 0268 0269 void 0270 WaveformWidget::setAutoscroll(bool autoscroll) 0271 { 0272 m_autoScroll = autoscroll; 0273 app()->action(ACT_WAVEFORM_AUTOSCROLL)->setChecked(m_autoScroll); 0274 } 0275 0276 void 0277 WaveformWidget::onScrollBarValueChanged(int value) 0278 { 0279 double winSize = windowSize(); 0280 m_timeStart = value; 0281 m_timeEnd = m_timeStart.shifted(winSize); 0282 handleTimeUpdate(winSize); 0283 } 0284 0285 void 0286 WaveformWidget::setSubtitle(Subtitle *subtitle) 0287 { 0288 if(m_subtitle) { 0289 disconnect(m_subtitle.constData(), &Subtitle::primaryChanged, this, &WaveformWidget::onSubtitleChanged); 0290 disconnect(m_subtitle.constData(), &Subtitle::secondaryChanged, this, &WaveformWidget::onSubtitleChanged); 0291 disconnect(m_subtitle.constData(), &Subtitle::lineAnchorChanged, this, &WaveformWidget::onSubtitleChanged); 0292 } 0293 0294 m_subtitle = subtitle; 0295 0296 if(m_subtitle) { 0297 connect(m_subtitle.constData(), &Subtitle::primaryChanged, this, &WaveformWidget::onSubtitleChanged); 0298 connect(m_subtitle.constData(), &Subtitle::secondaryChanged, this, &WaveformWidget::onSubtitleChanged); 0299 connect(m_subtitle.constData(), &Subtitle::lineAnchorChanged, this, &WaveformWidget::onSubtitleChanged); 0300 } 0301 0302 m_visibleLines.clear(); 0303 m_visibleLinesDirty = true; 0304 0305 m_waveformGraphics->update(); 0306 } 0307 0308 void 0309 WaveformWidget::onSubtitleChanged() 0310 { 0311 m_visibleLinesDirty = true; 0312 m_waveformGraphics->update(); 0313 } 0314 0315 QWidget * 0316 WaveformWidget::progressWidget() 0317 { 0318 return m_progressWidget; 0319 } 0320 0321 QWidget * 0322 WaveformWidget::toolbarWidget() 0323 { 0324 return m_toolbar; 0325 } 0326 0327 void 0328 WaveformWidget::setAudioStream(const QString &mediaFile, int audioStream) 0329 { 0330 if(m_mediaFile == mediaFile && audioStream == m_streamIndex) 0331 return; 0332 0333 clearAudioStream(); 0334 0335 m_mediaFile = mediaFile; 0336 m_streamIndex = audioStream; 0337 0338 m_wfBuffer->setAudioStream(m_mediaFile, m_streamIndex); 0339 } 0340 0341 void 0342 WaveformWidget::setNullAudioStream(quint64 msecVideoLength) 0343 { 0344 clearAudioStream(); 0345 0346 m_timeStart.setMillisTime(0.); 0347 m_timeStart.setMillisTime(msecVideoLength); 0348 // will do onTimeUpdated() during event from m_wfBuffer->setNullAudioStream() 0349 m_wfBuffer->setNullAudioStream(msecVideoLength); 0350 } 0351 0352 void 0353 WaveformWidget::clearAudioStream() 0354 { 0355 m_wfBuffer->clearAudioStream(); 0356 0357 m_mediaFile.clear(); 0358 m_streamIndex = -1; 0359 0360 delete[] m_zoomData; 0361 m_zoomData = nullptr; 0362 m_zoomDataLen = 0; 0363 } 0364 0365 void 0366 WaveformWidget::updateVisibleLines() 0367 { 0368 if(!m_subtitle || !m_visibleLinesDirty) 0369 return; 0370 0371 m_visibleLinesDirty = false; 0372 0373 auto it = m_visibleLines.begin(); 0374 while(it != m_visibleLines.end()) { 0375 if(*it == m_draggedLine) { 0376 it = m_visibleLines.erase(it); 0377 } else if((*it)->line()->subtitle() != m_subtitle.data() 0378 || !(*it)->line()->intersectsTimespan(m_timeStart, m_timeEnd)) { 0379 delete *it; 0380 it = m_visibleLines.erase(it); 0381 } else { 0382 ++it; 0383 } 0384 } 0385 0386 it = m_visibleLines.begin(); 0387 for(int i = 0, n = m_subtitle->count(); i < n; i++) { 0388 SubtitleLine *sub = m_subtitle->at(i); 0389 const bool isDragged = m_draggedLine != nullptr && sub == m_draggedLine->line(); 0390 if(!sub->intersectsTimespan(m_timeStart, m_timeEnd) && !isDragged) 0391 continue; 0392 const Time showTime = isDragged ? m_draggedLine->showTime() : sub->showTime(); 0393 while(it != m_visibleLines.end() && (*it)->showTime() < showTime) { 0394 if((*it)->line() == sub) 0395 break; 0396 ++it; 0397 } 0398 if(it == m_visibleLines.end() || (*it)->line() != sub) { 0399 if(isDragged) { 0400 m_visibleLines.emplace(it, m_draggedLine); 0401 it = m_visibleLines.begin(); 0402 } else { 0403 it = m_visibleLines.emplace(it, new WaveSubtitle(sub, m_waveformGraphics)); 0404 ++it; 0405 } 0406 } 0407 } 0408 } 0409 0410 void 0411 WaveformWidget::leaveEvent(QEvent */*event*/) 0412 { 0413 m_pointerTime.setMillisTime(std::numeric_limits<double>::max()); 0414 0415 if(m_autoScrollPause) { 0416 if(!m_RMBDown) 0417 m_autoScrollPause = false; 0418 if(m_autoScroll && !m_draggedLine && !m_autoScrollPause) 0419 scrollToTime(m_timeCurrent, true); 0420 } 0421 0422 m_waveformGraphics->update(); 0423 } 0424 0425 bool 0426 WaveformWidget::eventFilter(QObject */*obj*/, QEvent *event) 0427 { 0428 switch(event->type()) { 0429 case QEvent::Wheel: { 0430 QPoint delta = static_cast<QWheelEvent *>(event)->angleDelta() / 8; 0431 if(delta.isNull()) 0432 delta = static_cast<QWheelEvent *>(event)->pixelDelta(); 0433 if(delta.isNull()) 0434 return false; 0435 0436 m_autoScrollPause = true; 0437 0438 m_scrollBar->setValue(m_timeStart.shifted(-4 * double(delta.ry()) * windowSize() / m_waveformGraphics->span()).toMillis()); 0439 return true; // stop wheel events from propagating 0440 } 0441 case QEvent::MouseButtonPress: 0442 m_autoScrollPause = true; 0443 return false; 0444 0445 default: 0446 return false; 0447 } 0448 } 0449 0450 Time 0451 WaveformWidget::timeAt(int y) 0452 { 0453 return m_timeStart + double(y * qint32(windowSize()) / m_waveformGraphics->span()); 0454 } 0455 0456 DragPosition 0457 WaveformWidget::draggableAt(double posTime, WaveSubtitle **result) 0458 { 0459 if(result) 0460 *result = nullptr; 0461 0462 if(!m_wfBuffer->sampleRate()) 0463 return DRAG_NONE; 0464 0465 const double msTolerance = 10. * m_wfBuffer->zoomBuffer()->samplesPerPixel() * 1000. / m_wfBuffer->sampleRate(); 0466 0467 DragPosition dragMode = DRAG_NONE; 0468 for(WaveSubtitle *sub: m_visibleLines) { 0469 DragPosition dm = sub->draggableAt(posTime, msTolerance); 0470 if(dm > DRAG_FORBIDDEN || dragMode == DRAG_NONE) { 0471 dragMode = dm; 0472 if(result && dm > DRAG_FORBIDDEN) 0473 *result = sub; 0474 } 0475 } 0476 0477 return dragMode; 0478 } 0479 0480 SubtitleLine * 0481 WaveformWidget::subtitleLineAtMousePosition() const 0482 { 0483 const Time mouseTime = m_RMBDown ? m_timeRMBRelease : m_pointerTime; 0484 for(const WaveSubtitle *sub: qAsConst(m_visibleLines)) { 0485 if(sub->line()->containsTime(mouseTime)) 0486 return sub->line(); 0487 } 0488 return nullptr; 0489 } 0490 0491 void 0492 WaveformWidget::setScrollPosition(double milliseconds) 0493 { 0494 if(milliseconds < m_timeStart.toMillis() || milliseconds > m_timeEnd.toMillis()) { 0495 scrollToTime(milliseconds, true); 0496 m_visibleLinesDirty = true; 0497 m_waveformGraphics->update(); 0498 } 0499 } 0500 0501 void 0502 WaveformWidget::onHoverScrollTimeout() 0503 { 0504 if(!m_draggedLine && !m_RMBDown && !m_MMBDown) { 0505 m_hoverScrollAmount = .0; 0506 m_hoverScrollTimer.stop(); 0507 return; 0508 } 0509 0510 if(m_hoverScrollAmount == .0) 0511 return; 0512 0513 const Time ptrTime(m_pointerTime.toMillis() + m_hoverScrollAmount); 0514 if(m_draggedLine) 0515 m_draggedLine->dragUpdate(ptrTime.toMillis()); 0516 if(m_RMBDown) 0517 m_timeRMBRelease = ptrTime; 0518 m_scrollBar->setValue(m_timeStart.toMillis() + m_hoverScrollAmount); 0519 } 0520 0521 void 0522 WaveformWidget::updatePointerTime(int pos) 0523 { 0524 m_pointerTime = timeAt(pos); 0525 0526 if(m_RMBDown) { 0527 m_timeRMBRelease = m_pointerTime; 0528 scrollToTime(m_pointerTime, false); 0529 } 0530 0531 if(m_MMBDown) { 0532 scrollToTime(m_pointerTime, false); 0533 emit middleMouseMove(m_pointerTime); 0534 } 0535 0536 if(m_draggedLine) { 0537 m_draggedLine->dragUpdate(m_pointerTime.toMillis()); 0538 scrollToTime(m_pointerTime, false); 0539 } else { 0540 const double posTime = timeAt(pos).toMillis(); 0541 DragPosition res = draggableAt(posTime, nullptr); 0542 if(res == DRAG_FORBIDDEN) 0543 m_waveformGraphics->setCursor(QCursor(Qt::ForbiddenCursor)); 0544 else if(res == DRAG_LINE) 0545 m_waveformGraphics->setCursor(QCursor(m_waveformGraphics->vertical() ? Qt::SizeVerCursor : Qt::SizeHorCursor)); 0546 else if(res == DRAG_SHOW || res == DRAG_HIDE) 0547 m_waveformGraphics->setCursor(QCursor(m_waveformGraphics->vertical() ? Qt::SplitVCursor : Qt::SplitHCursor)); 0548 else 0549 m_waveformGraphics->unsetCursor(); 0550 } 0551 } 0552 0553 bool 0554 WaveformWidget::mousePress(int pos, Qt::MouseButton button) 0555 { 0556 if(button == Qt::RightButton) { 0557 m_timeRMBPress = m_timeRMBRelease = timeAt(pos); 0558 m_RMBDown = true; 0559 return false; 0560 } 0561 0562 if(button == Qt::MiddleButton) { 0563 m_MMBDown = true; 0564 emit middleMouseDown(timeAt(pos)); 0565 return false; 0566 } 0567 0568 if(button != Qt::LeftButton) 0569 return false; 0570 0571 const double posTime = timeAt(pos).toMillis(); 0572 DragPosition dragMode = draggableAt(posTime, &m_draggedLine); 0573 if(m_draggedLine) { 0574 m_pointerTime.setMillisTime(posTime); 0575 m_draggedLine->dragStart(dragMode, posTime); 0576 emit dragStart(m_draggedLine->line(), dragMode); 0577 } 0578 0579 return true; 0580 } 0581 0582 bool 0583 WaveformWidget::mouseRelease(int pos, Qt::MouseButton button, const QPointF &globalPos) 0584 { 0585 if(button == Qt::RightButton) { 0586 m_timeRMBRelease = timeAt(pos); 0587 m_hoverScrollTimer.stop(); 0588 showContextMenu(globalPos.toPoint()); 0589 m_RMBDown = false; 0590 return false; 0591 } 0592 0593 if(button == Qt::MiddleButton) { 0594 emit middleMouseUp(timeAt(pos)); 0595 m_hoverScrollTimer.stop(); 0596 m_MMBDown = false; 0597 return true; 0598 } 0599 0600 if(button != Qt::LeftButton) 0601 return false; 0602 0603 if(m_draggedLine) { 0604 DragPosition mode = m_draggedLine->dragEnd(timeAt(pos).toMillis()); 0605 emit dragEnd(m_draggedLine->line(), mode); 0606 m_draggedLine = nullptr; 0607 } 0608 return true; 0609 } 0610 0611 bool 0612 WaveformWidget::scrollToTime(const Time &time, bool scrollToPage) 0613 { 0614 double windowPadding; 0615 const double windowSize = windowSizeInner(&windowPadding); 0616 if(m_draggedLine || m_RMBDown || m_MMBDown) 0617 windowPadding = this->windowSize() / 5.; 0618 0619 const double topPadding = m_timeStart.toMillis() + windowPadding; 0620 const double bottomPadding = m_timeEnd.toMillis() - windowPadding; 0621 if(time <= bottomPadding && time >= topPadding) { 0622 if(!scrollToPage) { 0623 m_hoverScrollAmount = .0; 0624 m_hoverScrollTimer.stop(); 0625 } 0626 return false; 0627 } 0628 0629 if(scrollToPage) { 0630 const int scrollPosition = int(time.toMillis() / windowSize) * windowSize - windowPadding; 0631 if(SCConfig::wfSmoothScroll()) { 0632 m_scrollAnimation->setEndValue(scrollPosition); 0633 m_scrollAnimation->start(); 0634 } else { 0635 m_scrollBar->setValue(scrollPosition); 0636 } 0637 } else { 0638 const double bd = time.toMillis() - (time < topPadding ? topPadding : bottomPadding); 0639 m_hoverScrollAmount = bd * bd * bd / (3. * windowPadding * windowPadding); 0640 if(!m_hoverScrollTimer.isActive()) 0641 m_hoverScrollTimer.start(); 0642 } 0643 0644 return true; 0645 } 0646 0647 void 0648 WaveformWidget::onPlayerPositionChanged(double seconds) 0649 { 0650 Time playingPosition; 0651 playingPosition.setSecondsTime(seconds); 0652 0653 if(m_timeCurrent != playingPosition) { 0654 m_timeCurrent = playingPosition; 0655 0656 if(m_autoScroll && !m_draggedLine && !m_autoScrollPause) 0657 scrollToTime(m_timeCurrent, true); 0658 0659 if(m_waveformGraphics) 0660 m_waveformGraphics->update(); 0661 } 0662 } 0663 0664 QToolButton * 0665 WaveformWidget::createToolButton(const QString &actionName, int iconSize) 0666 { 0667 QToolButton *toolButton = new QToolButton(this); 0668 toolButton->setObjectName(actionName); 0669 toolButton->setMinimumSize(iconSize, iconSize); 0670 toolButton->setIconSize(iconSize >= 32 ? QSize(iconSize - 6, iconSize - 6) : QSize(iconSize, iconSize)); 0671 toolButton->setAutoRaise(true); 0672 toolButton->setFocusPolicy(Qt::NoFocus); 0673 return toolButton; 0674 } 0675 0676 void 0677 WaveformWidget::showContextMenu(QPoint pos) 0678 { 0679 static QMenu *menu = nullptr; 0680 static QList<QAction *> needCurrentLine; 0681 static QList<QAction *> needSubtitle; 0682 0683 const Application *app = SubtitleComposer::app(); 0684 SubtitleLine *currentLine = subtitleLineAtMousePosition(); 0685 SubtitleLine *selectedLine = app->linesWidget()->currentLine(); 0686 0687 if(!menu) { 0688 UserActionManager *actionManager = UserActionManager::instance(); 0689 menu = new QMenu(this); 0690 0691 needCurrentLine.append( 0692 menu->addAction(QIcon::fromTheme(QStringLiteral("select")), i18n("Select Line"), [&](){ 0693 app->linesWidget()->setCurrentLine(currentLine, true); 0694 })); 0695 menu->addSeparator(); 0696 actionManager->addAction( 0697 menu->addAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Insert Line"), [&](){ 0698 const Time timeShow = rightMouseSoonerTime(); 0699 const Time timeHide = rightMouseLaterTime(); 0700 SubtitleLine *newLine = new SubtitleLine(timeShow, 0701 timeHide.toMillis() - timeShow.toMillis() > SCConfig::minDuration() ? timeHide : timeShow + SCConfig::minDuration()); 0702 m_subtitle->insertLine(newLine); 0703 app->linesWidget()->setCurrentLine(newLine, true); 0704 }), 0705 UserAction::SubOpened); 0706 needCurrentLine.append( 0707 menu->addAction(QIcon::fromTheme(QStringLiteral("list-remove")), i18n("Remove Line"), [&](){ 0708 m_subtitle->removeLines(RangeList(Range(currentLine->index())), SubtitleTarget::Both); 0709 if(selectedLine != currentLine) 0710 app->linesWidget()->setCurrentLine(selectedLine, true); 0711 })); 0712 menu->addSeparator(); 0713 needSubtitle.append( 0714 menu->addAction(i18n("Join Lines"), this, [&](){ 0715 int startIndex = -1, endIndex = -1; 0716 const Time startTime = rightMouseSoonerTime(); 0717 const Time endTime = rightMouseLaterTime(); 0718 for(int idx = 0, n = m_subtitle->count(); idx < n; idx++) { 0719 const SubtitleLine *sub = m_subtitle->at(idx); 0720 if(sub->intersectsTimespan(startTime, endTime)) { 0721 if(startIndex == -1 || startIndex > idx) 0722 startIndex = idx; 0723 if(endIndex == -1 || endIndex < idx) 0724 endIndex = idx; 0725 } 0726 } 0727 if(endIndex >= 0 && startIndex != endIndex) 0728 m_subtitle->joinLines(RangeList(Range(startIndex, endIndex))); 0729 }) 0730 ); 0731 needCurrentLine.append( 0732 menu->addAction(i18n("Split Line"), this, [&](){ 0733 // TODO: split the line at exact waveform mouse position 0734 m_subtitle->splitLines(RangeList(Range(currentLine->index()))); 0735 })); 0736 menu->addSeparator(); 0737 needCurrentLine.append( 0738 menu->addAction(i18n("Toggle Anchor"), this, [&](){ m_subtitle->toggleLineAnchor(currentLine); })); 0739 menu->addAction(app->action(ACT_ANCHOR_REMOVE_ALL)); 0740 menu->addSeparator(); 0741 actionManager->addAction( 0742 menu->addAction(QIcon::fromTheme(QStringLiteral("set_show_time")), i18n("Set Current Line Show Time"), [&](){ 0743 selectedLine->setShowTime(m_timeRMBRelease); 0744 }), 0745 UserAction::HasSelection | UserAction::EditableShowTime); 0746 actionManager->addAction( 0747 menu->addAction(QIcon::fromTheme(QStringLiteral("set_hide_time")), i18n("Set Current Line Hide Time"), [&](){ 0748 selectedLine->setHideTime(m_timeRMBRelease); 0749 }), 0750 UserAction::HasSelection | UserAction::EditableShowTime); 0751 } 0752 0753 foreach(QAction *action, needCurrentLine) 0754 action->setDisabled(currentLine == nullptr); 0755 foreach(QAction *action, needSubtitle) 0756 action->setDisabled(m_subtitle == nullptr); 0757 0758 menu->exec(pos); 0759 } 0760 0761 void 0762 WaveformWidget::setTranslationMode(bool enabled) 0763 { 0764 m_translationMode = enabled; 0765 0766 if(!m_translationMode) 0767 setShowTranslation(false); 0768 } 0769 0770 void 0771 WaveformWidget::setShowTranslation(bool showTranslation) 0772 { 0773 if(m_showTranslation != showTranslation) { 0774 m_showTranslation = showTranslation; 0775 m_waveformGraphics->update(); 0776 } 0777 }