- add markdown editor
This commit is contained in:
parent
90ddb7535a
commit
dec5587c38
8
client/qmarkdowntextedit/.gitignore
vendored
Normal file
8
client/qmarkdowntextedit/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
*.o
|
||||
*.log
|
||||
*.dSYM
|
||||
*.plist
|
||||
*.pro.user
|
||||
.directory
|
||||
build-*
|
||||
.idea
|
||||
66
client/qmarkdowntextedit/.travis.yml
Normal file
66
client/qmarkdowntextedit/.travis.yml
Normal file
@ -0,0 +1,66 @@
|
||||
language: cpp
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- master
|
||||
- testing
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- CONFIG=release
|
||||
#- CONFIG=debug
|
||||
|
||||
install:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||
lsb_release -a
|
||||
&& sudo apt-add-repository -y ppa:ubuntu-toolchain-r/test
|
||||
&& sudo apt-add-repository -y ppa:beineri/opt-qt591-trusty
|
||||
&& sudo apt-get -qq update
|
||||
&& sudo apt-get -qq install g++-4.8 libc6-i386 qt59tools qt59svg qt59quickcontrols qt59quickcontrols2 qt59webengine qt59script
|
||||
&& export CXX="g++-4.8"
|
||||
&& export CC="gcc-4.8"
|
||||
;
|
||||
else
|
||||
brew update > /dev/null
|
||||
&& brew install qt5
|
||||
&& chmod -R 755 /usr/local/opt/qt5/*
|
||||
;
|
||||
fi
|
||||
|
||||
script:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||
QTDIR="/opt/qt59"
|
||||
&& PATH="$QTDIR/bin:$PATH"
|
||||
&& qt59-env.sh
|
||||
;
|
||||
else
|
||||
QTDIR="/usr/local/opt/qt5"
|
||||
&& PATH="$QTDIR/bin:$PATH"
|
||||
&& LDFLAGS=-L$QTDIR/lib
|
||||
&& CPPFLAGS=-I$QTDIR/include
|
||||
;
|
||||
fi
|
||||
- qmake qmarkdowntextedit.pro CONFIG+=$CONFIG
|
||||
- make
|
||||
|
||||
notifications:
|
||||
recipients:
|
||||
- developer@bekerle.com
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: change
|
||||
irc:
|
||||
# https://docs.travis-ci.com/user/notifications/#IRC-notification
|
||||
channels:
|
||||
- "chat.freenode.net#qownnotes"
|
||||
template:
|
||||
- "[%{commit}] %{repository} (%{branch}): %{message} | Commit message: %{commit_message} | Changes: %{compare_url} | Build details: %{build_url}"
|
||||
on_success: always
|
||||
on_failure: always
|
||||
use_notice: true
|
||||
skip_join: true
|
||||
49
client/qmarkdowntextedit/CMakeLists.txt
Normal file
49
client/qmarkdowntextedit/CMakeLists.txt
Normal file
@ -0,0 +1,49 @@
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
project(qmarkdowntextedit)
|
||||
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
find_package( Qt5Core REQUIRED )
|
||||
find_package( Qt5Widgets REQUIRED )
|
||||
find_package( Qt5Gui REQUIRED )
|
||||
|
||||
qt5_wrap_ui(ui_qplaintexteditsearchwidget.h qplaintexteditsearchwidget.ui)
|
||||
|
||||
set(RESOURCE_FILES
|
||||
media.qrc
|
||||
)
|
||||
|
||||
qt5_add_resources(RESOURCE_ADDED ${RESOURCE_FILES})
|
||||
|
||||
set(SOURCE_FILES
|
||||
markdownhighlighter.cpp
|
||||
markdownhighlighter.h
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
mainwindow.ui
|
||||
qmarkdowntextedit.cpp
|
||||
qmarkdowntextedit.h
|
||||
qplaintexteditsearchwidget.ui
|
||||
qplaintexteditsearchwidget.cpp
|
||||
qplaintexteditsearchwidget.h
|
||||
)
|
||||
|
||||
add_executable(qmarkdowntextedit ${SOURCE_FILES} ${RESOURCE_ADDED})
|
||||
|
||||
include_directories(${Qt5Widgets_INCLUDES})
|
||||
|
||||
# We need add -DQT_WIDGETS_LIB when using QtWidgets in Qt 5.
|
||||
add_definitions(${Qt5Widgets_DEFINITIONS})
|
||||
|
||||
# Executables fail to build with Qt 5 in the default configuration
|
||||
# without -fPIE. We add that here.
|
||||
set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||
|
||||
# The Qt5Widgets_LIBRARIES variable also includes QtGui and QtCore
|
||||
target_link_libraries(qmarkdowntextedit Qt5::Widgets)
|
||||
8
client/qmarkdowntextedit/LICENSE
Normal file
8
client/qmarkdowntextedit/LICENSE
Normal file
@ -0,0 +1,8 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2014-2019 Patrizio Bekerle
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
33
client/qmarkdowntextedit/README.md
Normal file
33
client/qmarkdowntextedit/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# [QMarkdownTextEdit](https://github.com/pbek/qmarkdowntextedit)
|
||||
[](https://travis-ci.org/pbek/qmarkdowntextedit)
|
||||
[](https://ci.appveyor.com/project/pbek/qmarkdowntextedit)
|
||||
|
||||
QMarkdownTextEdit is a C++ Qt [QPlainTextEdit](http://doc.qt.io/qt-5/qtextplainedit.html) widget with [markdown](https://en.wikipedia.org/wiki/Markdown) highlighting and some other goodies.
|
||||
|
||||
## Features
|
||||
- markdown highlighting
|
||||
- clickable links with `Ctrl + Click`
|
||||
- block indent with `Tab` and `Shift + Tab`
|
||||
- duplicate text with `Ctrl + Alt + Down`
|
||||
- searching of text with `Ctrl + F`
|
||||
- jump between search results with `Up` and `Down`
|
||||
- close search field with `Escape`
|
||||
- replacing of text with `Ctrl + R`
|
||||
- you can also replace text with regular expressions or whole words
|
||||
- and much more...
|
||||
|
||||
## Screenshot
|
||||

|
||||
|
||||
## How to use this widget
|
||||
- include [qmarkdowntextedit.pri](https://github.com/pbek/qmarkdowntextedit/blob/develop/qmarkdowntextedit.pri)
|
||||
to your project like this `include (qmarkdowntextedit/qmarkdowntextedit.pri)`
|
||||
- add a normal `QPlainTextEdit` to your UI and promote it to `QMarkdownTextEdit` (base class `QPlainTextEdit`)
|
||||
|
||||
## References
|
||||
- [QOwnNotes - cross-platform open source plain-text file notepad](http://www.qownnotes.org)
|
||||
|
||||
## Disclaimer
|
||||
This SOFTWARE PRODUCT is provided by THE PROVIDER "as is" and "with all faults." THE PROVIDER makes no representations or warranties of any kind concerning the safety, suitability, lack of viruses, inaccuracies, typographical errors, or other harmful components of this SOFTWARE PRODUCT.
|
||||
|
||||
There are inherent dangers in the use of any software, and you are solely responsible for determining whether this SOFTWARE PRODUCT is compatible with your equipment and other software installed on your equipment. You are also solely responsible for the protection of your equipment and backup of your data, and THE PROVIDER will not be liable for any damages you may suffer in connection with using, modifying, or distributing this SOFTWARE PRODUCT.
|
||||
12
client/qmarkdowntextedit/appveyor.yml
Normal file
12
client/qmarkdowntextedit/appveyor.yml
Normal file
@ -0,0 +1,12 @@
|
||||
# AppVeyor build configuration
|
||||
# http://www.appveyor.com/docs/build-configuration
|
||||
os: unstable
|
||||
skip_tags: true
|
||||
|
||||
install:
|
||||
- set QTDIR=C:\Qt\5.10.1\mingw53_32
|
||||
- set PATH=%PATH%;%QTDIR%\bin;C:\MinGW\bin
|
||||
|
||||
build_script:
|
||||
- qmake qmarkdowntextedit.pro -r -spec win32-g++
|
||||
- mingw32-make
|
||||
41
client/qmarkdowntextedit/main.cpp
Normal file
41
client/qmarkdowntextedit/main.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
QString filename;
|
||||
if (argc > 1) {
|
||||
filename = argv[1];
|
||||
}
|
||||
if (!filename.isEmpty() && !QFileInfo(filename).isReadable()) {
|
||||
qWarning() << filename << "is not a readable file";
|
||||
return 1;
|
||||
}
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
if (!filename.isEmpty()) {
|
||||
w.loadFile(filename);
|
||||
}
|
||||
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
118
client/qmarkdowntextedit/mainwindow.cpp
Normal file
118
client/qmarkdowntextedit/mainwindow.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* mainwindow.cpp
|
||||
*
|
||||
* Example to show the QMarkdownTextEdit widget
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "qmarkdowntextedit.h"
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
QToolBar *toolBar = new QToolBar;
|
||||
addToolBar(toolBar);
|
||||
QAction *openAction = new QAction(QIcon::fromTheme("document-open"), tr("Open..."));
|
||||
openAction->setShortcut(QKeySequence::Open);
|
||||
connect(openAction, &QAction::triggered, this, &MainWindow::open);
|
||||
|
||||
QAction *saveAction = new QAction(QIcon::fromTheme("document-save"), tr("Save"));
|
||||
saveAction->setShortcut(QKeySequence::Save);
|
||||
QAction *saveAsAction = new QAction(QIcon::fromTheme("document-save-as"), tr("Save as..."));
|
||||
saveAsAction->setShortcut(QKeySequence::SaveAs);
|
||||
QAction *quitAction = new QAction(QIcon::fromTheme("view-close"), tr("Quit"));
|
||||
quitAction->setShortcut(QKeySequence::Quit);
|
||||
connect(quitAction, &QAction::triggered, this, &MainWindow::onQuit);
|
||||
|
||||
m_loadedContent = ui->textEdit->toPlainText();
|
||||
|
||||
toolBar->addActions({openAction, saveAction, saveAsAction, quitAction});
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::loadFile(const QString &filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Failed to open" << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
m_filename = filename;
|
||||
m_loadedContent = QString::fromLocal8Bit(file.readAll());
|
||||
ui->textEdit->setPlainText(m_loadedContent);
|
||||
}
|
||||
|
||||
void MainWindow::saveToFile(const QString &filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Failed to open" << filename;
|
||||
return;
|
||||
}
|
||||
|
||||
m_filename = filename;
|
||||
|
||||
m_loadedContent = ui->textEdit->toPlainText();
|
||||
file.write(m_loadedContent.toLocal8Bit());
|
||||
}
|
||||
|
||||
void MainWindow::open()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName();
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
loadFile(filename);
|
||||
}
|
||||
|
||||
void MainWindow::save()
|
||||
{
|
||||
if (!m_filename.isEmpty()) {
|
||||
saveAs();
|
||||
return;
|
||||
}
|
||||
|
||||
saveToFile(m_filename);
|
||||
}
|
||||
|
||||
void MainWindow::saveAs()
|
||||
{
|
||||
QString filename = QFileDialog::getSaveFileName();
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveToFile(filename);
|
||||
}
|
||||
|
||||
void MainWindow::onQuit()
|
||||
{
|
||||
if (ui->textEdit->toPlainText() != m_loadedContent) {
|
||||
if (QMessageBox::question(this, tr("Not saved"), tr("Document not saved, sure you want to quit?")) != QMessageBox::Yes) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
close();
|
||||
}
|
||||
44
client/qmarkdowntextedit/mainwindow.h
Normal file
44
client/qmarkdowntextedit/mainwindow.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
void loadFile(const QString &filename);
|
||||
void saveToFile(const QString &filename);
|
||||
|
||||
private slots:
|
||||
void open();
|
||||
void save();
|
||||
void saveAs();
|
||||
void onQuit();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
QString m_loadedContent;
|
||||
QString m_filename;
|
||||
};
|
||||
82
client/qmarkdowntextedit/mainwindow.ui
Normal file
82
client/qmarkdowntextedit/mainwindow.ui
Normal file
@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1070</width>
|
||||
<height>839</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">QMarkdownTextEdit</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QMarkdownTextEdit" name="textEdit">
|
||||
<property name="plainText">
|
||||
<string>QMarkdownTextEdit
|
||||
==============
|
||||
|
||||
*QMarkdownTextEdit* is a C++ Qt [QPlainTextEdit](http://doc.qt.io/qt-5/qplaintextedit.html) widget with **markdown highlighting** and some other goodies.
|
||||
|
||||
## Features
|
||||
|
||||
- markdown highlighting
|
||||
- clickable links with `Ctrl + Click`
|
||||
- block indent with `Tab` and `Shift + Tab`
|
||||
- duplicate text with `Ctrl + Alt + Down`
|
||||
- searching of text with `Ctrl + F`
|
||||
- jump between search results with `Up` and `Down`
|
||||
- close search field with `Escape`
|
||||
- and much more...
|
||||
|
||||
## References
|
||||
|
||||
- [QOwnNotes - cross-platform open source plain-text notepad](http://www.qownnotes.org)
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This SOFTWARE PRODUCT is provided by THE PROVIDER "as is" and "with all faults." THE PROVIDER makes no representations or warranties of any kind concerning the safety, suitability, lack of viruses, inaccuracies, typographical errors, or other harmful components of this SOFTWARE PRODUCT.
|
||||
|
||||
There are inherent dangers in the use of any software, and you are solely responsible for determining whether this SOFTWARE PRODUCT is compatible with your equipment and other software installed on your equipment. You are also solely responsible for the protection of your equipment and backup of your data, and THE PROVIDER will not be liable for any damages you may suffer in connection with using, modifying, or distributing this SOFTWARE PRODUCT.
|
||||
</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1070</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="mainToolBar">
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QMarkdownTextEdit</class>
|
||||
<extends>QPlainTextEdit</extends>
|
||||
<header location="global">qmarkdowntextedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
698
client/qmarkdowntextedit/markdownhighlighter.cpp
Normal file
698
client/qmarkdowntextedit/markdownhighlighter.cpp
Normal file
@ -0,0 +1,698 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* QPlainTextEdit markdown highlighter
|
||||
*/
|
||||
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include "markdownhighlighter.h"
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QRegularExpressionMatchIterator>
|
||||
|
||||
|
||||
/**
|
||||
* Markdown syntax highlighting
|
||||
*
|
||||
* markdown syntax:
|
||||
* http://daringfireball.net/projects/markdown/syntax
|
||||
*
|
||||
* @param parent
|
||||
* @return
|
||||
*/
|
||||
MarkdownHighlighter::MarkdownHighlighter(
|
||||
QTextDocument *parent, HighlightingOptions highlightingOptions)
|
||||
: QSyntaxHighlighter(parent) {
|
||||
_highlightingOptions = highlightingOptions;
|
||||
_timer = new QTimer(this);
|
||||
QObject::connect(_timer, SIGNAL(timeout()),
|
||||
this, SLOT(timerTick()));
|
||||
_timer->start(1000);
|
||||
|
||||
// initialize the highlighting rules
|
||||
initHighlightingRules();
|
||||
|
||||
// initialize the text formats
|
||||
initTextFormats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does jobs every second
|
||||
*/
|
||||
void MarkdownHighlighter::timerTick() {
|
||||
// qDebug() << "timerTick: " << this << ", " << this->parent()->parent()->parent()->objectName();
|
||||
|
||||
// re-highlight all dirty blocks
|
||||
reHighlightDirtyBlocks();
|
||||
|
||||
// emit a signal every second if there was some highlighting done
|
||||
if (_highlightingFinished) {
|
||||
_highlightingFinished = false;
|
||||
emit(highlightingFinished());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-highlights all dirty blocks
|
||||
*/
|
||||
void MarkdownHighlighter::reHighlightDirtyBlocks() {
|
||||
while (_dirtyTextBlocks.count() > 0) {
|
||||
QTextBlock block = _dirtyTextBlocks.at(0);
|
||||
rehighlightBlock(block);
|
||||
_dirtyTextBlocks.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the dirty blocks vector
|
||||
*/
|
||||
void MarkdownHighlighter::clearDirtyBlocks() {
|
||||
_dirtyTextBlocks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dirty block to the list if it doesn't already exist
|
||||
*
|
||||
* @param block
|
||||
*/
|
||||
void MarkdownHighlighter::addDirtyBlock(QTextBlock block) {
|
||||
if (!_dirtyTextBlocks.contains(block)) {
|
||||
_dirtyTextBlocks.append(block);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the highlighting rules
|
||||
*
|
||||
* regexp tester:
|
||||
* https://regex101.com
|
||||
*
|
||||
* other examples:
|
||||
* /usr/share/kde4/apps/katepart/syntax/markdown.xml
|
||||
*/
|
||||
void MarkdownHighlighter::initHighlightingRules() {
|
||||
// highlight the reference of reference links
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::MaskedSyntax);
|
||||
rule.pattern = QRegularExpression("^\\[.+?\\]: \\w+://.+$");
|
||||
_highlightingRulesPre.append(rule);
|
||||
}
|
||||
|
||||
// highlight unordered lists
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::List);
|
||||
rule.pattern = QRegularExpression("^\\s*[-*+]\\s");
|
||||
rule.useStateAsCurrentBlockState = true;
|
||||
_highlightingRulesPre.append(rule);
|
||||
|
||||
// highlight ordered lists
|
||||
rule.pattern = QRegularExpression("^\\s*\\d+\\.\\s");
|
||||
_highlightingRulesPre.append(rule);
|
||||
}
|
||||
|
||||
// highlight block quotes
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::BlockQuote);
|
||||
rule.pattern = QRegularExpression(
|
||||
_highlightingOptions.testFlag(
|
||||
HighlightingOption::FullyHighlightedBlockQuote) ?
|
||||
"^\\s*(>\\s*.+)" : "^\\s*(>\\s*)+");
|
||||
_highlightingRulesPre.append(rule);
|
||||
}
|
||||
|
||||
// highlight horizontal rulers
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::HorizontalRuler);
|
||||
rule.pattern = QRegularExpression("^([*\\-_]\\s?){3,}$");
|
||||
_highlightingRulesPre.append(rule);
|
||||
}
|
||||
|
||||
// highlight tables without starting |
|
||||
// we drop that for now, it's far too messy to deal with
|
||||
// rule = HighlightingRule();
|
||||
// rule.pattern = QRegularExpression("^.+? \\| .+? \\| .+$");
|
||||
// rule.state = HighlighterState::Table;
|
||||
// _highlightingRulesPre.append(rule);
|
||||
|
||||
/*
|
||||
* highlight italic
|
||||
* this goes before bold so that bold can overwrite italic
|
||||
*
|
||||
* text to test:
|
||||
* **bold** normal **bold**
|
||||
* *start of line* normal
|
||||
* normal *end of line*
|
||||
* * list item *italic*
|
||||
*/
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::Italic);
|
||||
// we don't allow a space after the starting * to prevent problems with
|
||||
// unordered lists starting with a *
|
||||
rule.pattern = QRegularExpression(
|
||||
"(?:^|[^\\*\\b])(?:\\*([^\\* ][^\\*]*?)\\*)(?:[^\\*\\b]|$)");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
rule.pattern = QRegularExpression("\\b_([^_]+)_\\b");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::Bold);
|
||||
// highlight bold
|
||||
rule.pattern = QRegularExpression("\\B\\*{2}(.+?)\\*{2}\\B");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
rule.pattern = QRegularExpression("\\b__(.+?)__\\b");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
// highlight urls
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::Link);
|
||||
|
||||
// highlight urls without any other markup
|
||||
rule.pattern = QRegularExpression("\\b\\w+?:\\/\\/[^\\s]+");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// rule.pattern = QRegularExpression("<(.+?:\\/\\/.+?)>");
|
||||
rule.pattern = QRegularExpression("<([^\\s`][^`]*?[^\\s`])>");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// highlight urls with title
|
||||
// rule.pattern = QRegularExpression("\\[(.+?)\\]\\(.+?://.+?\\)");
|
||||
// rule.pattern = QRegularExpression("\\[(.+?)\\]\\(.+\\)\\B");
|
||||
rule.pattern = QRegularExpression("\\[([^\\[\\]]+)\\]\\((\\S+|.+?)\\)\\B");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// highlight urls with empty title
|
||||
// rule.pattern = QRegularExpression("\\[\\]\\((.+?://.+?)\\)");
|
||||
rule.pattern = QRegularExpression("\\[\\]\\((.+?)\\)");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// highlight email links
|
||||
rule.pattern = QRegularExpression("<(.+?@.+?)>");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// highlight reference links
|
||||
rule.pattern = QRegularExpression("\\[(.+?)\\]\\s?\\[.+?\\]");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
// Images
|
||||
{
|
||||
// highlight images with text
|
||||
HighlightingRule rule(HighlighterState::Image);
|
||||
rule.pattern = QRegularExpression("!\\[(.+?)\\]\\(.+?\\)");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// highlight images without text
|
||||
rule.pattern = QRegularExpression("!\\[\\]\\((.+?)\\)");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
// highlight images links
|
||||
{
|
||||
// HighlightingRule rule;
|
||||
HighlightingRule rule(HighlighterState::Link);
|
||||
rule.pattern = QRegularExpression("\\[!\\[(.+?)\\]\\(.+?\\)\\]\\(.+?\\)");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// highlight images links without text
|
||||
rule.pattern = QRegularExpression("\\[!\\[\\]\\(.+?\\)\\]\\((.+?)\\)");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
// highlight inline code
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::InlineCodeBlock);
|
||||
// HighlightingRule rule;
|
||||
rule.pattern = QRegularExpression("`(.+?)`");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
// highlight code blocks with four spaces or tabs in front of them
|
||||
// and no list character after that
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::CodeBlock);
|
||||
// HighlightingRule rule;
|
||||
rule.pattern = QRegularExpression("^((\\t)|( {4,})).+$");
|
||||
rule.disableIfCurrentStateIsSet = true;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
// highlight inline comments
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::Comment);
|
||||
rule.pattern = QRegularExpression("<!\\-\\-(.+?)\\-\\->");
|
||||
rule.capturingGroup = 1;
|
||||
_highlightingRulesAfter.append(rule);
|
||||
|
||||
// highlight comments for Rmarkdown for academic papers
|
||||
rule.pattern = QRegularExpression("^\\[.+?\\]: # \\(.+?\\)$");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
|
||||
// highlight tables with starting |
|
||||
{
|
||||
HighlightingRule rule(HighlighterState::Table);
|
||||
rule.pattern = QRegularExpression("^\\|.+?\\|$");
|
||||
_highlightingRulesAfter.append(rule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the text formats
|
||||
*
|
||||
* @param defaultFontSize
|
||||
*/
|
||||
void MarkdownHighlighter::initTextFormats(int defaultFontSize) {
|
||||
QTextCharFormat format;
|
||||
|
||||
// set character formats for headlines
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush(QColor(0, 49, 110)));
|
||||
format.setFontWeight(QFont::Bold);
|
||||
format.setFontPointSize(defaultFontSize * 1.6);
|
||||
_formats[H1] = format;
|
||||
format.setFontPointSize(defaultFontSize * 1.5);
|
||||
_formats[H2] = format;
|
||||
format.setFontPointSize(defaultFontSize * 1.4);
|
||||
_formats[H3] = format;
|
||||
format.setFontPointSize(defaultFontSize * 1.3);
|
||||
_formats[H4] = format;
|
||||
format.setFontPointSize(defaultFontSize * 1.2);
|
||||
_formats[H5] = format;
|
||||
format.setFontPointSize(defaultFontSize * 1.1);
|
||||
_formats[H6] = format;
|
||||
format.setFontPointSize(defaultFontSize);
|
||||
|
||||
// set character format for horizontal rulers
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush(Qt::darkGray));
|
||||
format.setBackground(QBrush(Qt::lightGray));
|
||||
_formats[HorizontalRuler] = format;
|
||||
|
||||
// set character format for lists
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush(QColor(163, 0, 123)));
|
||||
_formats[List] = format;
|
||||
|
||||
// set character format for links
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush(QColor(0, 128, 255)));
|
||||
format.setFontUnderline(true);
|
||||
_formats[Link] = format;
|
||||
|
||||
// set character format for images
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush(QColor(0, 191, 0)));
|
||||
format.setBackground(QBrush(QColor(228, 255, 228)));
|
||||
_formats[Image] = format;
|
||||
|
||||
// set character format for code blocks
|
||||
format = QTextCharFormat();
|
||||
format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
format.setBackground(QColor(220, 220, 220));
|
||||
_formats[CodeBlock] = format;
|
||||
_formats[InlineCodeBlock] = format;
|
||||
|
||||
// set character format for italic
|
||||
format = QTextCharFormat();
|
||||
format.setFontWeight(QFont::StyleItalic);
|
||||
format.setFontItalic(true);
|
||||
_formats[Italic] = format;
|
||||
|
||||
// set character format for bold
|
||||
format = QTextCharFormat();
|
||||
format.setFontWeight(QFont::Bold);
|
||||
_formats[Bold] = format;
|
||||
|
||||
// set character format for comments
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush(Qt::gray));
|
||||
_formats[Comment] = format;
|
||||
|
||||
// set character format for masked syntax
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush("#cccccc"));
|
||||
_formats[MaskedSyntax] = format;
|
||||
|
||||
// set character format for tables
|
||||
format = QTextCharFormat();
|
||||
format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
format.setForeground(QBrush(QColor("#649449")));
|
||||
_formats[Table] = format;
|
||||
|
||||
// set character format for block quotes
|
||||
format = QTextCharFormat();
|
||||
format.setForeground(QBrush(QColor(Qt::darkRed)));
|
||||
_formats[BlockQuote] = format;
|
||||
|
||||
format = QTextCharFormat();
|
||||
_formats[HeadlineEnd] = format;
|
||||
|
||||
format = QTextCharFormat();
|
||||
_formats[NoState] = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text formats
|
||||
*
|
||||
* @param formats
|
||||
*/
|
||||
void MarkdownHighlighter::setTextFormats(
|
||||
QHash<HighlighterState, QTextCharFormat> formats) {
|
||||
_formats = formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a text format
|
||||
*
|
||||
* @param formats
|
||||
*/
|
||||
void MarkdownHighlighter::setTextFormat(HighlighterState state,
|
||||
QTextCharFormat format) {
|
||||
_formats[state] = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the markdown highlighting
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
void MarkdownHighlighter::highlightBlock(const QString &text) {
|
||||
setCurrentBlockState(HighlighterState::NoState);
|
||||
currentBlock().setUserState(HighlighterState::NoState);
|
||||
highlightMarkdown(text);
|
||||
_highlightingFinished = true;
|
||||
}
|
||||
|
||||
void MarkdownHighlighter::highlightMarkdown(QString text) {
|
||||
if (!text.isEmpty()) {
|
||||
highlightAdditionalRules(_highlightingRulesPre, text);
|
||||
|
||||
// needs to be called after the horizontal ruler highlighting
|
||||
highlightHeadline(text);
|
||||
|
||||
highlightAdditionalRules(_highlightingRulesAfter, text);
|
||||
}
|
||||
|
||||
highlightCommentBlock(text);
|
||||
highlightCodeBlock(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight headlines
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
void MarkdownHighlighter::highlightHeadline(QString text) {
|
||||
QRegularExpression regex("^(#+)\\s+(.+?)$");
|
||||
QRegularExpressionMatch match = regex.match(text);
|
||||
QTextCharFormat &maskedFormat = _formats[HighlighterState::MaskedSyntax];
|
||||
|
||||
// check for headline blocks with # in front of them
|
||||
if (match.hasMatch()) {
|
||||
int count = match.captured(1).count();
|
||||
|
||||
// we just have H1 to H6
|
||||
count = qMin(count, 6);
|
||||
|
||||
HighlighterState state = HighlighterState(
|
||||
HighlighterState::H1 + count - 1);
|
||||
|
||||
QTextCharFormat &format = _formats[state];
|
||||
QTextCharFormat currentMaskedFormat = maskedFormat;
|
||||
|
||||
// set the font size from the current rule's font format
|
||||
currentMaskedFormat.setFontPointSize(format.fontPointSize());
|
||||
|
||||
// first highlight everything as MaskedSyntax
|
||||
setFormat(match.capturedStart(), match.capturedLength(),
|
||||
currentMaskedFormat);
|
||||
|
||||
// then highlight with the real format
|
||||
setFormat(match.capturedStart(2), match.capturedLength(2),
|
||||
_formats[state]);
|
||||
|
||||
// set a margin for the current block
|
||||
setCurrentBlockMargin(state);
|
||||
|
||||
setCurrentBlockState(state);
|
||||
currentBlock().setUserState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
// take care of ==== and ---- headlines
|
||||
QRegularExpression patternH1 = QRegularExpression("^=+$");
|
||||
QRegularExpression patternH2 = QRegularExpression("^-+$");
|
||||
QTextBlock previousBlock = currentBlock().previous();
|
||||
QString previousText = previousBlock.text();
|
||||
previousText.trimmed().remove(QRegularExpression("[=-]"));
|
||||
|
||||
// check for ===== after a headline text and highlight as H1
|
||||
if (patternH1.match(text).hasMatch()) {
|
||||
if (((previousBlockState() == HighlighterState::H1) ||
|
||||
(previousBlockState() == HighlighterState::NoState)) &&
|
||||
(previousText.length() > 0)) {
|
||||
// set the font size from the current rule's font format
|
||||
QTextCharFormat currentMaskedFormat = maskedFormat;
|
||||
currentMaskedFormat.setFontPointSize(
|
||||
_formats[HighlighterState::H1].fontPointSize());
|
||||
|
||||
setFormat(0, text.length(), currentMaskedFormat);
|
||||
setCurrentBlockState(HighlighterState::HeadlineEnd);
|
||||
previousBlock.setUserState(HighlighterState::H1);
|
||||
|
||||
// set a margin for the current block
|
||||
setCurrentBlockMargin(HighlighterState::H1);
|
||||
|
||||
// we want to re-highlight the previous block
|
||||
// this must not done directly, but with a queue, otherwise it
|
||||
// will crash
|
||||
// setting the character format of the previous text, because this
|
||||
// causes text to be formatted the same way when writing after
|
||||
// the text
|
||||
addDirtyBlock(previousBlock);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check for ----- after a headline text and highlight as H2
|
||||
if (patternH2.match(text).hasMatch()) {
|
||||
if (((previousBlockState() == HighlighterState::H2) ||
|
||||
(previousBlockState() == HighlighterState::NoState)) &&
|
||||
(previousText.length() > 0)) {
|
||||
// set the font size from the current rule's font format
|
||||
QTextCharFormat currentMaskedFormat = maskedFormat;
|
||||
currentMaskedFormat.setFontPointSize(
|
||||
_formats[HighlighterState::H2].fontPointSize());
|
||||
|
||||
setFormat(0, text.length(), currentMaskedFormat);
|
||||
setCurrentBlockState(HighlighterState::HeadlineEnd);
|
||||
previousBlock.setUserState(HighlighterState::H2);
|
||||
|
||||
// set a margin for the current block
|
||||
setCurrentBlockMargin(HighlighterState::H2);
|
||||
|
||||
// we want to re-highlight the previous block
|
||||
addDirtyBlock(previousBlock);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QTextBlock nextBlock = currentBlock().next();
|
||||
QString nextBlockText = nextBlock.text();
|
||||
|
||||
// highlight as H1 if next block is =====
|
||||
if (patternH1.match(nextBlockText).hasMatch() ||
|
||||
patternH2.match(nextBlockText).hasMatch()) {
|
||||
setFormat(0, text.length(), _formats[HighlighterState::H1]);
|
||||
setCurrentBlockState(HighlighterState::H1);
|
||||
currentBlock().setUserState(HighlighterState::H1);
|
||||
}
|
||||
|
||||
// highlight as H2 if next block is -----
|
||||
if (patternH2.match(nextBlockText).hasMatch()) {
|
||||
setFormat(0, text.length(), _formats[HighlighterState::H2]);
|
||||
setCurrentBlockState(HighlighterState::H2);
|
||||
currentBlock().setUserState(HighlighterState::H2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a margin for the current block
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
void MarkdownHighlighter::setCurrentBlockMargin(
|
||||
MarkdownHighlighter::HighlighterState state) {
|
||||
// this is currently disabled because it causes multiple problems:
|
||||
// - it prevents "undo" in headlines
|
||||
// https://github.com/pbek/QOwnNotes/issues/520
|
||||
// - invisible lines at the end of a note
|
||||
// https://github.com/pbek/QOwnNotes/issues/667
|
||||
// - a crash when reaching the invisible lines when the current line is
|
||||
// highlighted
|
||||
// https://github.com/pbek/QOwnNotes/issues/701
|
||||
return;
|
||||
|
||||
qreal margin;
|
||||
|
||||
switch (state) {
|
||||
case HighlighterState::H1:
|
||||
margin = 5;
|
||||
break;
|
||||
case HighlighterState::H2:
|
||||
case HighlighterState::H3:
|
||||
case HighlighterState::H4:
|
||||
case HighlighterState::H5:
|
||||
case HighlighterState::H6:
|
||||
margin = 3;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
QTextBlockFormat blockFormat = currentBlock().blockFormat();
|
||||
blockFormat.setTopMargin(2);
|
||||
blockFormat.setBottomMargin(margin);
|
||||
|
||||
// this prevents "undo" in headlines!
|
||||
QTextCursor* myCursor = new QTextCursor(currentBlock());
|
||||
myCursor->setBlockFormat(blockFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight multi-line code blocks
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
void MarkdownHighlighter::highlightCodeBlock(QString text) {
|
||||
QRegularExpression regex("^```\\w*?$");
|
||||
QRegularExpressionMatch match = regex.match(text);
|
||||
|
||||
if (match.hasMatch()) {
|
||||
setCurrentBlockState(
|
||||
previousBlockState() == HighlighterState::CodeBlock ?
|
||||
HighlighterState::CodeBlockEnd : HighlighterState::CodeBlock);
|
||||
// set the font size from the current rule's font format
|
||||
QTextCharFormat &maskedFormat =
|
||||
_formats[HighlighterState::MaskedSyntax];
|
||||
maskedFormat.setFontPointSize(
|
||||
_formats[HighlighterState::CodeBlock].fontPointSize());
|
||||
|
||||
setFormat(0, text.length(), maskedFormat);
|
||||
} else if (previousBlockState() == HighlighterState::CodeBlock) {
|
||||
setCurrentBlockState(HighlighterState::CodeBlock);
|
||||
setFormat(0, text.length(), _formats[HighlighterState::CodeBlock]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight multi-line comments
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
void MarkdownHighlighter::highlightCommentBlock(QString text) {
|
||||
bool highlight = false;
|
||||
text = text.trimmed();
|
||||
QString startText = "<!--";
|
||||
QString endText = "-->";
|
||||
|
||||
// we will skip this case because that is an inline comment and causes
|
||||
// troubles here
|
||||
if (text.startsWith(startText) && text.contains(endText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.startsWith(startText) ||
|
||||
(!text.endsWith(endText) &&
|
||||
(previousBlockState() == HighlighterState::Comment))) {
|
||||
setCurrentBlockState(HighlighterState::Comment);
|
||||
highlight = true;
|
||||
} else if (text.endsWith(endText)) {
|
||||
highlight = true;
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
setFormat(0, text.length(), _formats[HighlighterState::Comment]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights the rules from the _highlightingRules list
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
void MarkdownHighlighter::highlightAdditionalRules(
|
||||
QVector<HighlightingRule> &rules, QString text) {
|
||||
QTextCharFormat &maskedFormat = _formats[HighlighterState::MaskedSyntax];
|
||||
|
||||
for(const HighlightingRule &rule : rules) {
|
||||
// continue if an other current block state was already set if
|
||||
// disableIfCurrentStateIsSet is set
|
||||
if (rule.disableIfCurrentStateIsSet &&
|
||||
(currentBlockState() != HighlighterState::NoState)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QRegularExpression expression(rule.pattern);
|
||||
QRegularExpressionMatchIterator iterator = expression.globalMatch(text);
|
||||
int capturingGroup = rule.capturingGroup;
|
||||
int maskedGroup = rule.maskedGroup;
|
||||
QTextCharFormat &format = _formats[rule.state];
|
||||
|
||||
// store the current block state if useStateAsCurrentBlockState
|
||||
// is set
|
||||
if (iterator.hasNext() && rule.useStateAsCurrentBlockState) {
|
||||
setCurrentBlockState(rule.state);
|
||||
}
|
||||
|
||||
// find and format all occurrences
|
||||
while (iterator.hasNext()) {
|
||||
QRegularExpressionMatch match = iterator.next();
|
||||
|
||||
// if there is a capturingGroup set then first highlight
|
||||
// everything as MaskedSyntax and highlight capturingGroup
|
||||
// with the real format
|
||||
if (capturingGroup > 0) {
|
||||
QTextCharFormat currentMaskedFormat = maskedFormat;
|
||||
// set the font size from the current rule's font format
|
||||
if (format.fontPointSize() > 0) {
|
||||
currentMaskedFormat.setFontPointSize(format.fontPointSize());
|
||||
}
|
||||
|
||||
setFormat(match.capturedStart(maskedGroup),
|
||||
match.capturedLength(maskedGroup),
|
||||
currentMaskedFormat);
|
||||
}
|
||||
|
||||
setFormat(match.capturedStart(capturingGroup),
|
||||
match.capturedLength(capturingGroup),
|
||||
format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MarkdownHighlighter::setHighlightingOptions(HighlightingOptions options) {
|
||||
_highlightingOptions = options;
|
||||
}
|
||||
137
client/qmarkdowntextedit/markdownhighlighter.h
Normal file
137
client/qmarkdowntextedit/markdownhighlighter.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* QPlainTextEdit markdown highlighter
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QTextCharFormat>
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QRegularExpression>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextDocument;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MarkdownHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum HighlightingOption{
|
||||
None = 0,
|
||||
FullyHighlightedBlockQuote = 0x01
|
||||
};
|
||||
Q_DECLARE_FLAGS(HighlightingOptions, HighlightingOption)
|
||||
|
||||
MarkdownHighlighter(QTextDocument *parent = nullptr,
|
||||
HighlightingOptions highlightingOptions =
|
||||
HighlightingOption::None);
|
||||
|
||||
// we use some predefined numbers here to be compatible with
|
||||
// the peg-markdown parser
|
||||
enum HighlighterState {
|
||||
NoState = -1,
|
||||
Link = 0,
|
||||
Image = 3,
|
||||
CodeBlock,
|
||||
Italic = 7,
|
||||
Bold,
|
||||
List,
|
||||
Comment = 11,
|
||||
H1,
|
||||
H2,
|
||||
H3,
|
||||
H4,
|
||||
H5,
|
||||
H6,
|
||||
BlockQuote,
|
||||
HorizontalRuler = 21,
|
||||
Table,
|
||||
InlineCodeBlock,
|
||||
MaskedSyntax,
|
||||
CurrentLineBackgroundColor,
|
||||
BrokenLink,
|
||||
|
||||
// internal
|
||||
CodeBlockEnd = 100,
|
||||
HeadlineEnd
|
||||
};
|
||||
Q_ENUM(HighlighterState)
|
||||
|
||||
// enum BlockState {
|
||||
// NoBlockState = 0,
|
||||
// H1,
|
||||
// H2,
|
||||
// H3,
|
||||
// Table,
|
||||
// CodeBlock,
|
||||
// CodeBlockEnd
|
||||
// };
|
||||
|
||||
void setTextFormats(QHash<HighlighterState, QTextCharFormat> formats);
|
||||
void setTextFormat(HighlighterState state, QTextCharFormat format);
|
||||
void clearDirtyBlocks();
|
||||
void setHighlightingOptions(HighlightingOptions options);
|
||||
void initHighlightingRules();
|
||||
|
||||
signals:
|
||||
void highlightingFinished();
|
||||
|
||||
protected slots:
|
||||
void timerTick();
|
||||
|
||||
protected:
|
||||
struct HighlightingRule {
|
||||
HighlightingRule(const HighlighterState state_) : state(state_) {}
|
||||
HighlightingRule() = default;
|
||||
|
||||
QRegularExpression pattern;
|
||||
HighlighterState state = NoState;
|
||||
int capturingGroup = 0;
|
||||
int maskedGroup = 0;
|
||||
bool useStateAsCurrentBlockState = false;
|
||||
bool disableIfCurrentStateIsSet = false;
|
||||
};
|
||||
|
||||
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
|
||||
|
||||
void initTextFormats(int defaultFontSize = 12);
|
||||
|
||||
void highlightMarkdown(QString text);
|
||||
|
||||
void highlightHeadline(QString text);
|
||||
|
||||
void highlightAdditionalRules(QVector<HighlightingRule> &rules,
|
||||
QString text);
|
||||
|
||||
void highlightCodeBlock(QString text);
|
||||
|
||||
void highlightCommentBlock(QString text);
|
||||
|
||||
void addDirtyBlock(QTextBlock block);
|
||||
|
||||
void reHighlightDirtyBlocks();
|
||||
|
||||
QVector<HighlightingRule> _highlightingRulesPre;
|
||||
QVector<HighlightingRule> _highlightingRulesAfter;
|
||||
QVector<QTextBlock> _dirtyTextBlocks;
|
||||
QHash<HighlighterState, QTextCharFormat> _formats;
|
||||
QTimer *_timer;
|
||||
bool _highlightingFinished;
|
||||
HighlightingOptions _highlightingOptions;
|
||||
|
||||
void setCurrentBlockMargin(HighlighterState state);
|
||||
};
|
||||
9
client/qmarkdowntextedit/media.qrc
Normal file
9
client/qmarkdowntextedit/media.qrc
Normal file
@ -0,0 +1,9 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>media/window-close.svg</file>
|
||||
<file>media/go-top.svg</file>
|
||||
<file>media/go-bottom.svg</file>
|
||||
<file>media/edit-find-replace.svg</file>
|
||||
<file>media/format-text-superscript.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
15
client/qmarkdowntextedit/media/edit-find-replace.svg
Normal file
15
client/qmarkdowntextedit/media/edit-find-replace.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#4d4d4d;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
transform="translate(1,1)"
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 9 3 C 5.6759952 3 3 5.6759952 3 9 C 3 12.324005 5.6759952 15 9 15 C 10.481205 15 11.830584 14.465318 12.875 13.582031 L 18.292969 19 L 19 18.292969 L 13.582031 12.875 C 14.465318 11.830584 15 10.481205 15 9 C 15 5.6759952 12.324005 3 9 3 z M 9 4 C 11.770005 4 14 6.2299952 14 9 C 14 11.770005 11.770005 14 9 14 C 6.2299952 14 4 11.770005 4 9 C 4 6.2299952 6.2299952 4 9 4 z M 4 17 C 3.4460048 17 3 17.446005 3 18 C 3 18.553995 3.4460048 19 4 19 C 4.5539952 19 5 18.553995 5 18 C 5 17.446005 4.5539952 17 4 17 z M 8 17 C 7.4460048 17 7 17.446005 7 18 C 7 18.553995 7.4460048 19 8 19 C 8.5539952 19 9 18.553995 9 18 C 9 17.446005 8.5539952 17 8 17 z M 12 17 C 11.446005 17 11 17.446005 11 18 C 11 18.553995 11.446005 19 12 19 C 12.553995 19 13 18.553995 13 18 C 13 17.446005 12.553995 17 12 17 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
16
client/qmarkdowntextedit/media/format-text-superscript.svg
Normal file
16
client/qmarkdowntextedit/media/format-text-superscript.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#4d4d4d;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<a transform="translate(1,1)">
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 14 3 L 14 5 L 14 6 L 14 8 L 14 9 L 15 9 L 17 9 C 18.108 9 19 8.108 19 7 C 19 6.1891 18.518727 5.4980937 17.828125 5.1835938 C 17.934063 4.9776938 18 4.7483 18 4.5 C 18 3.669 17.330999 3 16.5 3 L 15 3 L 14 3 z M 15 4 L 16.5 4 C 16.777 4 17 4.223 17 4.5 C 17 4.777 16.777 5 16.5 5 L 15 5 L 15 4 z M 8.2167969 5 L 3 19 L 4.6875 19 L 6.3554688 14.570312 L 11.917969 14.570312 L 13.451172 19 L 15.140625 19 L 10.210938 5 L 8.2167969 5 z M 15 6 L 16.5 6 L 17 6 C 17.553999 6 18 6.446 18 7 C 18 7.554 17.553999 8 17 8 L 15 8 L 15 6 z M 9.2324219 6.6113281 L 11.361328 13.285156 L 6.8730469 13.285156 L 9.2324219 6.6113281 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</a>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 998 B |
156
client/qmarkdowntextedit/media/go-bottom.svg
Normal file
156
client/qmarkdowntextedit/media/go-bottom.svg
Normal file
@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg3869"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="go-bottom.svg">
|
||||
<defs
|
||||
id="defs3871">
|
||||
<linearGradient
|
||||
id="linearGradient3257">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#a50000;stop-opacity:1"
|
||||
id="stop3259" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#e73800;stop-opacity:1"
|
||||
id="stop3261" />
|
||||
</linearGradient>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4210">
|
||||
<rect
|
||||
y="1024.3622"
|
||||
x="-7"
|
||||
height="34"
|
||||
width="34"
|
||||
id="rect4212"
|
||||
style="opacity:1;fill:#0000ff;fill-opacity:0.51376145;stroke:none;stroke-opacity:1" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4160">
|
||||
<rect
|
||||
style="opacity:1;fill:#aade87;fill-opacity:0.47247709;stroke:none;stroke-opacity:1"
|
||||
id="rect4162"
|
||||
width="32"
|
||||
height="32.000015"
|
||||
x="-6"
|
||||
y="1028.3619" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627416"
|
||||
inkscape:cx="5.4926209"
|
||||
inkscape:cy="10.264796"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="709"
|
||||
inkscape:window-x="-4"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4132" />
|
||||
<sodipodi:guide
|
||||
position="4,18"
|
||||
orientation="18,0"
|
||||
id="guide4138" />
|
||||
<sodipodi:guide
|
||||
position="5,3"
|
||||
orientation="0,18"
|
||||
id="guide4140" />
|
||||
<sodipodi:guide
|
||||
position="20,2.0000174"
|
||||
orientation="-18,0"
|
||||
id="guide4142" />
|
||||
<sodipodi:guide
|
||||
position="2,21"
|
||||
orientation="0,-18"
|
||||
id="guide4144" />
|
||||
<sodipodi:guide
|
||||
position="3,19.000017"
|
||||
orientation="16,0"
|
||||
id="guide4146" />
|
||||
<sodipodi:guide
|
||||
position="2,4"
|
||||
orientation="0,16"
|
||||
id="guide4148" />
|
||||
<sodipodi:guide
|
||||
position="21,20"
|
||||
orientation="-16,0"
|
||||
id="guide4150" />
|
||||
<sodipodi:guide
|
||||
position="2,20"
|
||||
orientation="0,-16"
|
||||
id="guide4152" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-326,-532.3622)">
|
||||
<g
|
||||
transform="translate(327,507.3622)"
|
||||
inkscape:label="Layer 1"
|
||||
id="layer1-68">
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
id="layer1-6"
|
||||
transform="translate(5e-7,-1006.3622)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#4d4d4d;fill-opacity:1;stroke:none"
|
||||
d="M 3.7070312,7 3,7.7070312 l 6.125,6.1249998 1.875,1.875 1.875,-1.875 L 19,7.7070312 18.292969,7 12.167969,13.125 11,14.292969 9.8320312,13.125 3.7070312,7 Z"
|
||||
transform="translate(-5e-7,1030.3622)"
|
||||
id="rect4176" />
|
||||
</g>
|
||||
<rect
|
||||
y="42"
|
||||
x="3"
|
||||
height="1"
|
||||
width="16"
|
||||
id="rect4186"
|
||||
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
476
client/qmarkdowntextedit/media/go-top.svg
Normal file
476
client/qmarkdowntextedit/media/go-top.svg
Normal file
@ -0,0 +1,476 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg3869"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="go-top.svg">
|
||||
<defs
|
||||
id="defs3871">
|
||||
<linearGradient
|
||||
id="linearGradient3257">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#a50000;stop-opacity:1"
|
||||
id="stop3259" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#e73800;stop-opacity:1"
|
||||
id="stop3261" />
|
||||
</linearGradient>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4210">
|
||||
<rect
|
||||
y="1024.3622"
|
||||
x="-7"
|
||||
height="34"
|
||||
width="34"
|
||||
id="rect4212"
|
||||
style="opacity:1;fill:#0000ff;fill-opacity:0.51376145;stroke:none;stroke-opacity:1" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4160">
|
||||
<rect
|
||||
style="opacity:1;fill:#aade87;fill-opacity:0.47247709;stroke:none;stroke-opacity:1"
|
||||
id="rect4162"
|
||||
width="32"
|
||||
height="32.000015"
|
||||
x="-6"
|
||||
y="1028.3619" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clipPath4160-4"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<rect
|
||||
y="1023.3622"
|
||||
x="7"
|
||||
height="1"
|
||||
width="1"
|
||||
id="rect4162-5"
|
||||
style="opacity:1;fill:#f2f2f2;fill-opacity:1;stroke:none;stroke-opacity:1" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath16">
|
||||
<path
|
||||
d="m 0,706.465 1490.926,0 L 1490.926,0 0,0 0,706.465 Z"
|
||||
id="path18" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath24">
|
||||
<path
|
||||
d="m 22.1953,686.117 1447.7347,0 0,-667.1902 -1447.7347,0 L 22.1953,686.117 Z"
|
||||
id="path26" />
|
||||
</clipPath>
|
||||
<inkscape:perspective
|
||||
id="perspective4146-36"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-7" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-0"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-8" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-3"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-36-7"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-7-6" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-0-6"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-8-7" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-3-9"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-2" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-36-4"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-7-7" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-0-0"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-8-6" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-3-81"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-6" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-36-7-2"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-7-6-3" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-0-6-4"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-8-7-6" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-3-9-0"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-2-1" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-36-8"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-7-0" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-0-3"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-8-78" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-3-1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-05" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-36-3"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-7-8" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-0-9"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-8-3" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-3-4"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-9" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-36-7-6"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-7-6-0" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-0-6-7"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-3-8-7-7" />
|
||||
<inkscape:perspective
|
||||
id="perspective4146-3-9-7"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective4146-2-13" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627416"
|
||||
inkscape:cx="5.4926209"
|
||||
inkscape:cy="10.264796"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="709"
|
||||
inkscape:window-x="-4"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4132" />
|
||||
<sodipodi:guide
|
||||
position="4,18"
|
||||
orientation="18,0"
|
||||
id="guide4138" />
|
||||
<sodipodi:guide
|
||||
position="5,3"
|
||||
orientation="0,18"
|
||||
id="guide4140" />
|
||||
<sodipodi:guide
|
||||
position="20,2.0000174"
|
||||
orientation="-18,0"
|
||||
id="guide4142" />
|
||||
<sodipodi:guide
|
||||
position="2,21"
|
||||
orientation="0,-18"
|
||||
id="guide4144" />
|
||||
<sodipodi:guide
|
||||
position="3,19.000017"
|
||||
orientation="16,0"
|
||||
id="guide4146" />
|
||||
<sodipodi:guide
|
||||
position="2,4"
|
||||
orientation="0,16"
|
||||
id="guide4148" />
|
||||
<sodipodi:guide
|
||||
position="21,20"
|
||||
orientation="-16,0"
|
||||
id="guide4150" />
|
||||
<sodipodi:guide
|
||||
position="2,20"
|
||||
orientation="0,-16"
|
||||
id="guide4152" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-326,-532.3622)">
|
||||
<g
|
||||
transform="translate(327,507.3622)"
|
||||
inkscape:label="Layer 1"
|
||||
id="layer1-2">
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
id="layer1-6"
|
||||
transform="matrix(1,0,0,-1,5e-7,1080.3622)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#4d4d4d;fill-opacity:1;stroke:none"
|
||||
d="M 3.7070312,7 3,7.7070312 l 6.125,6.1249998 1.875,1.875 1.875,-1.875 L 19,7.7070312 18.292969,7 12.167969,13.125 11,14.292969 9.8320312,13.125 3.7070312,7 Z"
|
||||
transform="translate(-5e-7,1030.3622)"
|
||||
id="rect4176" />
|
||||
</g>
|
||||
<rect
|
||||
transform="scale(1,-1)"
|
||||
y="-32"
|
||||
x="3"
|
||||
height="1"
|
||||
width="16"
|
||||
id="rect4186"
|
||||
style="opacity:1;fill:#4d4d4d;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
148
client/qmarkdowntextedit/media/window-close.svg
Normal file
148
client/qmarkdowntextedit/media/window-close.svg
Normal file
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="24"
|
||||
height="24"
|
||||
id="svg3869"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="window-close.svg">
|
||||
<defs
|
||||
id="defs3871">
|
||||
<linearGradient
|
||||
id="linearGradient3257">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#a50000;stop-opacity:1"
|
||||
id="stop3259" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#e73800;stop-opacity:1"
|
||||
id="stop3261" />
|
||||
</linearGradient>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4210">
|
||||
<rect
|
||||
y="1024.3622"
|
||||
x="-7"
|
||||
height="34"
|
||||
width="34"
|
||||
id="rect4212"
|
||||
style="opacity:1;fill:#0000ff;fill-opacity:0.51376145;stroke:none;stroke-opacity:1" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4160">
|
||||
<rect
|
||||
style="opacity:1;fill:#aade87;fill-opacity:0.47247709;stroke:none;stroke-opacity:1"
|
||||
id="rect4162"
|
||||
width="32"
|
||||
height="32.000015"
|
||||
x="-6"
|
||||
y="1028.3619" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="22.627416"
|
||||
inkscape:cx="5.4926209"
|
||||
inkscape:cy="10.264796"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="709"
|
||||
inkscape:window-x="-4"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-bbox="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4132" />
|
||||
<sodipodi:guide
|
||||
position="4,18"
|
||||
orientation="18,0"
|
||||
id="guide4138" />
|
||||
<sodipodi:guide
|
||||
position="5,3"
|
||||
orientation="0,18"
|
||||
id="guide4140" />
|
||||
<sodipodi:guide
|
||||
position="20,2.0000174"
|
||||
orientation="-18,0"
|
||||
id="guide4142" />
|
||||
<sodipodi:guide
|
||||
position="2,21"
|
||||
orientation="0,-18"
|
||||
id="guide4144" />
|
||||
<sodipodi:guide
|
||||
position="3,19.000017"
|
||||
orientation="16,0"
|
||||
id="guide4146" />
|
||||
<sodipodi:guide
|
||||
position="2,4"
|
||||
orientation="0,16"
|
||||
id="guide4148" />
|
||||
<sodipodi:guide
|
||||
position="21,20"
|
||||
orientation="-16,0"
|
||||
id="guide4150" />
|
||||
<sodipodi:guide
|
||||
position="2,20"
|
||||
orientation="0,-16"
|
||||
id="guide4152" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3874">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-326,-532.3622)">
|
||||
<g
|
||||
transform="translate(-94.71429,7.57146)"
|
||||
id="layer1-7"
|
||||
inkscape:label="Capa 1">
|
||||
<circle
|
||||
id="circle16"
|
||||
cx="421.71429"
|
||||
cy="-504.57132"
|
||||
r="0" />
|
||||
<path
|
||||
id="path4144"
|
||||
d="m 432.71429,528.79074 a 7.9999995,8.0000081 0 0 0 -8,8 7.9999995,8.0000081 0 0 0 8,8 7.9999995,8.0000081 0 0 0 8,-8 7.9999995,8.0000081 0 0 0 -8,-8 z m -3.29297,4 3.29297,3.29297 3.29297,-3.29297 0.70703,0.70703 -3.29297,3.29297 3.29297,3.29297 -0.70703,0.70703 -3.29297,-3.29297 -3.29297,3.29297 -0.70703,-0.70703 3.29297,-3.29297 -3.29297,-3.29297 0.70703,-0.70703 z"
|
||||
style="opacity:1;fill:#da4453;fill-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
6
client/qmarkdowntextedit/qmarkdowntextedit-headers.pri
Normal file
6
client/qmarkdowntextedit/qmarkdowntextedit-headers.pri
Normal file
@ -0,0 +1,6 @@
|
||||
INCLUDEPATH += $$PWD/
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/markdownhighlighter.h \
|
||||
$$PWD/qmarkdowntextedit.h \
|
||||
$$PWD/qplaintexteditsearchwidget.h
|
||||
14
client/qmarkdowntextedit/qmarkdowntextedit-sources.pri
Normal file
14
client/qmarkdowntextedit/qmarkdowntextedit-sources.pri
Normal file
@ -0,0 +1,14 @@
|
||||
INCLUDEPATH += $$PWD/
|
||||
|
||||
QT += gui
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/markdownhighlighter.cpp \
|
||||
$$PWD/qmarkdowntextedit.cpp \
|
||||
$$PWD/qplaintexteditsearchwidget.cpp
|
||||
|
||||
RESOURCES += \
|
||||
$$PWD/media.qrc
|
||||
|
||||
FORMS += $$PWD/qplaintexteditsearchwidget.ui
|
||||
1099
client/qmarkdowntextedit/qmarkdowntextedit.cpp
Normal file
1099
client/qmarkdowntextedit/qmarkdowntextedit.cpp
Normal file
File diff suppressed because it is too large
Load Diff
87
client/qmarkdowntextedit/qmarkdowntextedit.h
Normal file
87
client/qmarkdowntextedit/qmarkdowntextedit.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPlainTextEdit>
|
||||
#include <QEvent>
|
||||
#include "markdownhighlighter.h"
|
||||
#include "qplaintexteditsearchwidget.h"
|
||||
|
||||
|
||||
class QMarkdownTextEdit : public QPlainTextEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum AutoTextOption {
|
||||
None = 0x0000,
|
||||
|
||||
// inserts closing characters for brackets and markdown characters
|
||||
BracketClosing = 0x0001,
|
||||
|
||||
// removes matching brackets and markdown characters
|
||||
BracketRemoval = 0x0002
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(AutoTextOptions, AutoTextOption)
|
||||
|
||||
explicit QMarkdownTextEdit(QWidget *parent = 0, bool initHighlighter = true);
|
||||
MarkdownHighlighter *highlighter();
|
||||
QPlainTextEditSearchWidget *searchWidget();
|
||||
void setIgnoredClickUrlSchemata(QStringList ignoredUrlSchemata);
|
||||
virtual void openUrl(QString urlString);
|
||||
QString getMarkdownUrlAtPosition(QString text, int position);
|
||||
void initSearchFrame(QWidget *searchFrame, bool darkMode = false);
|
||||
void setAutoTextOptions(AutoTextOptions options);
|
||||
void setHighlightingEnabled(bool enabled);
|
||||
static bool isValidUrl(QString urlString);
|
||||
void resetMouseCursor() const;
|
||||
void setReadOnly(bool ro);
|
||||
|
||||
public slots:
|
||||
void duplicateText();
|
||||
void setText(const QString & text);
|
||||
void setPlainText(const QString & text);
|
||||
void adjustRightMargin();
|
||||
void hide();
|
||||
bool openLinkAtCursorPosition();
|
||||
bool handleBracketRemoval();
|
||||
|
||||
protected:
|
||||
MarkdownHighlighter *_highlighter;
|
||||
bool _highlightingEnabled;
|
||||
QStringList _ignoredClickUrlSchemata;
|
||||
QPlainTextEditSearchWidget *_searchWidget;
|
||||
QWidget *_searchFrame;
|
||||
AutoTextOptions _autoTextOptions;
|
||||
QStringList _openingCharacters;
|
||||
QStringList _closingCharacters;
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
bool increaseSelectedTextIndention(bool reverse);
|
||||
bool handleTabEntered(bool reverse);
|
||||
QMap<QString, QString> parseMarkdownUrlsFromText(QString text);
|
||||
bool handleReturnEntered();
|
||||
bool handleBracketClosing(QString openingCharacter,
|
||||
QString closingCharacter = "");
|
||||
bool bracketClosingCheck(QString openingCharacter,
|
||||
QString closingCharacter);
|
||||
bool quotationMarkCheck(QString quotationCharacter);
|
||||
void focusOutEvent(QFocusEvent *event);
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
signals:
|
||||
void urlClicked(QString url);
|
||||
};
|
||||
4
client/qmarkdowntextedit/qmarkdowntextedit.pri
Normal file
4
client/qmarkdowntextedit/qmarkdowntextedit.pri
Normal file
@ -0,0 +1,4 @@
|
||||
INCLUDEPATH += $$PWD/
|
||||
|
||||
include($$PWD/qmarkdowntextedit-headers.pri)
|
||||
include($$PWD/qmarkdowntextedit-sources.pri)
|
||||
23
client/qmarkdowntextedit/qmarkdowntextedit.pro
Normal file
23
client/qmarkdowntextedit/qmarkdowntextedit.pro
Normal file
@ -0,0 +1,23 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2016-01-11T16:56:21
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = QMarkdownTextedit
|
||||
TEMPLATE = app
|
||||
TRANSLATIONS = trans/qmarkdowntextedit_de.ts
|
||||
CONFIG += c++11
|
||||
|
||||
SOURCES += main.cpp \
|
||||
mainwindow.cpp \
|
||||
|
||||
HEADERS += mainwindow.h
|
||||
|
||||
FORMS += mainwindow.ui
|
||||
|
||||
include(qmarkdowntextedit.pri)
|
||||
266
client/qmarkdowntextedit/qplaintexteditsearchwidget.cpp
Normal file
266
client/qmarkdowntextedit/qplaintexteditsearchwidget.cpp
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qplaintexteditsearchwidget.h"
|
||||
#include "ui_qplaintexteditsearchwidget.h"
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QDebug>
|
||||
|
||||
QPlainTextEditSearchWidget::QPlainTextEditSearchWidget(QPlainTextEdit *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::QPlainTextEditSearchWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
_textEdit = parent;
|
||||
_darkMode = false;
|
||||
hide();
|
||||
|
||||
QObject::connect(ui->closeButton, SIGNAL(clicked()),
|
||||
this, SLOT(deactivate()));
|
||||
QObject::connect(ui->searchLineEdit, SIGNAL(textChanged(const QString &)),
|
||||
this, SLOT(searchLineEditTextChanged(const QString &)));
|
||||
QObject::connect(ui->searchDownButton, SIGNAL(clicked()),
|
||||
this, SLOT(doSearchDown()));
|
||||
QObject::connect(ui->searchUpButton, SIGNAL(clicked()),
|
||||
this, SLOT(doSearchUp()));
|
||||
QObject::connect(ui->replaceToggleButton, SIGNAL(toggled(bool)),
|
||||
this, SLOT(setReplaceMode(bool)));
|
||||
QObject::connect(ui->replaceButton, SIGNAL(clicked()),
|
||||
this, SLOT(doReplace()));
|
||||
QObject::connect(ui->replaceAllButton, SIGNAL(clicked()),
|
||||
this, SLOT(doReplaceAll()));
|
||||
|
||||
installEventFilter(this);
|
||||
ui->searchLineEdit->installEventFilter(this);
|
||||
ui->replaceLineEdit->installEventFilter(this);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// set the spacing to 8 for OS X
|
||||
layout()->setSpacing(8);
|
||||
ui->buttonFrame->layout()->setSpacing(9);
|
||||
|
||||
// set the margin to 0 for the top buttons for OS X
|
||||
QString buttonStyle = "QPushButton {margin: 0}";
|
||||
ui->closeButton->setStyleSheet(buttonStyle);
|
||||
ui->searchDownButton->setStyleSheet(buttonStyle);
|
||||
ui->searchUpButton->setStyleSheet(buttonStyle);
|
||||
ui->replaceToggleButton->setStyleSheet(buttonStyle);
|
||||
ui->matchCaseSensitiveButton->setStyleSheet(buttonStyle);
|
||||
#endif
|
||||
}
|
||||
|
||||
QPlainTextEditSearchWidget::~QPlainTextEditSearchWidget() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::activate() {
|
||||
setReplaceMode(false);
|
||||
show();
|
||||
|
||||
// preset the selected text as search text if there is any and there is no
|
||||
// other search text
|
||||
QString selectedText = _textEdit->textCursor().selectedText();
|
||||
if (!selectedText.isEmpty() && ui->searchLineEdit->text().isEmpty()) {
|
||||
ui->searchLineEdit->setText(selectedText);
|
||||
}
|
||||
|
||||
ui->searchLineEdit->setFocus();
|
||||
ui->searchLineEdit->selectAll();
|
||||
doSearchDown();
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::activateReplace() {
|
||||
// replacing is prohibited if the text edit is readonly
|
||||
if (_textEdit->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ui->searchLineEdit->setText(_textEdit->textCursor().selectedText());
|
||||
ui->searchLineEdit->selectAll();
|
||||
activate();
|
||||
setReplaceMode(true);
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::deactivate() {
|
||||
hide();
|
||||
_textEdit->setFocus();
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::setReplaceMode(bool enabled) {
|
||||
ui->replaceToggleButton->setChecked(enabled);
|
||||
ui->replaceLabel->setVisible(enabled);
|
||||
ui->replaceLineEdit->setVisible(enabled);
|
||||
ui->modeLabel->setVisible(enabled);
|
||||
ui->buttonFrame->setVisible(enabled);
|
||||
ui->matchCaseSensitiveButton->setVisible(enabled);
|
||||
}
|
||||
|
||||
bool QPlainTextEditSearchWidget::eventFilter(QObject *obj, QEvent *event) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
deactivate();
|
||||
return true;
|
||||
} else if ((keyEvent->modifiers().testFlag(Qt::ShiftModifier) &&
|
||||
(keyEvent->key() == Qt::Key_Return)) ||
|
||||
(keyEvent->key() == Qt::Key_Up)) {
|
||||
doSearchUp();
|
||||
return true;
|
||||
} else if ((keyEvent->key() == Qt::Key_Return) ||
|
||||
(keyEvent->key() == Qt::Key_Down)) {
|
||||
doSearchDown();
|
||||
return true;
|
||||
} else if (keyEvent->key() == Qt::Key_F3) {
|
||||
doSearch(!keyEvent->modifiers().testFlag(Qt::ShiftModifier));
|
||||
return true;
|
||||
}
|
||||
|
||||
// if ((obj == ui->replaceLineEdit) && (keyEvent->key() == Qt::Key_Tab)
|
||||
// && ui->replaceToggleButton->isChecked()) {
|
||||
// ui->replaceLineEdit->setFocus();
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return QWidget::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::searchLineEditTextChanged(const QString &arg1) {
|
||||
Q_UNUSED(arg1);
|
||||
doSearchDown();
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::doSearchUp() {
|
||||
doSearch(false);
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::doSearchDown() {
|
||||
doSearch(true);
|
||||
}
|
||||
|
||||
bool QPlainTextEditSearchWidget::doReplace(bool forAll) {
|
||||
if (_textEdit->isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextCursor cursor = _textEdit->textCursor();
|
||||
|
||||
if (!forAll && cursor.selectedText().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int searchMode = ui->modeComboBox->currentIndex();
|
||||
if (searchMode == RegularExpressionMode) {
|
||||
QString text = cursor.selectedText();
|
||||
text.replace(QRegExp(ui->searchLineEdit->text()),
|
||||
ui->replaceLineEdit->text());
|
||||
cursor.insertText(text);
|
||||
} else {
|
||||
cursor.insertText(ui->replaceLineEdit->text());
|
||||
}
|
||||
|
||||
if (!forAll) {
|
||||
int position = cursor.position();
|
||||
|
||||
if (!doSearch(true)) {
|
||||
// restore the last cursor position if text wasn't found any more
|
||||
cursor.setPosition(position);
|
||||
_textEdit->setTextCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::doReplaceAll() {
|
||||
if (_textEdit->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start at the top
|
||||
_textEdit->moveCursor(QTextCursor::Start);
|
||||
|
||||
// replace until everything to the bottom is replaced
|
||||
while (doSearch(true, false) && doReplace(true)) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Searches for text in the text edit
|
||||
* @returns true if found
|
||||
*/
|
||||
bool QPlainTextEditSearchWidget::doSearch(bool searchDown, bool allowRestartAtTop) {
|
||||
QString text = ui->searchLineEdit->text();
|
||||
|
||||
if (text == "") {
|
||||
ui->searchLineEdit->setStyleSheet("");
|
||||
return false;
|
||||
}
|
||||
|
||||
int searchMode = ui->modeComboBox->currentIndex();
|
||||
|
||||
QFlags<QTextDocument::FindFlag> options = searchDown ?
|
||||
QTextDocument::FindFlag(0)
|
||||
: QTextDocument::FindBackward;
|
||||
if (searchMode == WholeWordsMode) {
|
||||
options |= QTextDocument::FindWholeWords;
|
||||
}
|
||||
|
||||
if (ui->matchCaseSensitiveButton->isChecked()) {
|
||||
options |= QTextDocument::FindCaseSensitively;
|
||||
}
|
||||
|
||||
bool found;
|
||||
if (searchMode == RegularExpressionMode) {
|
||||
found = _textEdit->find(QRegExp(text), options);
|
||||
} else {
|
||||
found = _textEdit->find(text, options);
|
||||
}
|
||||
|
||||
// start at the top (or bottom) if not found
|
||||
if (!found && allowRestartAtTop) {
|
||||
_textEdit->moveCursor(
|
||||
searchDown ? QTextCursor::Start : QTextCursor::End);
|
||||
found = _textEdit->find(text, options);
|
||||
}
|
||||
|
||||
QRect rect = _textEdit->cursorRect();
|
||||
QMargins margins = _textEdit->layout()->contentsMargins();
|
||||
int searchWidgetHotArea = _textEdit->height() - this->height();
|
||||
int marginBottom = (rect.y() > searchWidgetHotArea) ? (this->height() + 10)
|
||||
: 0;
|
||||
|
||||
// move the search box a bit up if we would block the search result
|
||||
if (margins.bottom() != marginBottom) {
|
||||
margins.setBottom(marginBottom);
|
||||
_textEdit->layout()->setContentsMargins(margins);
|
||||
}
|
||||
|
||||
// add a background color according if we found the text or not
|
||||
QString colorCode = found ? "#D5FAE2" : "#FAE9EB";
|
||||
|
||||
if (_darkMode) {
|
||||
colorCode = found ? "#135a13" : "#8d2b36";
|
||||
}
|
||||
|
||||
ui->searchLineEdit->setStyleSheet("* { background: " + colorCode + "; }");
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void QPlainTextEditSearchWidget::setDarkMode(bool enabled) {
|
||||
_darkMode = enabled;
|
||||
}
|
||||
60
client/qmarkdowntextedit/qplaintexteditsearchwidget.h
Normal file
60
client/qmarkdowntextedit/qplaintexteditsearchwidget.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Patrizio Bekerle -- http://www.bekerle.com
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
namespace Ui {
|
||||
class QPlainTextEditSearchWidget;
|
||||
}
|
||||
|
||||
class QPlainTextEditSearchWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum SearchMode {
|
||||
PlainTextMode,
|
||||
WholeWordsMode,
|
||||
RegularExpressionMode
|
||||
};
|
||||
|
||||
explicit QPlainTextEditSearchWidget(QPlainTextEdit *parent = 0);
|
||||
bool doSearch(bool searchDown = true, bool allowRestartAtTop = true);
|
||||
void setDarkMode(bool enabled);
|
||||
~QPlainTextEditSearchWidget();
|
||||
|
||||
private:
|
||||
Ui::QPlainTextEditSearchWidget *ui;
|
||||
|
||||
protected:
|
||||
QPlainTextEdit *_textEdit;
|
||||
bool _darkMode;
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
|
||||
public slots:
|
||||
void activate();
|
||||
void deactivate();
|
||||
void doSearchDown();
|
||||
void doSearchUp();
|
||||
void setReplaceMode(bool enabled);
|
||||
void activateReplace();
|
||||
bool doReplace(bool forAll = false);
|
||||
void doReplaceAll();
|
||||
|
||||
protected slots:
|
||||
void searchLineEditTextChanged(const QString &arg1);
|
||||
};
|
||||
253
client/qmarkdowntextedit/qplaintexteditsearchwidget.ui
Normal file
253
client/qmarkdowntextedit/qplaintexteditsearchwidget.ui
Normal file
@ -0,0 +1,253 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QPlainTextEditSearchWidget</class>
|
||||
<widget class="QWidget" name="QPlainTextEditSearchWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>836</width>
|
||||
<height>142</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="6">
|
||||
<widget class="QPushButton" name="replaceToggleButton">
|
||||
<property name="toolTip">
|
||||
<string>replace text</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-find-replace" resource="media.qrc">
|
||||
<normaloff>:/media/edit-find-replace.svg</normaloff>:/media/edit-find-replace.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="searchLabel">
|
||||
<property name="text">
|
||||
<string>Find:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="toolTip">
|
||||
<string>close search</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="window-close" resource="media.qrc">
|
||||
<normaloff>:/media/window-close.svg</normaloff>:/media/window-close.svg</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLineEdit" name="searchLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>find in text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="searchDownButton">
|
||||
<property name="toolTip">
|
||||
<string>search forward</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-bottom" resource="media.qrc">
|
||||
<normaloff>:/media/go-bottom.svg</normaloff>:/media/go-bottom.svg</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QPushButton" name="searchUpButton">
|
||||
<property name="toolTip">
|
||||
<string>search backward</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="go-top" resource="media.qrc">
|
||||
<normaloff>:/media/go-top.svg</normaloff>:/media/go-top.svg</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QLineEdit" name="replaceLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>replace with</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="replaceLabel">
|
||||
<property name="text">
|
||||
<string>Replace:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QFrame" name="buttonFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QComboBox" name="modeComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Plain text</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Whole words</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Regular expression</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="replaceButton">
|
||||
<property name="text">
|
||||
<string>Replace</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="replaceAllButton">
|
||||
<property name="text">
|
||||
<string>Replace All</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="modeLabel">
|
||||
<property name="text">
|
||||
<string>Mode:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="matchCaseSensitiveButton">
|
||||
<property name="toolTip">
|
||||
<string>Match case sensitive</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="format-text-superscript" resource="media.qrc">
|
||||
<normaloff>:/media/format-text-superscript.svg</normaloff>:/media/format-text-superscript.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>searchLineEdit</tabstop>
|
||||
<tabstop>replaceLineEdit</tabstop>
|
||||
<tabstop>replaceButton</tabstop>
|
||||
<tabstop>replaceAllButton</tabstop>
|
||||
<tabstop>searchDownButton</tabstop>
|
||||
<tabstop>searchUpButton</tabstop>
|
||||
<tabstop>replaceToggleButton</tabstop>
|
||||
<tabstop>closeButton</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="media.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
BIN
client/qmarkdowntextedit/screenshot.png
Normal file
BIN
client/qmarkdowntextedit/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
BIN
client/qmarkdowntextedit/trans/qmarkdowntextedit_de.qm
Normal file
BIN
client/qmarkdowntextedit/trans/qmarkdowntextedit_de.qm
Normal file
Binary file not shown.
57
client/qmarkdowntextedit/trans/qmarkdowntextedit_de.ts
Normal file
57
client/qmarkdowntextedit/trans/qmarkdowntextedit_de.ts
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.0" language="de_DE">
|
||||
<context>
|
||||
<name>QPlainTextEditSearchWidget</name>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="62"/>
|
||||
<source>close search</source>
|
||||
<translation>Suche schließen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="52"/>
|
||||
<source>Find:</source>
|
||||
<translation>Finden:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="32"/>
|
||||
<source>replace text</source>
|
||||
<translation>Text ersetzen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="79"/>
|
||||
<source>find in text</source>
|
||||
<translation>im Text finden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="86"/>
|
||||
<source>search forward</source>
|
||||
<translation>vorwärts suchen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="103"/>
|
||||
<source>search backward</source>
|
||||
<translation>rückwärts suchen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="120"/>
|
||||
<source>replace with</source>
|
||||
<translation>ersetzen mit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="127"/>
|
||||
<source>Replace:</source>
|
||||
<translation>Ersetzen:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="168"/>
|
||||
<source>Replace</source>
|
||||
<translation>Ersetzen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qplaintexteditsearchwidget.ui" line="178"/>
|
||||
<source>Replace All</source>
|
||||
<translation>Alle ersetzen</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
Loading…
x
Reference in New Issue
Block a user