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 "waverenderer.h" 0008 0009 #include "appglobal.h" 0010 #include "application.h" 0011 #include "scconfig.h" 0012 #include "core/richtext/richdocument.h" 0013 #include "gui/waveform/wavebuffer.h" 0014 #include "gui/waveform/waveformwidget.h" 0015 #include "gui/waveform/wavesubtitle.h" 0016 #include "gui/waveform/zoombuffer.h" 0017 0018 #include <QBoxLayout> 0019 #include <QPainter> 0020 #include <QScrollBar> 0021 #include <QTextLayout> 0022 0023 using namespace SubtitleComposer; 0024 0025 0026 WaveRenderer::WaveRenderer(WaveformWidget *parent) 0027 : QWidget(parent), 0028 m_wfw(parent) 0029 { 0030 setAttribute(Qt::WA_OpaquePaintEvent, true); 0031 setAttribute(Qt::WA_NoSystemBackground, true); 0032 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0033 setMouseTracking(true); 0034 0035 connect(SCConfig::self(), &SCConfig::configChanged, this, &WaveRenderer::onConfigChanged); 0036 onConfigChanged(); 0037 } 0038 0039 bool 0040 WaveRenderer::showTranslation() const 0041 { 0042 return m_wfw->m_showTranslation; 0043 } 0044 0045 void 0046 WaveRenderer::onConfigChanged() 0047 { 0048 m_fontNumber = QFont(SCConfig::wfFontFamily(), SCConfig::wfSubNumberFontSize()); 0049 m_fontNumberHeight = QFontMetrics(m_fontNumber).height(); 0050 m_fontText = QFont(SCConfig::wfFontFamily(), SCConfig::wfSubTextFontSize()); 0051 0052 m_subBorderWidth = SCConfig::wfSubBorderWidth(); 0053 0054 m_subNumberColor = QPen(QColor(SCConfig::wfSubNumberColor()), 0, Qt::SolidLine); 0055 m_subTextColor = QPen(QColor(SCConfig::wfSubTextColor()), 0, Qt::SolidLine); 0056 0057 // FIXME: instead of using devicePixelRatioF() for pen width we should draw waveform in higher resolution 0058 m_waveInner = QPen(QColor(SCConfig::wfInnerColor()), devicePixelRatioF(), Qt::SolidLine); 0059 m_waveOuter = QPen(QColor(SCConfig::wfOuterColor()), devicePixelRatioF(), Qt::SolidLine); 0060 0061 m_subtitleBack = QColor(SCConfig::wfSubBackground()); 0062 m_subtitleBorder = QColor(SCConfig::wfSubBorder()); 0063 0064 m_selectedBack = QColor(SCConfig::wfSelBackground()); 0065 m_selectedBorder = QColor(SCConfig::wfSelBorder()); 0066 0067 m_playColor = QPen(QColor(SCConfig::wfPlayLocation()), 0, Qt::SolidLine); 0068 m_mouseColor = QPen(QColor(SCConfig::wfMouseLocation()), 0, Qt::DotLine); 0069 } 0070 0071 bool 0072 WaveRenderer::event(QEvent *evt) 0073 { 0074 switch(evt->type()) { 0075 case QEvent::Resize: { 0076 bool vertical = height() > width(); 0077 if(m_vertical != vertical) { 0078 m_vertical = vertical; 0079 m_wfw->onWaveformRotate(m_vertical); 0080 } 0081 m_wfw->onWaveformResize(span()); 0082 break; 0083 } 0084 0085 case QEvent::Paint: { 0086 QPainter painter(this); 0087 painter.fillRect(static_cast<QPaintEvent *>(evt)->rect(), Qt::black); 0088 paintGraphics(painter); 0089 return true; 0090 } 0091 0092 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0093 #define position pos 0094 #endif 0095 0096 case QEvent::MouseMove: { 0097 QMouseEvent *mouse = static_cast<QMouseEvent *>(evt); 0098 m_wfw->updatePointerTime(m_vertical ? mouse->position().y() : mouse->position().x()); 0099 update(); 0100 break; 0101 } 0102 0103 case QEvent::MouseButtonDblClick: { 0104 QMouseEvent *mouse = static_cast<QMouseEvent *>(evt); 0105 emit m_wfw->doubleClick(m_wfw->timeAt(m_vertical ? mouse->position().y() : mouse->position().x())); 0106 return true; 0107 } 0108 0109 case QEvent::MouseButtonPress: { 0110 QMouseEvent *mouse = static_cast<QMouseEvent *>(evt); 0111 int y = m_vertical ? mouse->position().y() : mouse->position().x(); 0112 if(m_wfw->mousePress(y, mouse->button())) 0113 return true; 0114 break; 0115 } 0116 0117 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0118 #define globalPosition globalPos 0119 #endif 0120 0121 case QEvent::MouseButtonRelease: { 0122 QMouseEvent *mouse = static_cast<QMouseEvent *>(evt); 0123 int y = m_vertical ? mouse->position().y() : mouse->position().x(); 0124 if(m_wfw->mouseRelease(y, mouse->button(), mouse->globalPosition())) 0125 return true; 0126 break; 0127 } 0128 0129 default: 0130 break; 0131 } 0132 0133 return QWidget::event(evt); 0134 } 0135 0136 void 0137 WaveRenderer::paintWaveform(QPainter &painter, quint32 widgetWidth, quint32 widgetHeight) 0138 { 0139 const quint16 chans = m_wfw->m_wfBuffer->channels(); 0140 if(!chans || !m_wfw->m_zoomDataLen) 0141 return; 0142 0143 const quint32 chHalfWidth = (m_vertical ? widgetWidth : widgetHeight) / chans / 2; 0144 0145 for(quint16 ch = 0; ch < chans; ch++) { 0146 const qint32 chCenter = (ch * 2 + 1) * chHalfWidth; 0147 for(quint32 y = 0; y < m_wfw->m_zoomDataLen; y++) { 0148 const qint32 xMin = m_wfw->m_zoomData[ch][y].min * chHalfWidth / SAMPLE_MAX; 0149 const qint32 xMax = m_wfw->m_zoomData[ch][y].max * chHalfWidth / SAMPLE_MAX; 0150 0151 painter.setPen(m_waveOuter); 0152 if(m_vertical) 0153 painter.drawLine(chCenter - xMax, y, chCenter + xMax, y); 0154 else 0155 painter.drawLine(y, chCenter - xMax, y, chCenter + xMax); 0156 painter.setPen(m_waveInner); 0157 if(m_vertical) 0158 painter.drawLine(chCenter - xMin, y, chCenter + xMin, y); 0159 else 0160 painter.drawLine(y, chCenter - xMin, y, chCenter + xMin); 0161 } 0162 } 0163 } 0164 0165 void 0166 WaveRenderer::paintGraphics(QPainter &painter) 0167 { 0168 const quint32 msWindowSize = m_wfw->windowSize(); 0169 const quint32 widgetHeight = height(); 0170 const quint32 widgetWidth = width(); 0171 const quint32 widgetSpan = m_vertical ? widgetHeight : widgetWidth; 0172 0173 painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); 0174 0175 if(widgetSpan) 0176 paintWaveform(painter, widgetWidth, widgetHeight); 0177 0178 m_wfw->updateVisibleLines(); 0179 0180 const RangeList &selection = app()->linesWidget()->selectionRanges(); 0181 for(const WaveSubtitle *sub: qAsConst(m_wfw->m_visibleLines)) { 0182 const Time timeShow = sub->showTime(); 0183 const Time timeHide = sub->hideTime(); 0184 if(timeShow > m_wfw->m_timeEnd || m_wfw->m_timeStart > timeHide) 0185 continue; 0186 0187 const bool selected = selection.contains(sub->line()->index()); 0188 const int showY = widgetSpan * (timeShow.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize; 0189 const int hideY = widgetSpan * (timeHide.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize; 0190 QRect box; 0191 0192 // draw background 0193 if(m_vertical) 0194 box = QRect(2, showY + m_subBorderWidth, widgetWidth - 4, hideY - showY - 2 * m_subBorderWidth); 0195 else 0196 box = QRect(showY + m_subBorderWidth, 2, hideY - showY - 2 * m_subBorderWidth, widgetHeight - 4); 0197 0198 if(!m_wfw->m_subtitle || !m_wfw->m_subtitle->hasAnchors() || m_wfw->m_subtitle->isLineAnchored(sub->line())) 0199 painter.setOpacity(1.); 0200 else 0201 painter.setOpacity(.5); 0202 0203 painter.fillRect(box, selected ? m_selectedBack : m_subtitleBack); 0204 0205 // draw border lines 0206 if(m_subBorderWidth) { 0207 if(m_vertical) { 0208 painter.fillRect(0, showY, widgetWidth, m_subBorderWidth, selected ? m_selectedBorder : m_subtitleBorder); 0209 painter.fillRect(0, hideY - m_subBorderWidth, widgetWidth, m_subBorderWidth, selected ? m_selectedBorder : m_subtitleBorder); 0210 } else { 0211 painter.fillRect(showY, 0, m_subBorderWidth, widgetHeight, selected ? m_selectedBorder : m_subtitleBorder); 0212 painter.fillRect(hideY - m_subBorderWidth, 0, m_subBorderWidth, widgetHeight, selected ? m_selectedBorder : m_subtitleBorder); 0213 } 0214 } 0215 0216 // draw text 0217 painter.save(); 0218 painter.setClipRect(box); 0219 painter.translate(box.center()); 0220 if(!m_vertical) // TODO: make rotation angle configurable 0221 painter.rotate(-45.); 0222 const QImage &st = sub->image(); 0223 painter.drawImage(st.width() / -2, st.height() / -2, st, 0, 0, -1, -1); 0224 painter.restore(); 0225 0226 // draw subtitle index 0227 painter.setPen(m_subNumberColor); 0228 painter.setFont(m_fontNumber); 0229 if(m_vertical) 0230 painter.drawText(m_fontNumberHeight / 2, showY + m_fontNumberHeight + 2, QString::number(sub->line()->number())); 0231 else 0232 painter.drawText(showY + m_fontNumberHeight / 2, m_fontNumberHeight + 2, QString::number(sub->line()->number())); 0233 0234 // draw anchor icon 0235 if(m_wfw->m_subtitle && m_wfw->m_subtitle->isLineAnchored(sub->line())) { 0236 static QFont fontAnchor("sans-serif", 12); 0237 painter.setFont(fontAnchor); 0238 if(m_vertical) 0239 painter.drawText(box, Qt::AlignTop | Qt::AlignRight, QStringLiteral("\u2693")); 0240 else 0241 painter.drawText(box, Qt::AlignBottom | Qt::AlignLeft, QStringLiteral("\u2693")); 0242 } 0243 } 0244 0245 if(m_wfw->m_RMBDown) { 0246 int showY = widgetSpan * (m_wfw->m_timeRMBPress.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize; 0247 int hideY = widgetSpan * (m_wfw->m_timeRMBRelease.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize; 0248 0249 QRect box; 0250 if(m_vertical) 0251 box = QRect(0, showY + m_subBorderWidth, widgetWidth, hideY - showY - 2 * m_subBorderWidth); 0252 else 0253 box = QRect(showY + m_subBorderWidth, 0, hideY - showY - 2 * m_subBorderWidth, widgetHeight); 0254 0255 painter.fillRect(box, m_selectedBack); 0256 } 0257 0258 int playY = widgetSpan * (m_wfw->m_timeCurrent - m_wfw->m_timeStart).toMillis() / msWindowSize; 0259 painter.setPen(m_playColor); 0260 if(m_vertical) 0261 painter.drawLine(0, playY, widgetWidth, playY); 0262 else 0263 painter.drawLine(playY, 0, playY, widgetHeight); 0264 0265 painter.setPen(m_subTextColor); 0266 painter.setFont(m_fontText); 0267 if(m_vertical) { 0268 QRect textRect(6, 4, widgetWidth - 12, widgetHeight - 8); 0269 painter.drawText(textRect, Qt::AlignRight | Qt::AlignTop, m_wfw->m_timeStart.toString()); 0270 painter.drawText(textRect, Qt::AlignRight | Qt::AlignBottom, m_wfw->m_timeEnd.toString()); 0271 } else { 0272 QRect textRect(4, 6, widgetWidth - 8, widgetHeight - 12); 0273 painter.drawText(textRect, Qt::AlignLeft | Qt::AlignTop, m_wfw->m_timeStart.toString()); 0274 painter.drawText(textRect, Qt::AlignRight | Qt::AlignTop, m_wfw->m_timeEnd.toString()); 0275 } 0276 0277 painter.setPen(m_mouseColor); 0278 playY = widgetSpan * (m_wfw->m_pointerTime - m_wfw->m_timeStart).toMillis() / msWindowSize; 0279 if(m_vertical) 0280 painter.drawLine(0, playY, widgetWidth, playY); 0281 else 0282 painter.drawLine(playY, 0, playY, widgetHeight); 0283 }