File indexing completed on 2024-12-22 04:40:10

0001 /*
0002     SPDX-FileCopyrightText: 2022 Mladen Milinkovic <max@smoothware.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "subtitlemetawidget.h"
0008 #include "ui_subtitlepositionwidget.h"
0009 
0010 #include "appglobal.h"
0011 #include "application.h"
0012 #include "actions/useractionnames.h"
0013 #include "core/subtitle.h"
0014 #include "core/subtitleline.h"
0015 #include "core/richtext/richcss.h"
0016 #include "gui/subtitlemeta/csshighlighter.h"
0017 
0018 #include <QHBoxLayout>
0019 #include <QLabel>
0020 #include <QMouseEvent>
0021 #include <QStackedLayout>
0022 #include <QTabBar>
0023 #include <QTabWidget>
0024 #include <QTextEdit>
0025 
0026 #include <QDebug>
0027 
0028 #include <KLocalizedString>
0029 #include <KTextEdit>
0030 
0031 
0032 using namespace SubtitleComposer;
0033 
0034 SubtitleMetaWidget::SubtitleMetaWidget(QWidget *parent)
0035     : QWidget(parent),
0036       m_subtitle(nullptr),
0037       m_dockTitleWidget(new QWidget(parent)),
0038       m_tabBar(new QTabBar(m_dockTitleWidget)),
0039       m_cssEdit(new QTextEdit(this)),
0040       m_commentIntroEdit(new KTextEdit(this)),
0041       m_commentTopEdit(new KTextEdit(this)),
0042       m_commentBottomEdit(new KTextEdit(this)),
0043       m_subPosition(new SubtitlePositionWidget(this))
0044 {
0045     m_tabBar->setExpanding(false);
0046     m_tabBar->addTab(i18n("&Stylesheet"));
0047     m_tabBar->addTab(i18n("&Comments"));
0048     m_tabBar->addTab(i18n("&Position"));
0049     m_tabBar->installEventFilter(this);
0050 
0051     // parent title widget otherwise QTabBar has strange margins
0052     QHBoxLayout *titleLayout = new QHBoxLayout();
0053     titleLayout->setContentsMargins(0, 0, 0, 0);
0054     titleLayout->addWidget(m_tabBar);
0055     m_dockTitleWidget->setLayout(titleLayout);
0056     m_dockTitleWidget->setContentsMargins(1, 0, 0, 0);
0057 
0058     m_cssEdit->setAcceptRichText(false);
0059 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
0060     textEdit->setTabStopWidth(4 * QFontMetrics(textEdit->currentFont()).width(QChar::Space));
0061 #else
0062     m_cssEdit->setTabStopDistance(4 * QFontMetrics(m_cssEdit->currentFont()).horizontalAdvance(QChar::Space));
0063 #endif
0064     new CSSHighlighter(m_cssEdit->document());
0065 #if 0
0066     m_cssEdit->setPlainText(R"END(/* example stylesheet */
0067 
0068 ::cue {
0069     background-url: url("base64;something ; something");
0070     background-image: linear-gradient(to bottom, dimgray, lightgray);
0071     color: papayawhip;
0072 }
0073 /* Style blocks cannot use blank lines nor "dash dash greater than" */
0074 ::cue(b) {
0075     color: peachpuff;
0076 }
0077 
0078 ::cue(#\31) {
0079     color: lime;
0080 }
0081 ::cue(#crédit\ de\ transcription) {
0082     color: red;
0083 }
0084 )END");
0085 #endif
0086 
0087     QTabWidget *comments = new QTabWidget();
0088     comments->setTabPosition(QTabWidget::East);
0089     comments->addTab(m_commentIntroEdit, i18n("Intro"));
0090     m_commentIntroEdit->installEventFilter(this);
0091     comments->addTab(m_commentTopEdit, i18n("Header"));
0092     m_commentTopEdit->installEventFilter(this);
0093     comments->addTab(m_commentBottomEdit, i18n("Footer"));
0094     m_commentBottomEdit->installEventFilter(this);
0095 
0096     QStackedLayout *mainLayout = new QStackedLayout();
0097     mainLayout->setContentsMargins(0, 0, 0, 0);
0098     mainLayout->addWidget(m_cssEdit);
0099     m_cssEdit->installEventFilter(this);
0100     mainLayout->addWidget(comments);
0101     mainLayout->addWidget(m_subPosition);
0102     setLayout(mainLayout);
0103 
0104     connect(m_tabBar, &QTabBar::currentChanged, mainLayout, &QStackedLayout::setCurrentIndex);
0105 }
0106 
0107 SubtitleMetaWidget::~SubtitleMetaWidget()
0108 {
0109 }
0110 
0111 static QString
0112 mergedComments(const Subtitle *subtitle, const QByteArray &prefix)
0113 {
0114     QString comments;
0115     for(int i = 0;; i++) {
0116         const QByteArray key = prefix + QByteArray::number(i);
0117         if(!subtitle->metaExists(key))
0118             break;
0119         comments.append(subtitle->meta(key));
0120     }
0121     return comments;
0122 }
0123 
0124 static void
0125 setComments(Subtitle *subtitle, const QByteArray &prefix, const QString &comments)
0126 {
0127     subtitle->meta(prefix + QByteArray("0"), comments);
0128     for(int i = 1; subtitle->metaRemove(prefix + QByteArray::number(i)); i++);
0129 }
0130 
0131 void
0132 SubtitleMetaWidget::setSubtitle(Subtitle *subtitle)
0133 {
0134     if(m_subtitle) {
0135         disconnect(m_commentIntroEdit, &KTextEdit::textChanged, this, nullptr);
0136         disconnect(m_commentTopEdit, &KTextEdit::textChanged, this, nullptr);
0137         disconnect(m_commentBottomEdit, &KTextEdit::textChanged, this, nullptr);
0138         disconnect(m_cssEdit->document(), &QTextDocument::contentsChanged, this, nullptr);
0139     }
0140 
0141     m_subtitle = subtitle;
0142 
0143     if(subtitle) {
0144         m_commentIntroEdit->setPlainText(mergedComments(m_subtitle.constData(), "comment.intro."));
0145         m_commentTopEdit->setPlainText(mergedComments(m_subtitle.constData(), "comment.top."));
0146         m_commentBottomEdit->setPlainText(mergedComments(m_subtitle.constData(), "comment.bottom."));
0147         m_cssEdit->setPlainText(m_subtitle->stylesheet()->unformattedCSS());
0148 
0149         connect(m_commentIntroEdit, &KTextEdit::textChanged, this, [&](){
0150             setComments(m_subtitle.data(), "comment.intro.", m_commentIntroEdit->toPlainText());
0151         });
0152         connect(m_commentTopEdit, &KTextEdit::textChanged, this, [&](){
0153             setComments(m_subtitle.data(), "comment.top.", m_commentTopEdit->toPlainText());
0154         });
0155         connect(m_commentBottomEdit, &KTextEdit::textChanged, this, [&](){
0156             setComments(m_subtitle.data(), "comment.bottom.", m_commentBottomEdit->toPlainText());
0157         });
0158         connect(m_cssEdit->document(), &QTextDocument::contentsChanged, this, [&](){
0159             m_subtitle->stylesheetEdit(m_cssEdit);
0160         });
0161     } else {
0162         m_cssEdit->clear();
0163         m_commentIntroEdit->clear();
0164         m_commentTopEdit->clear();
0165         m_commentBottomEdit->clear();
0166         m_subPosition->setCurrentLine(nullptr);
0167     }
0168 }
0169 
0170 static QAction *
0171 actionGet(const QKeySequence &key)
0172 {
0173     QAction *action = app()->action(ACT_UNDO);
0174     if(action->shortcuts().contains(key))
0175         return action;
0176     action = app()->action(ACT_REDO);
0177     if(action->shortcuts().contains(key))
0178         return action;
0179     return nullptr;
0180 }
0181 
0182 bool
0183 SubtitleMetaWidget::eventFilter(QObject *obj, QEvent *event)
0184 {
0185     // workaround to forward (mouse) events to parent QDockWidget, otherwise it's impossible to drag the dock
0186     if(obj == m_tabBar) {
0187         // grab the mouse so move events are sent
0188         if(event->type() == QEvent::MouseButtonPress)
0189             m_tabBar->grabMouse();
0190         else if(event->type() == QEvent::MouseButtonRelease && reinterpret_cast<QMouseEvent *>(event)->buttons() == 0)
0191             m_tabBar->releaseMouse();
0192         // make sure m_tabBar handles the event
0193         static_cast<QObject *>(m_tabBar)->event(event);
0194         // flag it as ignored so QDockWidget can handle it
0195         event->ignore();
0196         return true;
0197     } else if(event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress) {
0198         if(obj == m_cssEdit || obj == m_commentIntroEdit || obj == m_commentTopEdit || obj == m_commentBottomEdit) {
0199             const QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
0200             QAction *action = actionGet(keyEvent->modifiers() | keyEvent->key());
0201             if(action) {
0202                 if(event->type() == QEvent::KeyPress)
0203                     action->trigger();
0204                 event->accept();
0205                 return true;
0206             }
0207         }
0208     }
0209     return QObject::eventFilter(obj, event);
0210 }