diff --git a/.gitignore b/.gitignore index 2c47b8c..4db5df3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ ZiraEditor.pro.user +android/Qt5_12/assets/ +android/Qt5_14/assets/ diff --git a/bin/themes/Intellij Dark/icons/home.png b/bin/themes/Intellij Dark/icons/home.png new file mode 100644 index 0000000..ce47184 Binary files /dev/null and b/bin/themes/Intellij Dark/icons/home.png differ diff --git a/bin/themes/Intellij Dark/theme.css b/bin/themes/Intellij Dark/theme.css index dd6eca0..4031593 100644 --- a/bin/themes/Intellij Dark/theme.css +++ b/bin/themes/Intellij Dark/theme.css @@ -1040,7 +1040,8 @@ QuickAccess QListView:focus border: none; } -QColorDialog +QColorDialog, +QInputDialog { border: 1px solid #555459; padding: 3px; diff --git a/bin/themes/Studio Light/icons/home.png b/bin/themes/Studio Light/icons/home.png new file mode 100644 index 0000000..cf9ef4f Binary files /dev/null and b/bin/themes/Studio Light/icons/home.png differ diff --git a/bin/themes/Studio Light/theme.css b/bin/themes/Studio Light/theme.css index 9e5667a..436f65d 100644 --- a/bin/themes/Studio Light/theme.css +++ b/bin/themes/Studio Light/theme.css @@ -1043,7 +1043,8 @@ QuickAccess QListView:focus border: none; } -QColorDialog +QColorDialog, +QInputDialog { border: 1px solid #8dbae8; padding: 3px; diff --git a/include/filebrowser.h b/include/filebrowser.h index 34111b8..0374559 100644 --- a/include/filebrowser.h +++ b/include/filebrowser.h @@ -67,6 +67,7 @@ private slots: void fileBrowserItemChanged(QTreeWidgetItem * item, int col); void fileBrowserItemSelectionChanged(); void upActionTriggered(bool checked); + void homeActionTriggered(bool checked); signals: void openFile(QString); void fileCreated(QString); diff --git a/include/git.h b/include/git.h index 8de9fa7..4709605 100644 --- a/include/git.h +++ b/include/git.h @@ -18,6 +18,7 @@ extern const QString GIT_DIFF_COMMAND; extern const QString GIT_COMMIT_COMMAND; extern const QString GIT_PUSH_COMMAND; extern const QString GIT_PULL_COMMAND; +extern const QString GIT_CLONE_COMMAND; class Git : public QObject { @@ -46,6 +47,10 @@ public: void addAndCommit(QString path, QString msg); void pushOriginMaster(QString path); void pullOriginMaster(QString path); + void initialize(QString path); + void addRemoteURL(QString path, QString url); + void changeRemoteURL(QString path, QString url); + void clone(QString path, QString url); void showAnnotation(QString path, QString fileName, bool outputResult = true, bool silent = false); QString highlightCommand(QString & text); QString highlightOutput(QString & output); diff --git a/include/helper.h b/include/helper.h index 89171ad..a406505 100644 --- a/include/helper.h +++ b/include/helper.h @@ -21,6 +21,7 @@ extern const QString AUTHOR_EMAIL_DOMAIN; extern const QString AUTHOR_CARD_URL; extern const QString AUTHOR_CARD_ID; extern const QString AUTHOR_CMS_URL; +extern const QString AUTHOR_DEVPACK_URL; extern const QString GITHUB_EDITOR_URL; extern const QString STYLE_PLUGIN_SUFFIX; diff --git a/include/mainwindow.h b/include/mainwindow.h index f6dca70..2731d17 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -140,6 +140,10 @@ private slots: void on_actionGitCommit_triggered(bool add = false); void on_actionGitPush_triggered(); void on_actionGitPull_triggered(); + void on_actionGitInitializeRepository_triggered(); + void on_actionGitAddRemoteURL_triggered(); + void on_actionGitChangeRemoteURL_triggered(); + void on_actionGitCloneRepository_triggered(); void on_actionStartServers_triggered(); void on_actionStopServers_triggered(); void on_actionServersStatus_triggered(); @@ -154,10 +158,13 @@ private slots: void on_actionHelpContact_triggered(); void on_actionHelpDonate_triggered(); void on_actionHelpZiraCMS_triggered(); + void on_actionHelpZiraDevPack_triggered(); void on_actionHelpFAQ_triggered(); void on_actionCompileSass_triggered(); void on_actionExecuteFile_triggered(); void on_actionExecuteSelection_triggered(); + void on_actionStartPHPWebServer_triggered(); + void on_actionStopPHPWebServer_triggered(); void on_actionSplitTab_triggered(); void on_actionOpenContextMenu_triggered(); void fileBrowserOpen(QString file); @@ -183,6 +190,7 @@ private slots: void editorSplitReady(int index); void parseLintFinished(int tabIndex, QStringList errorTexts, QStringList errorLines, QString output); void execPHPFinished(int tabIndex, QString output); + void execPHPWebServerFinished(bool success, QString output); void parsePHPCSFinished(int tabIndex, QStringList errorTexts, QStringList errorLines); void parseMixedFinished(int tabIndex, ParsePHP::ParseResult result); void parseJSFinished(int tabIndex, ParseJS::ParseResult result); @@ -251,6 +259,7 @@ private slots: void outputTabSwitched(int index); void inputMethodVisibleChanged(); void checkScaleFactor(); + void installAndroidPackFinished(QString result); private: Ui::MainWindow *ui; Settings * settings; @@ -312,6 +321,8 @@ signals: void parseLint(int tabIndex, QString path); void execPHP(int tabIndex, QString path); void execSelection(int tabIndex, QString text); + void startPHPWebServer(QString path); + void stopPHPWebServer(); void parsePHPCS(int tabIndex, QString path); void parseMixed(int tabIndex, QString content); void parseJS(int tabIndex, QString content); @@ -322,6 +333,7 @@ signals: void serversCommand(QString command, QString pwd); void sassCommand(QString src, QString dst); void quickFind(QString dir, QString text, WordsMapList words, QStringList wordPrefixes); + void installAndroidPack(); }; #endif // MAINWINDOW_H diff --git a/include/parserworker.h b/include/parserworker.h index a760769..bc776e1 100644 --- a/include/parserworker.h +++ b/include/parserworker.h @@ -8,17 +8,21 @@ #define PARSERWORKER_H #include +#include #include "settings.h" #include "parsephp.h" #include "parsejs.h" #include "parsecss.h" #include "types.h" +extern const QString PHP_WEBSERVER_URI; + class ParserWorker : public QObject { Q_OBJECT public: explicit ParserWorker(Settings * settings, QObject *parent = nullptr); + ~ParserWorker(); protected: void parseProjectDir(QString dir, QStringList & files); void parseProjectFile(QString file, QVariantMap & map); @@ -26,6 +30,13 @@ protected: void searchInFile(QString file, QString searchText, bool searchOptionCase, bool searchOptionWord, bool searchOptionRegexp); void searchInFilesResultFound(QString file, QString lineText, int line, int symbol); void quickFindInDir(QString startDir, QString dir, QString text); + bool createAndroidDirectory(QDir rootDir, QString path); + bool setAndroidFilePermissions(QFile &f); + bool setAndroidFilePermissions(QString path); + bool installAndroidFile(QString fileName, QString installDir); + bool installAndroidPackFiles(); + bool isAndroidPackInstalled(); + void setAndroidBinPaths(); private: QString phpPath; QString gitPath; @@ -44,9 +55,15 @@ private: int quickResultsCount; bool quickBreaked; bool wantStop; + QString androidHomePath; + QStringList androidBinFiles; + QStringList androidGitFiles; + QStringList androidOtherFiles; + qint64 phpWebServerPid; signals: void lintFinished(int tabIndex, QStringList errorTexts, QStringList errorLines, QString output); void execPHPFinished(int tabIndex, QString output); + void execPHPWebServerFinished(bool success, QString output); void phpcsFinished(int tabIndex, QStringList errorTexts, QStringList errorLines); void parseMixedFinished(int tabIndex, ParsePHP::ParseResult result); void parseJSFinished(int tabIndex, ParseJS::ParseResult result); @@ -65,11 +82,14 @@ signals: void activateProgressInfo(QString text); void deactivateProgressInfo(); void updateProgressInfo(QString text); + void installAndroidPackFinished(QString result); public slots: void disable(); void lint(int tabIndex, QString path); void execPHP(int tabIndex, QString path); void execSelection(int tabIndex, QString text); + void startPHPWebServer(QString path); + void stopPHPWebServer(); void phpcs(int tabIndex, QString path); void parseMixed(int tabIndex, QString text); void parseJS(int tabIndex, QString text); @@ -81,6 +101,7 @@ public slots: void sassCommand(QString src, QString dst); void quickFind(QString dir, QString text, WordsMapList words, QStringList wordPrefixes); void cancelRequested(); + void installAndroidPack(); }; #endif // PARSERWORKER_H diff --git a/qrc/image.qrc b/qrc/image.qrc index 7afb830..264a186 100644 --- a/qrc/image.qrc +++ b/qrc/image.qrc @@ -57,5 +57,6 @@ resources/images/icons/edit-paste.png resources/images/icons/branch-close.png resources/images/icons/branch-open.png + resources/images/icons/home.png diff --git a/qrc/resources/images/icons/home.png b/qrc/resources/images/icons/home.png new file mode 100644 index 0000000..6d65cec Binary files /dev/null and b/qrc/resources/images/icons/home.png differ diff --git a/qrc/resources/styles/dark/icons/home.png b/qrc/resources/styles/dark/icons/home.png new file mode 100644 index 0000000..ce47184 Binary files /dev/null and b/qrc/resources/styles/dark/icons/home.png differ diff --git a/qrc/resources/styles/dark/style b/qrc/resources/styles/dark/style index 558deea..4ce4c88 100644 --- a/qrc/resources/styles/dark/style +++ b/qrc/resources/styles/dark/style @@ -1041,7 +1041,8 @@ QuickAccess QListView:focus border: none; } -QColorDialog +QColorDialog, +QInputDialog { border: 1px solid #006a7b; padding: 3px; diff --git a/qrc/resources/styles/light/icons/home.png b/qrc/resources/styles/light/icons/home.png new file mode 100644 index 0000000..cf9ef4f Binary files /dev/null and b/qrc/resources/styles/light/icons/home.png differ diff --git a/qrc/resources/styles/light/style b/qrc/resources/styles/light/style index 2c83194..0bbc2a8 100644 --- a/qrc/resources/styles/light/style +++ b/qrc/resources/styles/light/style @@ -1042,7 +1042,8 @@ QuickAccess QListView:focus border: none; } -QColorDialog +QColorDialog, +QInputDialog { border: 1px solid #8dbae8; padding: 3px; diff --git a/qrc/style.qrc b/qrc/style.qrc index 10559c4..2c86b9b 100644 --- a/qrc/style.qrc +++ b/qrc/style.qrc @@ -98,6 +98,7 @@ resources/styles/dark/icons/clear.png resources/styles/dark/icons/separator.png resources/styles/dark/icons/close.png + resources/styles/dark/icons/home.png resources/styles/light/icons/actionOpenFile.png @@ -146,5 +147,6 @@ resources/styles/light/icons/clear.png resources/styles/light/icons/separator.png resources/styles/light/icons/close.png + resources/styles/light/icons/home.png diff --git a/src/filebrowser.cpp b/src/filebrowser.cpp index 8de690c..890d4d4 100644 --- a/src/filebrowser.cpp +++ b/src/filebrowser.cpp @@ -61,6 +61,8 @@ void FileBrowser::initFileBrowser(QString homeDir) if (homeDir.size()==0) homeDir = "."; QAction * upAction = pathLine->addAction(Icon::get("up", QIcon(":icons/levelup.png")), QLineEdit::TrailingPosition); connect(upAction, SIGNAL(triggered(bool)), this, SLOT(upActionTriggered(bool))); + QAction * homeAction = pathLine->addAction(Icon::get("home", QIcon(":icons/home.png")), QLineEdit::LeadingPosition); + connect(homeAction, SIGNAL(triggered(bool)), this, SLOT(homeActionTriggered(bool))); buildFileBrowserTree(homeDir); connect(treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(fileBrowserExpanded(QTreeWidgetItem*))); connect(treeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(fileBrowserCollapsed(QTreeWidgetItem*))); @@ -202,6 +204,15 @@ void FileBrowser::upActionTriggered(bool) } } +void FileBrowser::homeActionTriggered(bool) +{ + QStringList stddirs = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); + if (stddirs.size()>0) { + pathLine->setText(stddirs.at(0)); + fileBrowserPathReturnPressed(); + } +} + void FileBrowser::fileBrowserContextMenuRequested(QPoint /*p*/) { //QTreeWidgetItem * item = treeWidget->itemAt(p); diff --git a/src/git.cpp b/src/git.cpp index be05e7c..808d212 100644 --- a/src/git.cpp +++ b/src/git.cpp @@ -16,6 +16,7 @@ const QString GIT_DIFF_COMMAND = "diff"; const QString GIT_COMMIT_COMMAND = "commit"; const QString GIT_PUSH_COMMAND = "push"; const QString GIT_PULL_COMMAND = "pull"; +const QString GIT_CLONE_COMMAND = "clone"; Git::Git(Settings * settings, QObject *parent) : QObject(parent) { @@ -138,6 +139,26 @@ void Git::pullOriginMaster(QString path) emit runGitCommand(path, "pull", QStringList() << "origin" << "master"); } +void Git::initialize(QString path) +{ + emit runGitCommand(path, "init", QStringList()); +} + +void Git::addRemoteURL(QString path, QString url) +{ + emit runGitCommand(path, "remote", QStringList() << "add" << "origin" << url); +} + +void Git::changeRemoteURL(QString path, QString url) +{ + emit runGitCommand(path, "remote", QStringList() << "set-url" << "origin" << url); +} + +void Git::clone(QString path, QString url) +{ + emit runGitCommand(path, "clone", QStringList() << url); +} + void Git::showAnnotation(QString path, QString fileName, bool outputResult, bool silent) { emit runGitCommand(path, "blame", QStringList() << "--line-porcelain" << fileName, outputResult, silent); diff --git a/src/helper.cpp b/src/helper.cpp index fb76e1b..58e9ee5 100644 --- a/src/helper.cpp +++ b/src/helper.cpp @@ -36,6 +36,7 @@ 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"; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4f30d82..4607028 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -328,6 +328,8 @@ MainWindow::MainWindow(QWidget *parent) : connect(this, SIGNAL(parseLint(int,QString)), parserWorker, SLOT(lint(int,QString))); connect(this, SIGNAL(execPHP(int,QString)), parserWorker, SLOT(execPHP(int,QString))); connect(this, SIGNAL(execSelection(int,QString)), parserWorker, SLOT(execSelection(int,QString))); + connect(this, SIGNAL(startPHPWebServer(QString)), parserWorker, SLOT(startPHPWebServer(QString))); + connect(this, SIGNAL(stopPHPWebServer()), parserWorker, SLOT(stopPHPWebServer())); connect(this, SIGNAL(parsePHPCS(int,QString)), parserWorker, SLOT(phpcs(int,QString))); connect(this, SIGNAL(parseMixed(int,QString)), parserWorker, SLOT(parseMixed(int,QString))); connect(this, SIGNAL(parseJS(int,QString)), parserWorker, SLOT(parseJS(int,QString))); @@ -338,9 +340,11 @@ MainWindow::MainWindow(QWidget *parent) : connect(this, SIGNAL(serversCommand(QString, QString)), parserWorker, SLOT(serversCommand(QString,QString))); connect(this, SIGNAL(sassCommand(QString, QString)), parserWorker, SLOT(sassCommand(QString,QString))); connect(this, SIGNAL(quickFind(QString, QString, WordsMapList, QStringList)), parserWorker, SLOT(quickFind(QString, QString, WordsMapList, QStringList))); + connect(this, SIGNAL(installAndroidPack()), parserWorker, SLOT(installAndroidPack())); connect(progressInfo, SIGNAL(cancelTriggered()), parserWorker, SLOT(cancelRequested())); connect(parserWorker, SIGNAL(lintFinished(int,QStringList,QStringList,QString)), this, SLOT(parseLintFinished(int,QStringList,QStringList,QString))); connect(parserWorker, SIGNAL(execPHPFinished(int,QString)), this, SLOT(execPHPFinished(int,QString))); + connect(parserWorker, SIGNAL(execPHPWebServerFinished(bool,QString)), this, SLOT(execPHPWebServerFinished(bool,QString))); connect(parserWorker, SIGNAL(phpcsFinished(int,QStringList,QStringList)), this, SLOT(parsePHPCSFinished(int,QStringList,QStringList))); connect(parserWorker, SIGNAL(parseMixedFinished(int,ParsePHP::ParseResult)), this, SLOT(parseMixedFinished(int,ParsePHP::ParseResult))); connect(parserWorker, SIGNAL(parseJSFinished(int,ParseJS::ParseResult)), this, SLOT(parseJSFinished(int,ParseJS::ParseResult))); @@ -359,6 +363,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(parserWorker, SIGNAL(activateProgressInfo(QString)), this, SLOT(activateProgressInfo(QString))); connect(parserWorker, SIGNAL(deactivateProgressInfo()), this, SLOT(deactivateProgressInfo())); connect(parserWorker, SIGNAL(updateProgressInfo(QString)), this, SLOT(updateProgressInfo(QString))); + connect(parserWorker, SIGNAL(installAndroidPackFinished(QString)), this, SLOT(installAndroidPackFinished(QString))); parserThread.start(); tmpDisableParser = false; @@ -692,6 +697,11 @@ void MainWindow::closeEvent(QCloseEvent *event) emit disableWorker(); // save project project->save(editorTabs->getOpenTabFiles(), editorTabs->getOpenTabLines(), editorTabs->getCurrentTabIndex(), ui->todoEdit->toPlainText()); + if (settings->get("devpack_install_silent") == "no") { + std::unordered_map sMap; + sMap["devpack_install_silent"] = "yes"; + settings->change(sMap); + } settings->save(); // save wnd geometry & state QSettings windowSettings; @@ -754,13 +764,17 @@ void MainWindow::menuToolsOnShow() { bool sassEnabled = false; bool execEnabled = false; + bool execSelectionEnabled = false; Editor * textEditor = getActiveEditor(); - if (textEditor != nullptr && !textEditor->isModified()) { + if (textEditor != nullptr) { QString ext = textEditor->getFileExtension().toLower(); - if (ext == "scss" || ext == "sass") { + if (!textEditor->isModified() && (ext == "scss" || ext == "sass")) { sassEnabled = true; } else if (ext == "php") { - execEnabled = true; + if (!textEditor->isModified()) execEnabled = true; + if (textEditor->textCursor().selectedText().size() > 0) { + execSelectionEnabled = true; + } } } QList toolsActions = ui->menuTools->actions(); @@ -770,10 +784,7 @@ void MainWindow::menuToolsOnShow() } else if (action->objectName() == "actionExecuteFile") { action->setEnabled(execEnabled); } else if (action->objectName() == "actionExecuteSelection") { - if (execEnabled && textEditor->textCursor().selectedText().size() == 0) { - execEnabled = false; - } - action->setEnabled(execEnabled); + action->setEnabled(execSelectionEnabled); } } } @@ -946,6 +957,9 @@ void MainWindow::enableActionsForOpenProject() void MainWindow::projectLoadOnStart() { showWelcomeScreen(); + #if defined(Q_OS_ANDROID) + emit installAndroidPack(); + #endif QSettings windowSettings; QString projectPath = windowSettings.value("project_path").toString(); if (projectPath.size() > 0 && Helper::folderExists(projectPath) && project->exists(projectPath)) { @@ -954,6 +968,9 @@ void MainWindow::projectLoadOnStart() } void MainWindow::openFromArgs() { + #if defined(Q_OS_ANDROID) + emit installAndroidPack(); + #endif if (args.length() <= 1) return; QStringList files; for (int i=1; ioutputDockWidget->isVisible()) ui->outputDockWidget->show(); ui->outputTabWidget->setCurrentIndex(OUTPUT_TAB_RESULTS_INDEX); ui->outputEdit->clear(); - QString cmdStr = "php -n -d max_execution_time=30 -f "+fileName; + QString cmdStr = "php -d max_execution_time=30 -f "+fileName; ui->outputEdit->setHtml(Servers::highlightServersCommand(cmdStr, settings)); emit execPHP(textEditor->getTabIndex(), fileName); @@ -1434,12 +1451,34 @@ void MainWindow::on_actionExecuteSelection_triggered() if (!ui->outputDockWidget->isVisible()) ui->outputDockWidget->show(); ui->outputTabWidget->setCurrentIndex(OUTPUT_TAB_RESULTS_INDEX); ui->outputEdit->clear(); - QString cmdStr = "php -n -d max_execution_time=30 -r '"+text+"'"; + QString cmdStr = "php -d max_execution_time=30 -r '"+text+"'"; ui->outputEdit->setHtml(Servers::highlightServersCommand(cmdStr, settings)); emit execSelection(textEditor->getTabIndex(), code); } +void MainWindow::on_actionStartPHPWebServer_triggered() +{ + emit startPHPWebServer(filebrowser->getRootPath()); +} + +void MainWindow::on_actionStopPHPWebServer_triggered() +{ + emit stopPHPWebServer(); +} + +void MainWindow::execPHPWebServerFinished(bool success, QString output) +{ + hideQAPanel(); + if (!ui->outputDockWidget->isVisible()) ui->outputDockWidget->show(); + ui->outputTabWidget->setCurrentIndex(OUTPUT_TAB_RESULTS_INDEX); + ui->outputEdit->clear(); + ui->outputEdit->setHtml(Servers::highlightServersCommand(output, settings)); + if (success) { + QDesktopServices::openUrl(QUrl("http://" + PHP_WEBSERVER_URI)); + } +} + void MainWindow::execPHPFinished(int tabIndex, QString output) { Editor * textEditor = getActiveEditor(); @@ -1569,6 +1608,35 @@ void MainWindow::on_actionGitPull_triggered() git->pullOriginMaster(getGitWorkingDir()); } +void MainWindow::on_actionGitInitializeRepository_triggered() +{ + git->initialize(getGitWorkingDir()); +} + +void MainWindow::on_actionGitAddRemoteURL_triggered() +{ + bool ok; + QString url = QInputDialog::getText(this, tr("Enter URL"), tr("Enter remote URL.\nNote: you might want to add a username and password to repository URI\n(https://username:password@github.com/username/repository.git)"), QLineEdit::Normal, "", &ok); + if (!ok || url.size() == 0) return; + git->addRemoteURL(getGitWorkingDir(), url); +} + +void MainWindow::on_actionGitChangeRemoteURL_triggered() +{ + bool ok; + QString url = QInputDialog::getText(this, tr("Enter URL"), tr("Enter remote URL.\nNote: you might want to add a username and password to repository URI\n(https://username:password@github.com/username/repository.git)"), QLineEdit::Normal, "", &ok); + if (!ok || url.size() == 0) return; + git->changeRemoteURL(getGitWorkingDir(), url); +} + +void MainWindow::on_actionGitCloneRepository_triggered() +{ + bool ok; + QString url = QInputDialog::getText(this, tr("Enter URL"), tr("Enter repository URL.\nNote: you might want to add a username and password to repository URI\n(https://username:password@github.com/username/repository.git)"), QLineEdit::Normal, "", &ok); + if (!ok || url.size() == 0) return; + git->clone(getGitWorkingDir(), url); +} + void MainWindow::runGitCommand(QString path, QString command, QStringList attrs, bool outputResult, bool silent) { if (!gitCommandsEnabled) return; @@ -1638,6 +1706,8 @@ void MainWindow::gitCommandFinished(QString command, QString output, bool output gitAnnotationRequested(textEditor->getFileName()); gitDiffUnifiedRequested(textEditor->getFileName()); } + } else if (command == GIT_PULL_COMMAND || command == GIT_CLONE_COMMAND) { + filebrowser->refreshFileBrowserDirectory(filebrowser->getRootPath()); } } @@ -1684,6 +1754,18 @@ void MainWindow::gitDiffUnifiedRequested(QString path) git->showUncommittedDiffCurrentUnified(getGitWorkingDir(), path, false, true); } +void MainWindow::installAndroidPackFinished(QString result) +{ + if (settings->get("devpack_install_silent") == "yes") return; + hideQAPanel(); + if (result.size() > 0) { + if (!ui->outputDockWidget->isVisible()) ui->outputDockWidget->show(); + ui->outputTabWidget->setCurrentIndex(OUTPUT_TAB_RESULTS_INDEX); + ui->outputEdit->clear(); + ui->outputEdit->setHtml(result); + } +} + void MainWindow::on_actionSettings_triggered() { SettingsDialog dialog(settings, this); @@ -1729,6 +1811,11 @@ void MainWindow::on_actionHelpZiraCMS_triggered() QDesktopServices::openUrl(QUrl(AUTHOR_CMS_URL)); } +void MainWindow::on_actionHelpZiraDevPack_triggered() +{ + QDesktopServices::openUrl(QUrl(AUTHOR_DEVPACK_URL)); +} + void MainWindow::resetLastSearchParams() { lastSearchText = ""; diff --git a/src/parserworker.cpp b/src/parserworker.cpp index 841df9a..f79e2b7 100644 --- a/src/parserworker.cpp +++ b/src/parserworker.cpp @@ -10,78 +10,27 @@ #include #include #include +#include #include "helper.h" #include "project.h" #include "servers.h" #include "git.h" +const QString ANDROID_PACK_SUBDIR = "packages"; +const QString ANDROID_BIN_DIR = "bin"; +const QString ANDROID_GIT_CORE_DIR = "git-core"; +const QString ANDROID_GIT_TEMPLATES_DIR = "templates"; +const QString ANDROID_PHP_TMP_DIR = "tmp"; +const QString ANDROID_INSTALL_RESULT_LINE_TEMPLATE = "

%1

"; +const QString PHP_WEBSERVER_URI = "127.0.0.1:8000"; + ParserWorker::ParserWorker(Settings * settings, QObject *parent) : QObject(parent) -{ - // php path +{ phpPath = ""; - QString phpPathStr = QString::fromStdString(settings->get("parser_php_path")); - if (phpPathStr.size() == 0) { - QProcess process(this); - process.start("which", QStringList() << "php"); - if (!process.waitForFinished()) return; - QByteArray result = process.readAllStandardOutput(); - phpPathStr = QString(result).trimmed(); - } - if (phpPathStr.size() > 0 && Helper::fileOrFolderExists(phpPathStr)) { - phpPath = phpPathStr; - } - // git path gitPath = ""; - QString gitPathStr = QString::fromStdString(settings->get("parser_git_path")); - if (gitPathStr.size() == 0) { - QProcess process(this); - process.start("which", QStringList() << "git"); - if (!process.waitForFinished()) return; - QByteArray result = process.readAllStandardOutput(); - gitPathStr = QString(result).trimmed(); - } - if (gitPathStr.size() > 0 && Helper::fileOrFolderExists(gitPathStr)) { - gitPath = gitPathStr; - } - // bash path bashPath = ""; - QString bashPathStr = QString::fromStdString(settings->get("parser_bash_path")); - if (bashPathStr.size() == 0) { - QProcess process(this); - process.start("which", QStringList() << "bash"); - if (!process.waitForFinished()) return; - QByteArray result = process.readAllStandardOutput(); - bashPathStr = QString(result).trimmed(); - } - if (bashPathStr.size() > 0 && Helper::fileOrFolderExists(bashPathStr)) { - bashPath = bashPathStr; - } - // sassc path sasscPath = ""; - QString sasscPathStr = QString::fromStdString(settings->get("parser_sassc_path")); - if (sasscPathStr.size() == 0) { - QProcess process(this); - process.start("which", QStringList() << "sassc"); - if (!process.waitForFinished()) return; - QByteArray result = process.readAllStandardOutput(); - sasscPathStr = QString(result).trimmed(); - } - if (sasscPathStr.size() > 0 && Helper::fileOrFolderExists(sasscPathStr)) { - sasscPath = sasscPathStr; - } - // phpcs path phpcsPath = ""; - QString phpcsPathStr = QString::fromStdString(settings->get("parser_phpcs_path")); - if (phpcsPathStr.size() == 0) { - QProcess process(this); - process.start("which", QStringList() << "phpcs"); - if (!process.waitForFinished()) return; - QByteArray result = process.readAllStandardOutput(); - phpcsPathStr = QString(result).trimmed(); - } - if (phpcsPathStr.size() > 0 && Helper::fileOrFolderExists(phpcsPathStr)) { - phpcsPath = phpcsPathStr; - } phpcsStandard = QString::fromStdString(settings->get("parser_phpcs_standard")); if (phpcsStandard.size() == 0) phpcsStandard = "PEAR"; phpcsErrorSeverity = std::stoi(settings->get("parser_phpcs_error_severity")); @@ -97,6 +46,95 @@ ParserWorker::ParserWorker(Settings * settings, QObject *parent) : QObject(paren quickResultsCount = 0; quickBreaked = false; wantStop = false; + phpWebServerPid = 0; + + // android pack + androidHomePath=""; + androidBinFiles << "php" << "git" << "git-receive-pack" << "git-upload-archive" << "git-upload-pack" << "sassc" << "termux-elf-cleaner"; + androidGitFiles << "git" << "git-remote" << "git-remote-http" << "git-remote-https" << "git-receive-pack" << "git-upload-archive" << "git-upload-pack"; + androidOtherFiles << "php.ini" << "gitconfig" << "cacert.pem"; + #if defined(Q_OS_ANDROID) + QStringList stddirs = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); + if (stddirs.size()>0) androidHomePath = stddirs.at(0); + #endif + + // php path + QString phpPathStr = QString::fromStdString(settings->get("parser_php_path")); + if (phpPathStr.size() == 0) { + QProcess process(this); + process.start("which", QStringList() << "php"); + if (process.waitForFinished()) { + QByteArray result = process.readAllStandardOutput(); + phpPathStr = QString(result).trimmed(); + } + } + if (phpPathStr.size() > 0 && Helper::fileOrFolderExists(phpPathStr)) { + phpPath = phpPathStr; + } + + // git path + QString gitPathStr = QString::fromStdString(settings->get("parser_git_path")); + if (gitPathStr.size() == 0) { + QProcess process(this); + process.start("which", QStringList() << "git"); + if (process.waitForFinished()) { + QByteArray result = process.readAllStandardOutput(); + gitPathStr = QString(result).trimmed(); + } + } + if (gitPathStr.size() > 0 && Helper::fileOrFolderExists(gitPathStr)) { + gitPath = gitPathStr; + } + + // bash path + QString bashPathStr = QString::fromStdString(settings->get("parser_bash_path")); + if (bashPathStr.size() == 0) { + QProcess process(this); + process.start("which", QStringList() << "bash"); + if (process.waitForFinished()) { + QByteArray result = process.readAllStandardOutput(); + bashPathStr = QString(result).trimmed(); + } + } + if (bashPathStr.size() > 0 && Helper::fileOrFolderExists(bashPathStr)) { + bashPath = bashPathStr; + } + + // sassc path + QString sasscPathStr = QString::fromStdString(settings->get("parser_sassc_path")); + if (sasscPathStr.size() == 0) { + QProcess process(this); + process.start("which", QStringList() << "sassc"); + if (process.waitForFinished()) { + QByteArray result = process.readAllStandardOutput(); + sasscPathStr = QString(result).trimmed(); + } + } + if (sasscPathStr.size() > 0 && Helper::fileOrFolderExists(sasscPathStr)) { + sasscPath = sasscPathStr; + } + + // phpcs path + QString phpcsPathStr = QString::fromStdString(settings->get("parser_phpcs_path")); + if (phpcsPathStr.size() == 0) { + QProcess process(this); + process.start("which", QStringList() << "phpcs"); + if (process.waitForFinished()) { + QByteArray result = process.readAllStandardOutput(); + phpcsPathStr = QString(result).trimmed(); + } + } + if (phpcsPathStr.size() > 0 && Helper::fileOrFolderExists(phpcsPathStr)) { + phpcsPath = phpcsPathStr; + } +} + +ParserWorker::~ParserWorker() +{ + disable(); + if (phpWebServerPid != 0) { + stopPHPWebServer(); + } } void ParserWorker::disable() @@ -137,7 +175,7 @@ void ParserWorker::execPHP(int tabIndex, QString path) return; } QProcess process(this); - process.start(phpPath, QStringList() << "-n" << "-d" << "max_execution_time=30" << "-f" << path); + process.start(phpPath, QStringList() << "-d" << "max_execution_time=30" << "-f" << path); if (!process.waitForFinished(60000)) return; QString result = QString(process.readAllStandardOutput()); if (result.size() == 0) result = QString(process.readAllStandardError()); @@ -152,7 +190,7 @@ void ParserWorker::execSelection(int tabIndex, QString text) return; } QProcess process(this); - process.start(phpPath, QStringList() << "-n" << "-d" << "max_execution_time=30" << "-r" << text); + process.start(phpPath, QStringList() << "-d" << "max_execution_time=30" << "-r" << text); if (!process.waitForFinished(60000)) return; QString result = QString(process.readAllStandardOutput()); if (result.size() == 0) result = QString(process.readAllStandardError()); @@ -160,6 +198,39 @@ void ParserWorker::execSelection(int tabIndex, QString text) emit execPHPFinished(tabIndex, output); } +void ParserWorker::startPHPWebServer(QString path) +{ + if (phpPath.size() == 0) { + emit message(tr("PHP executable not found.")); + return; + } + if (path.size() == 0 || !Helper::folderExists(path)) return; + if (phpWebServerPid != 0) return; + QProcess process(this); + process.setWorkingDirectory(path); + process.setProgram(phpPath); + process.setArguments(QStringList() << "-S" << PHP_WEBSERVER_URI); + process.setStandardOutputFile(QProcess::nullDevice()); + process.setStandardErrorFile(QProcess::nullDevice()); + if (process.startDetached(&phpWebServerPid)) { + emit execPHPWebServerFinished(true, tr("PHP web-server started.")); + } else { + emit execPHPWebServerFinished(false, tr("Could not start PHP web-server.")); + } +} + +void ParserWorker::stopPHPWebServer() +{ + if (phpWebServerPid == 0) return; + if (QProcess::startDetached("kill", {QString::number(phpWebServerPid)})) { + phpWebServerPid = 0; + // success = false (do not want browser to be opened) + emit execPHPWebServerFinished(false, tr("PHP web-server stopped.")); + } else { + emit execPHPWebServerFinished(false, tr("Could not stop PHP web-server.")); + } +} + void ParserWorker::phpcs(int tabIndex, QString path) { if (phpcsPath.size() == 0) return; //silence @@ -433,7 +504,7 @@ void ParserWorker::gitCommand(QString path, QString command, QStringList attrs, } if (path.size() == 0 || !Helper::folderExists(path)) return; bool useProgress = false; - if (command == GIT_PUSH_COMMAND || command == GIT_PULL_COMMAND) useProgress = true; + if (command == GIT_PUSH_COMMAND || command == GIT_PULL_COMMAND || command == GIT_CLONE_COMMAND) useProgress = true; if (useProgress && !isBusy) emit activateProgress(); QProcess process(this); process.setWorkingDirectory(path); @@ -610,3 +681,182 @@ void ParserWorker::cancelRequested() { wantStop = true; } + +bool ParserWorker::createAndroidDirectory(QDir rootDir, QString path) +{ + if (!rootDir.mkpath(path)) return false; + if (!setAndroidFilePermissions(path)) return false; + return true; +} + +bool ParserWorker::setAndroidFilePermissions(QFile &f) +{ + return f.setPermissions( + QFileDevice::ReadOwner | + QFileDevice::ReadGroup | + QFileDevice::ReadOther | + QFileDevice::WriteOwner | + QFileDevice::WriteGroup | + QFileDevice::WriteOther | + QFileDevice::ExeOwner | + QFileDevice::ExeGroup | + QFileDevice::ExeOther + ); +} + +bool ParserWorker::setAndroidFilePermissions(QString path) +{ + QFile f(path); + return setAndroidFilePermissions(f); +} + +bool ParserWorker::installAndroidFile(QString fileName, QString installDir) +{ + QFile f("assets:/"+fileName); + if (!f.exists()) return false; + + QFile fi(installDir+"/"+fileName); + if (fi.exists()) fi.remove(); + + if (!f.copy(installDir+"/"+fileName)) return false; + + QFile pf(installDir+"/"+fileName); + if (!setAndroidFilePermissions(pf)) return false; + + return true; +} + +void ParserWorker::installAndroidPack() +{ + if (androidHomePath.size() == 0) return; + if (isAndroidPackInstalled()) { + setAndroidBinPaths(); + return; + } + + emit installAndroidPackFinished(ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(tr("Installing development pack..."))); + + if (installAndroidPackFiles()) { + QString androidPackInstallPath = androidHomePath + "/" + ANDROID_PACK_SUBDIR; + QString result = ""; + QProcess phpProcess(this); + phpProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/php", QStringList() << "-v"); + if (phpProcess.waitForFinished()) { + QByteArray phpResult = phpProcess.readAllStandardError(); + phpResult += phpProcess.readAllStandardOutput(); + result += ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(QString(phpResult).trimmed().split("\n").at(0)); + } + QProcess gitProcess(this); + gitProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/git", QStringList() << "--version"); + if (gitProcess.waitForFinished()) { + QByteArray gitResult = gitProcess.readAllStandardError(); + gitResult += gitProcess.readAllStandardOutput(); + result += ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(QString(gitResult).trimmed().split("\n").at(0)); + } + QProcess sasscProcess(this); + sasscProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/sassc", QStringList() << "--version"); + if (sasscProcess.waitForFinished()) { + QByteArray sasscResult = sasscProcess.readAllStandardError(); + sasscResult += sasscProcess.readAllStandardOutput(); + result += ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(QString(sasscResult).trimmed().split("\n").at(0)); + } + setAndroidBinPaths(); + emit installAndroidPackFinished(ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(tr("Development pack successfully installed."))+result); + } else { + emit installAndroidPackFinished(ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(tr("Installation failed :("))); + } +} + +bool ParserWorker::installAndroidPackFiles() +{ + // creating directories + QDir androidHomeDir(androidHomePath); + QFileInfo androidHomeDirInfo(androidHomePath); + if (!androidHomeDirInfo.isWritable() || !androidHomeDirInfo.isExecutable()) return false; + + if (!createAndroidDirectory(androidHomeDir, androidHomePath + "/" + ANDROID_PACK_SUBDIR)) return false; + QString androidPackInstallPath = androidHomePath + "/" + ANDROID_PACK_SUBDIR; + + QDir d(androidPackInstallPath); + if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_BIN_DIR)) return false; + if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR)) return false; + if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_GIT_TEMPLATES_DIR)) return false; + if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_PHP_TMP_DIR)) return false; + + // copying files + for (QString fileName : androidBinFiles) { + if (!installAndroidFile(fileName, androidPackInstallPath+"/"+ANDROID_BIN_DIR)) return false; + } + + for (QString fileName : androidGitFiles) { + if (!installAndroidFile(fileName, androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR)) return false; + } + + for (QString fileName : androidOtherFiles) { + if (!installAndroidFile(fileName, androidPackInstallPath)) return false; + } + + // .gitconfig + QFile gitConfigHidden(androidPackInstallPath+"/.gitconfig"); + if (gitConfigHidden.exists()) gitConfigHidden.remove(); + + QFile gitConfig(androidPackInstallPath+"/gitconfig"); + if (gitConfig.exists()) gitConfig.rename(androidPackInstallPath+"/.gitconfig"); + + // run termux-elf-cleaner + QStringList cleanFiles; + for (QString fileName : androidBinFiles) { + if (fileName == "termux-elf-cleaner") continue; + cleanFiles << androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+fileName; + } + + for (QString fileName : androidGitFiles) { + cleanFiles << androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR+"/"+fileName; + } + + QProcess cleanProcess(this); + cleanProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/termux-elf-cleaner", cleanFiles); + if (!cleanProcess.waitForFinished()) return false; + + return true; +} + +bool ParserWorker::isAndroidPackInstalled() +{ + if (androidHomePath.size() == 0) return false; + + for (QString fileName : androidBinFiles) { + QFileInfo fileInfo(androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/"+fileName); + if (!fileInfo.exists() || !fileInfo.isExecutable()) return false; + } + + for (QString fileName : androidGitFiles) { + QFileInfo fileInfo(androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR+"/"+fileName); + if (!fileInfo.exists() || !fileInfo.isExecutable()) return false; + } + + for (QString fileName : androidOtherFiles) { + if (fileName == "gitconfig") fileName = ".gitconfig"; + QFileInfo fileInfo(androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+fileName); + if (!fileInfo.exists() || !fileInfo.isReadable()) return false; + } + + return true; +} + +void ParserWorker::setAndroidBinPaths() +{ + if (androidHomePath.size() == 0) return; + if (phpPath.size() == 0) { + QString phpPathStr = androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/php"; + if (Helper::fileOrFolderExists(phpPathStr)) phpPath = phpPathStr; + } + if (gitPath.size() == 0) { + QString gitPathStr = androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/git"; + if (Helper::fileOrFolderExists(gitPathStr)) gitPath = gitPathStr; + } + if (sasscPath.size() == 0) { + QString sasscPathStr = androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/sassc"; + if (Helper::fileOrFolderExists(sasscPathStr)) sasscPath = sasscPathStr; + } +} diff --git a/src/settings.cpp b/src/settings.cpp index 9c22ac7..6289e54 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -118,7 +118,8 @@ Settings::Settings(QObject * parent) : QObject(parent) {"custom_snippets_file", ""}, {"scale_auto", "yes"}, {"scale_factor", "100"}, - {"scale_factor_unchecked", "no"} + {"scale_factor_unchecked", "no"}, + {"devpack_install_silent", "no"} }; } diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 35d97b4..51953b4 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -139,6 +139,19 @@ &Git + + + Repository + + + + + + + + + + @@ -175,6 +188,9 @@ + + + @@ -206,6 +222,7 @@ + @@ -1176,6 +1193,41 @@ Context menu + + + Initialize + + + + + Add remote URL + + + + + Change remote URL + + + + + Clone + + + + + Start PHP web-server + + + + + Stop PHP web-server + + + + + Zira DevPack + +