Compare commits

...

4 Commits

Author SHA1 Message Date
Jean-Baptiste Mardelle
187947ea93 Fix pasting MD without timecode 2024-12-09 06:42:54 +01:00
Jean-Baptiste Mardelle
21b1ca17ae introduce header test 2024-12-08 17:37:38 +01:00
Jean-Baptiste Mardelle
3a41707866 Add bold button, save notes in markdown inside project file 2024-12-08 12:11:57 +01:00
Jean-Baptiste Mardelle
b9f6a2b29a Switch project notes to use Markdown, with a button to switch between normal editing and markdown code editing 2024-12-06 23:01:32 +01:00
5 changed files with 138 additions and 14 deletions

View File

@@ -15,6 +15,7 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QMenu>
#include <QMimeData>
#include <QMouseEvent>
#include <QTextDocumentFragment>
#include <QToolTip>
#include <QUuid>
@@ -22,6 +23,7 @@ NotesWidget::NotesWidget(QWidget *parent)
: QTextEdit(parent)
{
setMouseTracking(true);
// setAcceptRichText(true);
}
NotesWidget::~NotesWidget() = default;
@@ -215,6 +217,32 @@ void NotesWidget::createMarkers()
}
}
void NotesWidget::switchMarkDownEditing(bool enable)
{
if (enable) {
QString text = toMarkdown();
clear();
QTextCursor cursor = textCursor();
cursor.select(QTextCursor::Document);
cursor.setCharFormat(QTextCharFormat());
cursor.clearSelection();
setTextCursor(cursor);
// setAcceptRichText(false);
setPlainText(text);
} else {
QString text = toPlainText();
text.replace(QStringLiteral("\n\n\n"), QStringLiteral("\n<br/>\n\n"));
clear();
QTextCursor cursor = textCursor();
cursor.select(QTextCursor::Document);
cursor.setCharFormat(QTextCharFormat());
cursor.clearSelection();
setTextCursor(cursor);
// setAcceptRichText(true);
setMarkdown(text);
}
}
void NotesWidget::addProjectNote()
{
if (!textCursor().atBlockStart()) {
@@ -239,27 +267,88 @@ void NotesWidget::addTextNote(const QString &text)
void NotesWidget::insertFromMimeData(const QMimeData *source)
{
QString pastedText = source->text();
bool enforceHtml = false;
bool enforceMarkDown = pastedText.contains(QLatin1String("# "));
// Check for timecodes
QStringList words = pastedText.split(QLatin1Char(' '));
for (const QString &w : std::as_const(words)) {
if (w.size() > 4 && w.size() < 13 && w.count(QLatin1Char(':')) > 1) {
// This is probably a timecode
int frames = pCore->timecode().getFrameCount(w);
if (frames > 0) {
pastedText.replace(w, QStringLiteral("<a href=\"") + QString::number(frames) + QStringLiteral("\">") + w + QStringLiteral("</a> "));
enforceHtml = true;
if (frames == 0) {
// Check if that is a correct timecode like 00:00:00
QString simplified = w;
simplified.remove(QLatin1Char(':'));
simplified.remove(QLatin1Char('0'));
simplified.remove(QLatin1Char('.'));
if (simplified.simplified().isEmpty()) {
// Ok, we really have a zero timecode
pastedText.replace(w, QStringLiteral("[%1](%2)").arg(w).arg(QString::number(0)));
enforceMarkDown = true;
}
} else if (frames > 0) {
pastedText.replace(w, QStringLiteral("[%1](%2)").arg(w).arg(QString::number(frames)));
enforceMarkDown = true;
}
}
}
if (enforceHtml || Qt::mightBeRichText(pastedText)) {
pastedText.replace(QLatin1Char('\n'), QStringLiteral("<br/>"));
insertHtml(pastedText);
if (enforceMarkDown) {
textCursor().insertMarkdown(pastedText);
} else {
insertPlainText(pastedText);
}
}
void NotesWidget::insertMarkDown(const QString &md)
{
textCursor().insertMarkdown(md);
}
void NotesWidget::switchBoldText()
{
QTextCursor cur = textCursor();
int nCurPos = cur.position();
if (!cur.hasSelection()) {
cur.select(QTextCursor::WordUnderCursor);
} else {
int startPos = cur.selectionStart();
int endPos = cur.selectionEnd();
cur.setPosition(endPos, QTextCursor::MoveAnchor);
cur.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
cur.setPosition(startPos, QTextCursor::QTextCursor::KeepAnchor);
// Get current text style
cur.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
}
QTextCharFormat format;
if (cur.charFormat().font().bold()) {
format.setFontWeight(QFont::Normal);
} else {
format.setFontWeight(QFont::Bold);
}
cur.mergeCharFormat(format);
cur.setPosition(nCurPos);
setTextCursor(cur);
}
void NotesWidget::switchHeaderText()
{
QTextCursor cur = textCursor();
cur.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
cur.select(QTextCursor::LineUnderCursor);
QString currentText = cur.selection().toMarkdown();
if (currentText.startsWith(QStringLiteral("# "))) {
// Already a header, clear
cur.setCharFormat(QTextCharFormat());
currentText.remove(0, 2);
} else {
cur.setCharFormat(QTextCharFormat());
currentText.prepend(QStringLiteral("# "));
}
cur.removeSelectedText();
setTextCursor(cur);
insertMarkDown(currentText);
}
bool NotesWidget::event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {

View File

@@ -24,6 +24,9 @@ public:
* @param text the text
*/
void addTextNote(const QString &text);
void insertMarkDown(const QString &md);
void switchBoldText();
void switchHeaderText();
protected:
void mouseMoveEvent(QMouseEvent *e) override;
@@ -35,6 +38,7 @@ protected:
public Q_SLOTS:
void createMarkers();
void assignProjectNote();
void switchMarkDownEditing(bool enable);
private:
void createMarker(const QStringList &anchors);

View File

@@ -56,6 +56,24 @@ void NotesPlugin::setProject(KdenliveDoc *document)
xi18nc("@info:whatsthis", "Creates markers in the timeline from the selected timecodes (doesnt matter if other text is selected too)."));
connect(a, &QAction::triggered, m_widget, &NotesWidget::createMarkers);
m_tb->addAction(a);
m_tb->addSeparator();
a = new QAction(QIcon::fromTheme(QStringLiteral("format-text-bold")), i18n("Bold"));
a->setWhatsThis(xi18nc("@info:whatsthis", "Make selected text bold."));
connect(a, &QAction::triggered, m_widget, &NotesWidget::switchBoldText);
m_tb->addAction(a);
a = new QAction(QIcon::fromTheme(QStringLiteral("format-font-size-more")), i18n("Header"));
a->setWhatsThis(xi18nc("@info:whatsthis", "Switch header format"));
connect(a, &QAction::triggered, m_widget, &NotesWidget::switchHeaderText);
m_tb->addAction(a);
QWidget *empty = new QWidget();
empty->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_tb->addWidget(empty);
m_markDownEdit = new QAction(QIcon::fromTheme(QStringLiteral("format-text-code")), i18n("Edit Markdown source code"));
m_markDownEdit->setCheckable(true);
m_markDownEdit->setWhatsThis(xi18nc("@info:whatsthis", "Enable or disable markdown editing."));
connect(m_markDownEdit, &QAction::triggered, m_widget, &NotesWidget::switchMarkDownEditing);
m_tb->addAction(m_markDownEdit);
}
}
@@ -65,6 +83,16 @@ void NotesPlugin::showDock()
m_notesDock->raise();
}
void NotesPlugin::loadNotes(QString text)
{
m_widget->clear();
if (m_markDownEdit->isChecked()) {
m_markDownEdit->trigger();
}
text.replace(QStringLiteral("\n\n\n"), QStringLiteral("\n<br />\n\n"));
m_widget->setMarkdown(text);
}
void NotesPlugin::slotInsertTimecode()
{
if (pCore->monitorManager()->isActive(Kdenlive::ClipMonitor)) {
@@ -78,7 +106,7 @@ void NotesPlugin::slotInsertTimecode()
}
const QString clipName = pCore->bin()->getBinClipName(binId);
const QString uuid = pCore->projectItemModel()->getBinClipUuid(binId);
m_widget->insertHtml(QStringLiteral("<a href=\"%1#%2\">%3:%4</a> ").arg(uuid, QString::number(frames), clipName, position));
m_widget->insertMarkDown(QStringLiteral("[%3:%4](%1#%2)&nbsp;").arg(uuid, QString::number(frames), clipName, position));
} else {
int frames = pCore->monitorManager()->projectMonitor()->position();
QString position = pCore->timecode().getTimecodeFromFrames(frames);
@@ -86,11 +114,11 @@ void NotesPlugin::slotInsertTimecode()
const QUuid uuid = pCore->currentTimelineId();
if (currentTrackInfo.first != -1) {
// Insert timeline position with track reference
m_widget->insertHtml(
QStringLiteral("<a href=\"%1!%2?%3\">%4 %5</a> ")
m_widget->insertMarkDown(
QStringLiteral("[%4 %5](%1!%2?%3)&nbsp;")
.arg(uuid.toString(), QString::number(frames), QString::number(currentTrackInfo.first), currentTrackInfo.second, position));
} else {
m_widget->insertHtml(QStringLiteral("<a href=\"%1!%2\">%3</a> ").arg(uuid.toString(), QString::number(frames), position));
m_widget->insertMarkDown(QStringLiteral("[%3](%1!%2)&nbsp;").arg(uuid.toString(), QString::number(frames), position));
}
}
}
@@ -148,7 +176,7 @@ void NotesPlugin::slotReAssign(const QStringList &anchors, const QList<QPoint> &
void NotesPlugin::slotInsertText(const QString &text)
{
m_widget->insertHtml(text);
m_widget->insertMarkDown(text);
}
NotesWidget *NotesPlugin::widget()

View File

@@ -12,6 +12,7 @@ class KdenliveDoc;
class ProjectManager;
class QDockWidget;
class QToolBar;
class QAction;
/** @class NotesPlugin
@brief Handles connection of NotesWidget
@@ -27,6 +28,7 @@ public:
NotesWidget *widget();
void clear();
void showDock();
void loadNotes(QString text);
private Q_SLOTS:
void setProject(KdenliveDoc *document);
@@ -41,4 +43,5 @@ private:
NotesWidget *m_widget;
QDockWidget *m_notesDock;
QToolBar *m_tb;
QAction *m_markDownEdit;
};

View File

@@ -1280,7 +1280,7 @@ void ProjectManager::setDocumentNotes(QString &notes, QStringList deprecatedBinI
notes = notes.replace(pattern, replacement);
}
}
m_notesPlugin->widget()->setHtml(notes);
m_notesPlugin->loadNotes(notes);
}
}
@@ -1290,7 +1290,7 @@ QString ProjectManager::documentNotes() const
if (text.isEmpty()) {
return QString();
}
return m_notesPlugin->widget()->toHtml();
return m_notesPlugin->widget()->toMarkdown();
}
void ProjectManager::slotAddProjectNote()