editor/src/helper.cpp

471 lines
14 KiB
C++

/*******************************************
* Zira Editor
* A lightweight PHP Editor
* (C)2019 https://github.com/ziracms/editor
*******************************************/
#include "helper.h"
#include <QFile>
#include <QTextStream>
#include <sstream>
#include <QFileInfo>
#include <QTextCodec>
#include <QMessageBox>
#include <QRegularExpression>
#include <QDir>
#include <QCoreApplication>
#include <QPluginLoader>
#include <QFileDialog>
#include <QApplication>
#include <QStandardPaths>
#include <QStylePlugin>
#include <QDirIterator>
#include "mainwindow.h"
#include "fileiconprovider.h"
#include "filedialog.h"
#if defined(Q_OS_ANDROID)
#include <QtAndroidExtras/QtAndroid>
#endif
const QString APPLICATION_NAME = "Zira Editor";
const QString APPLICATION_VERSION = "1.9.2";
const QString ORGANIZATION_NAME = "Zira";
const QString PROJECT_NAME = "Zira project";
const QString AUTHOR_EMAIL_USERNAME = "ziracms";
const QString AUTHOR_EMAIL_DOMAIN = "gmail.com";
const QString AUTHOR_CARD_URL = "https://money.yandex.ru/to";
const QString AUTHOR_CARD_ID = "410014796567498";
const QString AUTHOR_CMS_URL = "https://github.com/ziracms/zira";
const QString AUTHOR_DEVPACK_URL = "https://github.com/ziracms/devpack";
const QString GITHUB_EDITOR_URL = "https://github.com/ziracms/editor";
const QString STYLE_PLUGIN_SUFFIX = "Style";
const QString STYLE_PLUGIN_DISPLAY_NAME_SUFFIX = " Plugin";
const QString DIALOG_HEADER_STYLESHEET = "color:#fff;font-size:17px;background-image:url(:/image/abstract);background-position:left bottom;background-repeat:repeat-x;";
QString Helper::loadFile(QString path, std::string encoding, std::string fallbackEncoding, bool silent)
{
QFile inputFile(path);
if (!inputFile.open(QIODevice::ReadOnly)) return "";
QByteArray byteArray = inputFile.readAll();
inputFile.close();
QTextCodec::ConverterState state;
QTextCodec *codec = QTextCodec::codecForName(encoding.c_str());
QString txt = codec->toUnicode(byteArray.constData(), byteArray.size(), &state);
if (state.invalidChars > 0) {
if (!silent) {
showMessage(QObject::tr("File has a not valid byte sequence. Fallback encoding will be used."));
}
QTextCodec *fcodec = QTextCodec::codecForName(fallbackEncoding.c_str());
txt = fcodec->toUnicode(byteArray.constData(), byteArray.size(), &state);
}
return txt;
}
QString Helper::loadTextFile(QString path, std::string encoding, std::string fallbackEncoding, bool silent)
{
QFile inputFile(path);
if (!inputFile.open(QIODevice::ReadOnly | QIODevice::Text)) return "";
QByteArray byteArray = inputFile.readAll();
inputFile.close();
QTextCodec::ConverterState state;
QTextCodec *codec = QTextCodec::codecForName(encoding.c_str());
QString txt = codec->toUnicode(byteArray.constData(), byteArray.size(), &state);
if (state.invalidChars > 0) {
if (!silent) {
showMessage(QObject::tr("File has a not valid byte sequence. Fallback encoding will be used."));
}
QTextCodec *fcodec = QTextCodec::codecForName(fallbackEncoding.c_str());
txt = fcodec->toUnicode(byteArray.constData(), byteArray.size(), &state);
}
return txt;
}
bool Helper::saveTextFile(QString path, const QString & text, std::string encoding)
{
QFile outputFile(path);
if (!outputFile.open(QIODevice::WriteOnly)) return false;
QTextStream out(&outputFile);
out.setGenerateByteOrderMark(false);
out.setCodec(encoding.c_str());
out << text;
outputFile.close();
return true;
}
bool Helper::createFile(QString path)
{
QFile f(path);
if (f.exists()) return false;
if (!f.open(QIODevice::WriteOnly)) return false;
f.close();
return true;
}
bool Helper::createDir(QString path)
{
QDir dir;
if (dir.exists(path)) return false;
return dir.mkpath(path);
}
bool Helper::deleteFile(QString path)
{
QFile f(path);
if (!f.exists(path)) return false;
return f.remove();
}
bool Helper::deleteFolder(QString path)
{
QDir dir;
if (!dir.exists(path)) return false;
return dir.rmdir(path);
}
bool Helper::renameFile(QString path, QString newpath)
{
QFile f(path);
if (!f.exists()) return false;
return f.rename(newpath);
}
bool Helper::renameDir(QString path, QString newpath)
{
QDir dir;
if (!dir.exists(path)) return false;
return dir.rename(path, newpath);
}
bool Helper::renameFileOrFolder(QString path, QString newpath)
{
QFileInfo fInfo(path);
if (!fInfo.exists() || !fInfo.isReadable() || !fInfo.isWritable()) return false;
if (fInfo.isDir()) return renameDir(path, newpath);
else return renameFile(path, newpath);
}
bool Helper::copyFile(QString path, QString newpath)
{
QFile f(path);
if (!f.exists()) return false;
return f.copy(newpath);
}
bool Helper::fileExists(QString path)
{
QFileInfo check_file(path);
return check_file.exists() && check_file.isFile() && check_file.isReadable();
}
bool Helper::folderExists(QString path)
{
QFileInfo check_file(path);
return check_file.exists() && check_file.isDir() && check_file.isReadable();
}
bool Helper::fileOrFolderExists(QString path)
{
QFileInfo check_file(path);
return check_file.exists();
}
QString Helper::intToStr(int n)
{
std::stringstream ss;
ss << n;
return QString::fromStdString(ss.str());
}
QString Helper::doubleToStr(double n)
{
std::stringstream ss;
ss << n;
return QString::fromStdString(ss.str());
}
QString Helper::stripScopedText(QString scopedText)
{
// parenthesis
int pars = 0, parsStart = -1;
int p = scopedText.indexOf("(");
if (p >= 0) {
pars++;
parsStart = p;
}
while(p >= 0) {
int pO = scopedText.indexOf("(", p+1);
int pC = scopedText.indexOf(")", p+1);
int pM = -1;
if (pO >= 0 && pC >= 0) pM = std::min(pO, pC);
else if (pO >= 0 && pC < 0) pM = pO;
else if (pO < 0 && pC >= 0) pM = pC;
if (pM >= 0) {
if (pM == pO) {
if (pars == 0) parsStart = pM;
pars++;
}
if (pM == pC) pars--;
int start = parsStart + 1;
int len = pM-parsStart-1;
if (pars == 0 && len > 0) {
scopedText.replace(start, len, QString(" ").repeated(len));
}
}
p = pM;
}
// braces
int scope = 0, scopeStart = -1;
p = scopedText.indexOf("{");
if (p >= 0) {
scope++;
scopeStart = p;
}
while(p >= 0) {
int pO = scopedText.indexOf("{", p+1);
int pC = scopedText.indexOf("}", p+1);
int pM = -1;
if (pO >= 0 && pC >= 0) pM = std::min(pO, pC);
else if (pO >= 0 && pC < 0) pM = pO;
else if (pO < 0 && pC >= 0) pM = pC;
if (pM >= 0) {
if (pM == pO) {
if (scope == 0) scopeStart = pM;
scope++;
}
if (pM == pC) scope--;
int start = scopeStart + 1;
int len = pM-scopeStart-1;
if (scope == 0 && len > 0) {
scopedText.replace(start, len, QString(" ").repeated(len));
}
}
p = pM;
}
return scopedText.replace(QRegularExpression("[\\s]+"), " ");
}
void Helper::log(int n)
{
QString str = intToStr(n);
QTextStream out(stdout);
out << str;
}
void Helper::log(QString str)
{
QTextStream out(stdout);
out << str;
}
void Helper::log(const char * str)
{
QTextStream out(stdout);
out << str;
}
void Helper::log(std::string str)
{
const char * cstr = str.c_str();
QTextStream out(stdout);
out << cstr;
}
QString Helper::getPluginFile(QString name, QString path)
{
if (path.size() == 0) path = QCoreApplication::applicationDirPath() + "/" + PLUGINS_DEFAULT_FOLDER_NAME;
path += "/" + name;
QDir pluginsDir(path);
return pluginsDir.absoluteFilePath("lib"+name+".so");
}
QObject * Helper::loadPlugin(QString name, QString path)
{
QString pluginFile = getPluginFile(name, path);
if (!fileExists(pluginFile)) return nullptr;
QPluginLoader pluginLoader(pluginFile);
return pluginLoader.instance();
}
SpellCheckerInterface * Helper::loadSpellChecker(QString path)
{
QObject * plugin = loadPlugin(SPELLCHECKER_PLUGIN_NAME, path);
if (!plugin) return nullptr;
SpellCheckerInterface * spellChecker = qobject_cast<SpellCheckerInterface *>(plugin);
if (!spellChecker) {
delete plugin;
return nullptr;
}
spellChecker->initialize(path);
return spellChecker;
}
bool Helper::loadStylePlugin(QString name, QString path, bool light)
{
QObject * plugin = loadPlugin(name, path);
if (plugin == nullptr) return false;
QStylePlugin * stylePlugin = qobject_cast<QStylePlugin *>(plugin);
if (!stylePlugin) {
delete plugin;
return false;
}
QString styleName = light ? name+"-"+COLOR_SCHEME_LIGHT : name+"-"+COLOR_SCHEME_DARK;
QStyle * style = stylePlugin->create(styleName);
if (style == nullptr) return false;
QApplication::setStyle(style);
QApplication::setPalette(style->standardPalette());
return true;
}
QStringList Helper::getInstalledStylePlugins(QString path)
{
if (path.size() == 0) path = QCoreApplication::applicationDirPath() + "/" + PLUGINS_DEFAULT_FOLDER_NAME;
QDirIterator it(path, QDir::Dirs | QDir::NoDotAndDotDot);
QStringList pluginsList;
while (it.hasNext()) {
QString _path = it.next();
QFileInfo fInfo(_path);
if (!fInfo.exists() || !fInfo.isDir() || !fInfo.isReadable()) continue;
if (fInfo.baseName().size() < STYLE_PLUGIN_SUFFIX.size()+1) continue;
if (fInfo.baseName().mid(fInfo.baseName().size() - STYLE_PLUGIN_SUFFIX.size()) != STYLE_PLUGIN_SUFFIX) continue;
if (!isPluginExists(fInfo.baseName(), path)) continue;
pluginsList.append(fInfo.baseName());
}
return pluginsList;
}
TerminalInterface * Helper::loadTerminalPlugin(QString path)
{
QObject * plugin = loadPlugin(TERMINAL_PLUGIN_NAME, path);
if (!plugin) return nullptr;
TerminalInterface * terminal = qobject_cast<TerminalInterface *>(plugin);
if (!terminal) {
delete plugin;
return nullptr;
}
terminal->initialize(QDir::homePath());
return terminal;
}
bool Helper::isPluginExists(QString name, QString path)
{
QString pluginFile = getPluginFile(name, path);
return fileExists(pluginFile);
}
QString Helper::getExistingDirectory(QWidget * parent, QString title, QString directory)
{
#if defined(Q_OS_ANDROID)
requestAndroidPermissions();
#endif
QString dir = "";
FileDialog dialog(parent);
dialog.setWindowTitle(title);
dialog.setFileMode(QFileDialog::Directory);
dialog.setOptions(QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (directory.size() == 0) {
QStringList stddirs = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
if (stddirs.size()>0) directory = stddirs.at(0);
}
if (directory.size() > 0) dialog.setDirectory(directory);
dialog.setReadOnly(true);
if (dialog.exec()) {
QStringList dirs = dialog.selectedFiles();
if (dirs.size() > 0 && folderExists(dirs.at(0))) dir = dirs.at(0);
}
return dir;
}
QString Helper::getExistingFile(QWidget * parent, QString title, QString directory, QString filter)
{
#if defined(Q_OS_ANDROID)
requestAndroidPermissions();
#endif
QString file = "";
FileDialog dialog(parent);
dialog.setWindowTitle(title);
dialog.setFileMode(QFileDialog::ExistingFile);
if (filter.size() > 0) dialog.setNameFilter(filter);
if (directory.size() == 0) {
QStringList stddirs = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
if (stddirs.size()>0) directory = stddirs.at(0);
}
if (directory.size() > 0) dialog.setDirectory(directory);
dialog.setReadOnly(true);
if (dialog.exec()) {
QStringList fileNames = dialog.selectedFiles();
if (fileNames.size() > 0 && fileExists(fileNames.at(0))) file = fileNames.at(0);
}
return file;
}
QString Helper::getSaveFileName(QWidget * parent, QString title, QString directory, QString filter)
{
#if defined(Q_OS_ANDROID)
requestAndroidPermissions();
#endif
QString file = "";
FileDialog dialog(parent);
dialog.setWindowTitle(title);
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (filter.size() > 0) dialog.setNameFilter(filter);
if (directory.size() == 0) {
QStringList stddirs = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
if (stddirs.size()>0) directory = stddirs.at(0);
}
if (directory.size() > 0) dialog.setDirectory(directory);
dialog.setReadOnly(true);
if (dialog.exec()) {
QStringList fileNames = dialog.selectedFiles();
if (fileNames.size() > 0) file = fileNames.at(0);
}
return file;
}
QWidget * Helper::getWindowWidget()
{
QWidget * widget = QApplication::activeWindow();
QWidgetList widgets = QApplication::topLevelWidgets();
if (widgets.size() > 0) widget = widgets.at(0);
for (auto w : widgets) {
MainWindow * wnd = qobject_cast<MainWindow *>(w);
if (wnd) {
widget = wnd;
break;
}
}
return widget;
}
void Helper::showMessage(QString text)
{
QMessageBox msgBox(getWindowWidget());
msgBox.setWindowTitle(QObject::tr("Message"));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(text);
msgBox.exec();
}
bool Helper::showQuestion(QString title, QString msg)
{
return QMessageBox::question(getWindowWidget(), title, msg, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok;
}
#if defined(Q_OS_ANDROID)
void Helper::requestAndroidPermissions()
{
if(QtAndroid::androidSdkVersion() >= 23) {
const QString readPermissionID("android.permission.READ_EXTERNAL_STORAGE");
const QString writePermissionID("android.permission.WRITE_EXTERNAL_STORAGE");
if(QtAndroid::checkPermission(readPermissionID) != QtAndroid::PermissionResult::Granted || QtAndroid::checkPermission(writePermissionID) != QtAndroid::PermissionResult::Granted) {
QtAndroid::requestPermissionsSync(QStringList() << readPermissionID << writePermissionID);
}
}
}
#endif