mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 03:18:07 +00:00
Linux spell checker based on ispell
This commit is contained in:
parent
92ca29b3cd
commit
19d54cb0aa
15 changed files with 316 additions and 9 deletions
5
.gitmodules
vendored
5
.gitmodules
vendored
|
|
@ -6,4 +6,7 @@ path = linphone-sdk
|
|||
url = https://gitlab.linphone.org/BC/public/linphone-desktop-plugins/contacts/contacts-api.git
|
||||
[submodule "external/qtkeychain"]
|
||||
path = external/qtkeychain
|
||||
url = https://gitlab.linphone.org/BC/public/external/qtkeychain.git
|
||||
url = https://gitlab.linphone.org/BC/public/external/qtkeychain.git
|
||||
[submodule "external/ispell"]
|
||||
path = external/ispell
|
||||
url = https://gitlab.linphone.org/BC/public/external/ispell.git
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ if( NOT QTKEYCHAIN_OUTPUT_DIR) # set this variable only if you don't build the m
|
|||
set(QTKEYCHAIN_OUTPUT_DIR "${CMAKE_INSTALL_PREFIX}")# Cannot be different from the current CMAKE_INSTALL_PREFIX
|
||||
endif()
|
||||
|
||||
if(NOT ISPELL_OUTPUT_DIR) # set this variable only if you don't build the module
|
||||
set(ISPELL_OUTPUT_DIR "${CMAKE_INSTALL_PREFIX}")# Cannot be different from the current CMAKE_INSTALL_PREFIX
|
||||
endif()
|
||||
|
||||
|
||||
# Avoid cmake warning if CMP0071 is not set.
|
||||
|
|
@ -219,6 +222,13 @@ if(NOT APPLE OR MONO_ARCH)
|
|||
endfunction()
|
||||
add_linphone_keychain()
|
||||
endif()
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
function(add_linphone_ispell)
|
||||
add_subdirectory("external/ispell")
|
||||
endfunction()
|
||||
add_linphone_ispell()
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
endif()
|
||||
endif()
|
||||
function(add_linphone_app)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # Prevent project from overriding the options we just set here
|
||||
|
|
|
|||
1
external/ispell
vendored
Submodule
1
external/ispell
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 061c7e52b507f146396c3b08f289c88ca598fc2f
|
||||
|
|
@ -67,7 +67,7 @@ list(APPEND CMAKE_MODULE_PATH "${LINPHONE_OUTPUT_DIR}/lib64/cmake")
|
|||
list(APPEND CMAKE_MODULE_PATH "${LINPHONE_OUTPUT_DIR}/lib/cmake")
|
||||
|
||||
list(APPEND CMAKE_PREFIX_PATH "${QTKEYCHAIN_OUTPUT_DIR}/lib/cmake")
|
||||
|
||||
list(APPEND CMAKE_PREFIX_PATH "${ISPELL_OUTPUT_DIR}/lib/cmake")
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND CMAKE_FRAMEWORK_PATH "${LINPHONE_OUTPUT_DIR}/Frameworks")
|
||||
|
|
@ -132,6 +132,16 @@ if(ENABLE_QT_KEYCHAIN)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
if(NOT ISPELL_TARGET_NAME)
|
||||
set(ISPELL_TARGET_NAME "ISpell")
|
||||
endif()
|
||||
find_package(${ISPELL_TARGET_NAME})
|
||||
if(NOT ISpell_FOUND)
|
||||
find_package(${ISPELL_TARGET_NAME} CONFIG REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_BUILD_VERBOSE)
|
||||
message("INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} FRAMEWORK_PATH=${CMAKE_FRAMEWORK_PATH}, PREFIX_PATH=${CMAKE_PREFIX_PATH}")
|
||||
message("LINPHONE : ${LINPHONE_INCLUDE_DIRS} => ${LINPHONE_LIBRARIES}")
|
||||
|
|
@ -194,6 +204,9 @@ if( ENABLE_QT_KEYCHAIN)
|
|||
endif()
|
||||
list(APPEND APP_TARGETS ${QTKEYCHAIN_TARGET_NAME})
|
||||
endif()
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
list(APPEND APP_TARGETS ${ISPELL_TARGET_NAME})
|
||||
endif()
|
||||
if (UNIX AND NOT APPLE)
|
||||
list(APPEND QT5_PACKAGES DBus)
|
||||
endif ()
|
||||
|
|
@ -715,6 +728,9 @@ include_directories("${LINPHONE_OUTPUT_DIR}/include/OpenGL")
|
|||
include_directories("${LINPHONE_OUTPUT_DIR}/include/")
|
||||
include_directories("${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/")
|
||||
include_directories("${QTKEYCHAIN_OUTPUT_DIR}/include/")
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
include_directories("${ISpell_BINARY_DIR}/include")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_INSTALL_RPATH)
|
||||
#Retrieve lib path from a know QT executable
|
||||
|
|
|
|||
47
linphone-app/cmake/FindISpell.cmake
Normal file
47
linphone-app/cmake/FindISpell.cmake
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
############################################################################
|
||||
# FindISpell.cmake
|
||||
# Copyright (C) 2023 Belledonne Communications, Grenoble France
|
||||
#
|
||||
############################################################################
|
||||
#
|
||||
# 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; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
############################################################################
|
||||
#
|
||||
# - Find the ispell include files and library
|
||||
#
|
||||
# ISpell_FOUND - system has lib ispell
|
||||
# ISpell_SOURCE_DIR - the ispell include directory
|
||||
# ISpell_BINARY_DIR - the ispell library directory
|
||||
|
||||
if(NOT TARGET ${ISPELL_TARGET_NAME})
|
||||
set(EXPORT_PATH ${ISPELL_OUTPUT_DIR})
|
||||
include(GNUInstallDirs)
|
||||
include(${EXPORT_PATH}/${CMAKE_INSTALL_LIBDIR}/cmake/${ISPELL_TARGET_NAME}/${ISPELL_TARGET_NAME}Config.cmake)
|
||||
endif()
|
||||
|
||||
set(_ISpell_REQUIRED_VARS ISpell_TARGET)
|
||||
set(_ISpell_CACHE_VARS ${_ISpell_REQUIRED_VARS})
|
||||
|
||||
if(TARGET ${ISPELL_TARGET_NAME})
|
||||
set(ISpell_TARGET ${ISPELL_TARGET_NAME})
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(ISpell
|
||||
REQUIRED_VARS ${_ISpell_REQUIRED_VARS}
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
mark_as_advanced(${_ISpell_CACHE_VARS})
|
||||
|
|
@ -332,6 +332,8 @@ else()# Not Windows and Apple
|
|||
if(ENABLE_APP_WEBVIEW)
|
||||
install(FILES "${QT_PATH}/plugins/webview/libqtwebview_webengine.so" DESTINATION "plugins/webview") #Workaround : linuxdeploy doesn't deploy it
|
||||
endif()
|
||||
# ISPELL
|
||||
install(DIRECTORY "${ISpell_SOURCE_DIR}/ispell_dictionaries" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS)
|
||||
endif ()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -309,6 +309,14 @@ string Paths::getZrtpSecretsFilePath () {
|
|||
return getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathZrtpSecrets);
|
||||
}
|
||||
|
||||
QString Paths::getISpellDictsDirPath () {
|
||||
return getAppPackageDataDirPath() + Constants::PathISpellDicts;
|
||||
}
|
||||
|
||||
string Paths::getISpellOwnDictsDirPath () {
|
||||
return getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) + Constants::PathISpellOwnDict;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void migrateFile (const QString &oldPath, const QString &newPath) {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ namespace Paths {
|
|||
std::string getUserCertificatesDirPath ();
|
||||
std::string getZrtpDataFilePath ();
|
||||
std::string getZrtpSecretsFilePath ();
|
||||
|
||||
QString getISpellDictsDirPath ();
|
||||
std::string getISpellOwnDictsDirPath ();
|
||||
|
||||
void migrate ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ QString Clipboard::getChatFormattedText () const {
|
|||
QString text = getText();
|
||||
if (text.isEmpty())
|
||||
return text;
|
||||
#ifdef linux
|
||||
#ifdef __linux__
|
||||
QString cr = "\n";
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
#include <QTimer>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QTextEdit>
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "components/settings/SettingsModel.hpp"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <spellcheck.h>
|
||||
#endif
|
||||
|
|
@ -32,15 +35,25 @@ SpellChecker::SpellChecker(QObject *parent) : QSyntaxHighlighter(parent) {
|
|||
errorFormater.setFontUnderline(true);
|
||||
errorFormater.setUnderlineColor(Qt::red); // not supported before Qt6.2
|
||||
|
||||
QFontMetrics fm = QFontMetrics(QApplication::font());
|
||||
QFontMetrics fm = QFontMetrics(CoreManager::getInstance()->getSettingsModel()->getTextMessageFont());
|
||||
#ifdef __linux__
|
||||
wave = QString("‾");
|
||||
QRect boundingRect = fm.boundingRect(wave);
|
||||
waveHeight = 10;
|
||||
waveTopPadding = 5;
|
||||
#else
|
||||
wave = QString(u8"\uFE4B");
|
||||
QRect boundingRect = fm.boundingRect(wave);
|
||||
waveHeight = 5;
|
||||
waveTopPadding = 3;
|
||||
#endif
|
||||
waveWidth = boundingRect.width();
|
||||
|
||||
graceTimer = new QTimer(this);
|
||||
graceTimer->setSingleShot(true);
|
||||
connect(graceTimer, SIGNAL(timeout()), SLOT(highlightAfterGracePeriod()));
|
||||
|
||||
mAvailable = false;
|
||||
setLanguage();
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +125,7 @@ void SpellChecker::highlightDocument() {
|
|||
QRectF boundingRect = fragment.glyphRuns(begin,length).first().boundingRect();
|
||||
QPointF start = boundingRect.bottomLeft();
|
||||
qreal width = boundingRect.width();
|
||||
redLines.append(QString::number(start.x())+","+QString::number(start.y())+","+QString::number(width)+","+underLine(width));
|
||||
redLines.append(QString::number(start.x())+","+QString::number(start.y())+","+QString::number(width)+","+underLine(width)+","+QString::number(waveHeight)+","+QString::number(waveTopPadding));
|
||||
}
|
||||
if (wordActive) {
|
||||
hadActiveWord = wordActive;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@
|
|||
#include <QTimer>
|
||||
#include "app/App.hpp"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
#define SUGGESTIONS_LIMIT 10
|
||||
#define GRACE_PERIOD_SECS 1.0
|
||||
|
||||
|
|
@ -89,7 +93,10 @@ private:
|
|||
QHash<int,QString> ignoredOnce;
|
||||
QString wave;
|
||||
qreal waveWidth;
|
||||
qreal waveHeight;
|
||||
qreal waveTopPadding;
|
||||
qint64 mLastHightlight;
|
||||
bool mAvailable;
|
||||
|
||||
void setLanguage();
|
||||
bool isWordActive(QStringList words, QString word, int index);
|
||||
|
|
@ -99,6 +106,24 @@ private:
|
|||
#ifdef WIN32
|
||||
ISpellChecker* mNativeSpellChecker = nullptr;
|
||||
#endif
|
||||
|
||||
// ISpell linux
|
||||
#ifdef __linux__
|
||||
static int gISpell_sc_read_fd;
|
||||
static int gISpell_sc_write_fd;
|
||||
static int gISpell_app_read_fd;
|
||||
static int gISpell_app_write_fd;
|
||||
static std::thread *gISpellCheckerThread;
|
||||
static QHash<QString,QStringList> gISpellSuggestions;
|
||||
static std::string gISpellCheckeCurrentLanguage;
|
||||
static std::string gIspellDictionariesFolder;
|
||||
void stopISpellChecker();
|
||||
static std::shared_ptr<linphone::Config> gISpellSelfDictionary;
|
||||
bool isLearnt(QString word);
|
||||
bool wordValidWithFrVariants(QString word);
|
||||
bool validSplittedOn(QString pattern, QString word);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,18 +20,194 @@
|
|||
|
||||
|
||||
#import "SpellChecker.hpp"
|
||||
#import <libispell.h>
|
||||
#include "app/paths/Paths.hpp"
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <linphone++/linphone.hh>
|
||||
#include "utils/Utils.hpp"
|
||||
#include <QCryptographicHash>
|
||||
|
||||
int SpellChecker::gISpell_sc_read_fd = 0;
|
||||
int SpellChecker::gISpell_sc_write_fd = 0;
|
||||
int SpellChecker::gISpell_app_read_fd = 0;
|
||||
int SpellChecker::gISpell_app_write_fd = 0;
|
||||
std::thread *SpellChecker::gISpellCheckerThread = nullptr;
|
||||
QHash<QString,QStringList> SpellChecker::gISpellSuggestions;
|
||||
std::string SpellChecker::gISpellCheckeCurrentLanguage;
|
||||
std::shared_ptr<linphone::Config> SpellChecker::gISpellSelfDictionary = linphone::Factory::get()->createConfig(Paths::getISpellOwnDictsDirPath());
|
||||
std::string SpellChecker::gIspellDictionariesFolder;
|
||||
|
||||
|
||||
bool open_channel(int& read_fd, int& write_fd) {
|
||||
int vals[2];
|
||||
int errc = pipe(vals);
|
||||
if(errc) {
|
||||
return false;
|
||||
} else {
|
||||
read_fd = vals[0];
|
||||
write_fd = vals[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void SpellChecker::stopISpellChecker() {
|
||||
QString stop("__spellchecker_stop__");
|
||||
auto message = stop.toStdString();
|
||||
ssize_t amnt_written = write(gISpell_sc_write_fd, message.data(), message.size());
|
||||
if(amnt_written != message.size()) {
|
||||
qWarning() << LOG_TAG << "Linux ispell unable to stop spell checker";
|
||||
}
|
||||
gISpellCheckerThread->join();
|
||||
gISpellCheckerThread = nullptr;
|
||||
mAvailable = false;
|
||||
}
|
||||
|
||||
void SpellChecker::setLanguage() {
|
||||
|
||||
QString locale = SpellChecker::currentLanguage().toLower().mid(0,2);
|
||||
|
||||
if (gISpellCheckeCurrentLanguage == locale.toStdString() && gISpellCheckerThread != nullptr) {
|
||||
mAvailable = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gISpellCheckerThread != nullptr) // Language change
|
||||
stopISpellChecker();
|
||||
|
||||
QString dict = Paths::getISpellDictsDirPath()+locale+".hash";
|
||||
gIspellDictionariesFolder = Paths::getISpellDictsDirPath().toStdString();
|
||||
|
||||
if (!QFile::exists(dict)) {
|
||||
qWarning() << LOG_TAG << "Linux ispell language not supported " << SpellChecker::currentLanguage() << dict;
|
||||
mAvailable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!open_channel(gISpell_sc_read_fd, gISpell_sc_write_fd) ||
|
||||
!open_channel(gISpell_app_read_fd, gISpell_app_write_fd)) {
|
||||
qWarning() << LOG_TAG << "Linux ispell language unable to open channels";
|
||||
mAvailable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
gISpellCheckeCurrentLanguage = locale.toStdString();
|
||||
gISpellCheckerThread = new std::thread(bc_spell_checker,
|
||||
gIspellDictionariesFolder.data(),
|
||||
gISpellCheckeCurrentLanguage.data(),
|
||||
gISpell_sc_read_fd,
|
||||
gISpell_app_write_fd);
|
||||
|
||||
mAvailable = true;
|
||||
qDebug() << LOG_TAG << "Linux ispell language loaded from " << dict;
|
||||
}
|
||||
|
||||
// Few special situation in French language not detected by the fr.hash.
|
||||
|
||||
bool SpellChecker::wordValidWithFrVariants(QString word) {
|
||||
if (word.toLower().contains("qu'")) {
|
||||
QString replace = word.toLower().replace("qu'","que ");
|
||||
if (isValid(replace)||validSplittedOn(" ",replace))
|
||||
return true;
|
||||
}
|
||||
if (word.toLower().contains("s'")) {
|
||||
QString replace = word.toLower().replace("s'","se ");
|
||||
if (isValid(replace)||validSplittedOn(" ",replace))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SpellChecker::validSplittedOn(QString pattern, QString word) {
|
||||
if (!word.contains(pattern))
|
||||
return false;
|
||||
auto split = word.split(pattern);
|
||||
return isValid(split[0]) && isValid(split[1]);
|
||||
}
|
||||
|
||||
bool SpellChecker::isValid(QString word) {
|
||||
return true;
|
||||
|
||||
if (!mAvailable || word.length() == 1 || isLearnt(word))
|
||||
return true;
|
||||
|
||||
// no letters in word -> valid
|
||||
QString wordCopy = word;
|
||||
auto iterator = std::remove_if(wordCopy.begin(), wordCopy.end(), [](const QChar& c){ return !c.isLetter();});
|
||||
wordCopy.chop(std::distance(iterator, wordCopy.end()));
|
||||
if (wordCopy.isEmpty())
|
||||
return true;
|
||||
|
||||
// Some preformating
|
||||
|
||||
word = word.replace("’","'");
|
||||
word = word.replace("(","");
|
||||
word = word.replace(")","");
|
||||
word = word.replace("‘","'");
|
||||
|
||||
while (word.endsWith(".") || word.endsWith("!") || word.endsWith(",") || word.endsWith(","))
|
||||
word.chop(1);
|
||||
|
||||
while (word.startsWith(".") || word.startsWith("!") || word.startsWith(",") || word.startsWith(",")) {
|
||||
word = word.mid(1);
|
||||
}
|
||||
|
||||
// submit word to ispell
|
||||
auto message = word.toStdString();
|
||||
ssize_t amnt_written = write(gISpell_sc_write_fd, message.data(), message.size());
|
||||
if(amnt_written != message.size()) {
|
||||
qWarning() << LOG_TAG << "Linux ispell unable to communicate with spell checker thread";
|
||||
return true;
|
||||
}
|
||||
|
||||
// wait and read ispell result
|
||||
constexpr int buffer_size = 1024;
|
||||
char buffer[buffer_size] = {0};
|
||||
ssize_t amnt_read = read(gISpell_app_read_fd, &buffer[0], buffer_size);
|
||||
QString returned = QString::fromUtf8(buffer);
|
||||
if (returned == "1") {
|
||||
return true;
|
||||
} else {
|
||||
if (!gISpellSuggestions.contains(word)) { // Record returned suggestions if any
|
||||
QStringList returnedUggestions = returned.split(", ");
|
||||
returnedUggestions.removeFirst();
|
||||
gISpellSuggestions.insert(word,returnedUggestions);
|
||||
}
|
||||
return (gISpellCheckeCurrentLanguage == "fr" && wordValidWithFrVariants(word)) ||
|
||||
validSplittedOn("'",word) ||
|
||||
validSplittedOn("-",word);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SpellChecker::learn(QString word){
|
||||
QCryptographicHash hash( QCryptographicHash::Sha1 ); // Hash to avoid fancy character conflict with config format.
|
||||
hash.addData(word.toUtf8());
|
||||
auto hashString = Utils::appStringToCoreString(hash.result().toHex());
|
||||
gISpellSelfDictionary->setInt("words",hashString,1);
|
||||
gISpellSelfDictionary->sync();
|
||||
highlightDocument();
|
||||
}
|
||||
|
||||
bool SpellChecker::isLearnt(QString word){
|
||||
QCryptographicHash hash( QCryptographicHash::Sha1 ); // Hash to avoid fancy character conflict with config format.
|
||||
hash.addData(word.toUtf8());
|
||||
auto hashString = Utils::appStringToCoreString(hash.result().toHex());
|
||||
return gISpellSelfDictionary->getInt("words",hashString,0) == 1;
|
||||
}
|
||||
|
||||
QStringList SpellChecker::suggestionsForWord(QString word) {
|
||||
QStringList suggestions;
|
||||
if (!gISpellSuggestions.contains(word))
|
||||
return suggestions;
|
||||
QListIterator<QString> itr (gISpellSuggestions.value(word));
|
||||
while (itr.hasNext()) {
|
||||
QString suggestion = itr.next();
|
||||
if (!suggestion.contains("+"))
|
||||
suggestions << suggestion;
|
||||
if (suggestions.length() >= SUGGESTIONS_LIMIT) {
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ constexpr char Constants::PathFriendsList[];
|
|||
constexpr char Constants::PathLimeDatabase[];
|
||||
constexpr char Constants::PathMessageHistoryList[];
|
||||
constexpr char Constants::PathZrtpSecrets[];
|
||||
constexpr char Constants::PathISpellDicts[];
|
||||
constexpr char Constants::PathISpellOwnDict[];
|
||||
|
||||
// Max image size in bytes. (100Kb)
|
||||
constexpr qint64 Constants::MaxImageSize;
|
||||
|
|
|
|||
|
|
@ -144,6 +144,8 @@ public:
|
|||
static constexpr char PathLimeDatabase[] = "/x3dh.c25519.sqlite3";
|
||||
static constexpr char PathMessageHistoryList[] = "/message-history.db";
|
||||
static constexpr char PathZrtpSecrets[] = "/zidcache";
|
||||
static constexpr char PathISpellDicts[] = "/" EXECUTABLE_NAME "/ispell_dictionaries/";
|
||||
static constexpr char PathISpellOwnDict[] = "/" EXECUTABLE_NAME "/ispell_own_dict";
|
||||
|
||||
static constexpr char LanguagePath[] = ":/languages/";
|
||||
|
||||
|
|
|
|||
|
|
@ -238,9 +238,9 @@ Item {
|
|||
model: spellChecker.redLines
|
||||
Rectangle {
|
||||
clip: true
|
||||
height: 5
|
||||
height: parseFloat(modelData.split(',')[4])
|
||||
x: textArea.leftPadding + parseFloat(modelData.split(',')[0])
|
||||
y: textArea.topPadding + parseFloat(modelData.split(',')[1])-3
|
||||
y: textArea.topPadding + parseFloat(modelData.split(',')[1])-parseFloat(modelData.split(',')[5])
|
||||
width: parseFloat(modelData.split(',')[2])
|
||||
color: 'transparent'
|
||||
Text {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue