/
breton75
/
modus2
Обзор
Документация
Войти
/
breton75
/
modus2
Код
Запросы
0
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
utils/mdeditor/src/mainwindow.cpp
608 строк
19 KB
breton75
Update master_logger.cpp, io.pro, and 558 more files...
16 июл 2025, 18:45
16 июл 2025, 18:45
7270658
Код
Авторство
О чём код?
#include "mainwindow.h" #include "ui_mainwindow.h" SvTextEditor* EDITCONFIG_UI; SvTextEditor::SvTextEditor(const Params& params, QWidget *parent) : QMainWindow(parent), ui(new Ui::SvTextEditorWindow), m_params(params), m_current_search_pos(0), statusImages(QMap<Status, StatusImage>()) { ui->setupUi(this); statusImages.insert(Status::ReadOnly, StatusImage("Только чтение", QImage(":/superMonoBasic/icons/superMonoBasic/lock.png").scaled(24,24))); statusImages.insert(Status::Editable, StatusImage("Редактирование доступно", QImage(":/superMonoBasic/icons/superMonoBasic/unlock.png").scaled(24,24))); statusImages.insert(Status::SecurityGood, StatusImage("Данные достоверны", QImage(":/superMonoBasic/icons/superMonoBasic/green/shield.png").scaled(24,24))); statusImages.insert(Status::SecurityBad, StatusImage("Данные могут быть не достоверны", QImage(":/superMonoBasic/icons/superMonoBasic/exclamation.png").scaled(24,24))); // ui->labelCurrntFile->setParent(ui->statusbar); // ui->labelDocumentInfo->setParent(ui->statusbar); ui->statusbar->addWidget(ui->labelCurrntFile, 50); ui->statusbar->addWidget(ui->labelDocumentInfo); ui->statusbar->addWidget(ui->labelDocumentState); ui->statusbar->addWidget(ui->labelSecurityInfo); m_mdHightLighter = new sv::SvModusHighLighter(ui->textBrowser->document()); try { if(!m_params.project_file.isEmpty()) { // открываем и читаем файл проекта QFile project_file(m_params.project_file); if(!project_file.open(QIODevice::ReadOnly)) throw SvException(QString("Ошибка чтения проекта %1\n%2").arg(m_params.project_file).arg(project_file.errorString())); /* загружаем json проекта в QJSonDocument */ QByteArray pba = project_file.readAll(); project_file.close(); QJsonParseError parse_error; QJsonDocument pjd = QJsonDocument::fromJson(pba, &parse_error); if(parse_error.error != QJsonParseError::NoError) throw SvException(QString("Ошибка разбора проекта %1\n%2").arg(m_params.project_file).arg(parse_error.errorString())); QJsonObject project_json = pjd.object(); QString usersPath; if(project_json.contains(P_USERS)) usersPath = m_params.search_path.absoluteFilePath(project_json.value(P_USERS).toString()); else usersPath = m_params.search_path.absoluteFilePath(QString(".%1_users.bin").arg(m_params.project_file)); if(project_json.contains(P_FILES)) m_hashPath = m_params.search_path.absoluteFilePath(project_json.value(P_FILES).toString()); else m_hashPath = m_params.search_path.absoluteFilePath(QString(".%1_files.hash").arg(m_params.project_file)); // m_users.clear(); // QString errorString; // modus::getUsers(usersPath, m_users, errorString); } readFile(params.file, params.search_path); // имеется какой то баг в функции setReadOnly. поэтому так. // Если идет вызов setReadOnly(false), то почему то перестают рабоатать ссылки if(params.readonly) ui->textBrowser->setReadOnly(params.readonly); ui->textBrowser->setUndoRedoEnabled(!params.readonly); ui->actionNew->setEnabled(!params.readonly); ui->actionOpen->setEnabled(!params.readonly); ui->actionNew->setVisible(!params.mdmode); ui->actionOpen->setVisible(!params.mdmode); ui->textBrowser->setOpenLinks(false); connect(ui->textBrowser, &QTextBrowser::anchorClicked, this, &SvTextEditor::openFileUrl); connect(ui->actionSearchForward, &QAction::triggered, this, &SvTextEditor::search); connect(ui->actionSearchBackward, &QAction::triggered, this, &SvTextEditor::search); connect(ui->lineSearchWhat, &QLineEdit::textChanged, this, &SvTextEditor::search); connect(ui->bnCaseSencitive, &QPushButton::clicked, this, &SvTextEditor::search); connect(ui->bnSearchWholeWord, &QPushButton::clicked, this, &SvTextEditor::search); connect(ui->bnSearchRegExp, &QPushButton::clicked, this, &SvTextEditor::search); ui->dockSearchWidget->setVisible(false); // connect(ui->dockSearchWidget, &QDockWidget::visibilityChanged, [](){ qDebug() << "QDockWidget::visibilityChanged"; }); } catch(const SvException& e) { qDebug()<<e.error; } } void SvTextEditor::readFile(const QString& file, const QDir& path) { m_params.file = file; m_params.search_path = path; ui->textBrowser->clear(); // bool checkSecurity = false; if(!m_params.file.trimmed().isEmpty()) { QFile f(m_params.search_path.absoluteFilePath(m_params.file)); if(!f.open(QFile::ReadOnly)) { QMessageBox::critical(this, "Ошибка", QString("Ошибка при открытии файла %1\n%2").arg(m_params.file).arg(f.errorString())); return; } QString text = QString(f.readAll()); f.close(); // if(!m_filesPath.isEmpty()) // { // QString errorString; // if(!modus::checkFileSecurity(m_hashPath, m_params.search_path.absoluteFilePath(m_params.file),errorString)) // securityError(errorString); // else // checkSecurity = true; // } // else // { // QFile fhash(m_filesPath); // if(fhash.open(QFile::ReadOnly)) // { // bool fileFind = false; // bool hasError = false; // QString hashText = QString(qUncompress(fhash.readAll())); // for(QString line: hashText.split('\n')) // { // if(line.isEmpty()) // continue; // QList<QString> ls; // ls = line.split(';'); // if(ls.size() != 2) // { // securityError("В хэштаблице неправильный формат"); // hasError = true; // break; // } // else // { // if(ls[0] == m_params.file) // { // if(!(checkSecurity = modus::checkFile(ls[0], ls[1]))) // securityError(QString("Файл,\n %1\nбыл изменен").arg(ls[0])); // fileFind = true; // break; // } // } // } // if(!fileFind && !hasError) // securityError(QString("Не удалось найти файл\n%1\nв хэштаблице").arg(m_params.file)); // fhash.close(); // } // else{ // securityError("Не удалось открыть хэштаблицу"); // } // } ui->textBrowser->setText(text); // ищем в тексте html слово "file": чтобы пометить имена файлов как ссылки text = ui->textBrowser->toHtml(); int index = text.indexOf(QRegExp(""file"[\\s]{0,}:[\\s]{0,}"", Qt::CaseInsensitive)); while(index > 0) { index = text.indexOf(':', index); int i0 = text.indexOf(""", index); int i1 = text.indexOf(""", i0 + 6); if(i0 > 0 && i0 < i1) { QString href = text.mid(i0 + 6, i1 - i0 - 6); QString url = QString("<a href=\"%1\">\"%1\"</a>").arg(href); text.replace(i0, i1 - i0 + 6, url); } index = text.indexOf(QRegExp(""file"[\\s]{0,}:[\\s]{0,}"", Qt::CaseInsensitive), i1); } ui->textBrowser->setHtml(text); } ui->textBrowser->document()->setModified(false); ui->actionSave->setEnabled(false); ui->actionCopy->setEnabled(false); ui->actionCut->setEnabled(false); ui->actionPaste->setEnabled(false); ui->actionUndo->setEnabled(false); ui->actionRedo->setEnabled(false); // ui->actionFind->setEnabled(false); ui->labelCurrntFile->setText(QString(P_STATUS) .arg(m_params.search_path.absoluteFilePath(m_params.file)) .arg("")); if(m_params.readonly) setStatusLabel(ui->labelDocumentState, statusImages.value(Status::ReadOnly)); else setStatusLabel(ui->labelDocumentState, statusImages.value(Status::Editable)); // проверка целостности файла // if(checkSecurity) // setStatusLabel(ui->labelSecurityInfo, statusImages.value(Status::SecurityGood)); // else // setStatusLabel(ui->labelSecurityInfo, statusImages.value(Status::SecurityBad)); setWindowTitle(QString(P_CAPTION) .arg(APP_VERSION).arg(m_params.file.isEmpty() ? "Новый" : m_params.file) .arg(m_params.readonly ? " - ТОЛЬКО ЧТЕНИЕ": "")); } void SvTextEditor::setStatusLabel(QLabel* label, const StatusImage& status) { label->setPixmap(QPixmap::fromImage(status.image)); label->setToolTip(status.caption); } void SvTextEditor::setReadOnly(bool readOnly) { ui->textBrowser->setReadOnly(readOnly); } void SvTextEditor::openFileUrl(const QUrl& url) { if(!url.isValid()) { QMessageBox::critical(this, "Error", QString("Не валидный URL: %1").arg(url.toString())); return; } QString file_path = m_params.search_path.absoluteFilePath(url.toString()); QProcess::startDetached(QString("sudo %1 -file \"%2\" -path \"%3\" %4 %5 -project_file \"%6\"") .arg(qApp->applicationFilePath()) .arg(file_path) .arg(QDir(m_params.search_path).absolutePath()) .arg(m_params.mdmode ? "-mdmode" : "") .arg(m_params.readonly ? "-readonly" : "") .arg(m_params.project_file)); } void SvTextEditor::closeEvent(QCloseEvent *e) { if(ui->textBrowser->document()->isModified()) { int ask = QMessageBox::question(this, "Сохранение", "Документ изменен. Сохранить изменения перд закрытием?", QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if((ask == QMessageBox::Yes && save()) || ask == QMessageBox::No) e->accept(); else e->ignore(); } else e->accept(); } SvTextEditor::~SvTextEditor() { delete ui; } void SvTextEditor::on_textBrowser_textChanged() { bool modified = ui->textBrowser->document()->isModified(); ui->actionSave->setEnabled(modified); // ui->statusbar->showMessage(QString(P_STATUS) // .arg(m_params.search_path.absoluteFilePath(m_params.file)) // .arg(modified ? " - Не сохранен": "")); setWindowTitle(QString(P_CAPTION).arg(APP_VERSION).arg(m_params.file).arg(modified ? " - Не сохранен" : "")); ui->actionFind->setEnabled(!ui->textBrowser->toPlainText().isEmpty()); } bool SvTextEditor::save() { try { if (!ui->textBrowser->document()->isModified()) return true; if (m_params.file.isEmpty()) throw SvException("Имя файла не задано"); QFile f(m_params.file); if(!f.open(QFile::WriteOnly)) throw SvException(f.errorString()); if(ui->textBrowser->toPlainText().isEmpty() && QMessageBox::question(this, "Документ пуст", "Документ пуст. Вы уверены, что хотите сохранить его?", QMessageBox::Yes | QMessageBox::Cancel) != QMessageBox::Yes) return false; if(f.write(ui->textBrowser->toPlainText().toUtf8()) <= 0) throw SvException(f.errorString()); f.close(); // QFile fhash(m_hashPath); // if(fhash.fileName().isEmpty()) // { // securityError("Не удалось найти хэштаблицу"); // } // else // { // bool fileFind = false; // if(fhash.open(QFile::ReadWrite)) // { // QString text = QString(qUncompress(fhash.readAll())); // for(QString line: text.split('\n')) // { // if(line.isEmpty()) // continue; // QList<QString> ls; // ls = line.split(';'); // if(ls.size() != 2) // { // securityError("В хэш таблице неправильный формат"); // break; // } // else // { // QString filePath = ls[0]; // if(filePath == m_params.file) // { // text.replace(ls[1], (QCryptographicHash::hash(ui->textBrowser->toPlainText().toUtf8(), QCryptographicHash::Sha1).toBase64())); // fhash.seek(0); // fhash.write(qCompress(text.toUtf8())); // fileFind = true; // setStatusLabel(ui->labelSecurityInfo, statusImages.value(Status::SecurityGood)); // break; // } // } // } // if(!fileFind) // { // QString textToWrite = QString("%1;%2\n").arg(m_params.file).arg(QString(QCryptographicHash::hash(ui->textBrowser->toPlainText().toUtf8(), QCryptographicHash::Sha1).toBase64())); // fhash.write(qCompress(textToWrite.toUtf8())); // setStatusLabel(ui->labelSecurityInfo, statusImages.value(Status::SecurityGood)); // } // fhash.close(); // } // else{ // securityError("Не удалось открыть хэш таблицу"); // } // } ui->textBrowser->document()->setModified(false); on_textBrowser_textChanged(); return true; } catch(SvException& e) { QMessageBox::critical(this, "Error", QString("Ошибка при сохранении файла %1").arg(e.error)); return false; } } //void TextEdit::textSize(const QString &p) //{ // qreal pointSize = p.toFloat(); // if (p.toFloat() > 0) { // QTextCharFormat fmt; // fmt.setFontPointSize(pointSize); // mergeFormatOnWordOrSelection(fmt); // } //} //void TextEdit::mergeFormatOnWordOrSelection(const QTextCharFormat &format) //{ // QTextCursor cursor = textEdit->textCursor(); // if (!cursor.hasSelection()) // cursor.select(QTextCursor::WordUnderCursor); // cursor.mergeCharFormat(format); // textEdit->mergeCurrentCharFormat(format); //} void SvTextEditor::on_textBrowser_copyAvailable(bool b) { ui->actionCopy->setEnabled(b); ui->actionCut->setEnabled(b); ui->actionPaste->setEnabled(b); } void SvTextEditor::on_textBrowser_undoAvailable(bool b) { ui->actionUndo->setEnabled(b); } void SvTextEditor::on_textBrowser_redoAvailable(bool b) { ui->actionRedo->setEnabled(b); } void SvTextEditor::on_textBrowser_historyChanged() { ui->textBrowser->historyUrl(1); } void SvTextEditor::on_actionSave_triggered() { if(!m_params.file.isEmpty()) save(); else on_actionSaveAs_triggered(); } void SvTextEditor::on_actionSaveAs_triggered() { QString sfn = QFileDialog::getSaveFileName(this, "Сохранение файла", m_params.search_path.path()); if(sfn.isEmpty()) return; m_params.file = sfn; save(); } void SvTextEditor::on_actionOpen_triggered() { if(ui->textBrowser->document()->isModified()) { int ask = QMessageBox::question(this, "Сохранение", "Документ изменен. Сохранить изменения перд закрытием?", QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if(ask == QMessageBox::Yes) save(); else if(ask == QMessageBox::Cancel) return; } QString ofn = QFileDialog::getOpenFileName(this, "Открытие файла", m_params.search_path.path()); if(ofn.isEmpty()) return; readFile(ofn, QDir(ofn)); } void SvTextEditor::on_actionFind_triggered() { ui->dockSearchWidget->show(); ui->lineSearchWhat->setStyleSheet(""); ui->lineSearchWhat->setFocus(); } void SvTextEditor::search() { auto find = [=](QTextDocument::FindFlags flags) -> QTextCursor { if(ui->bnSearchRegExp->isChecked()) { return ui->textBrowser->document()->find(QRegExp(ui->lineSearchWhat->text()), ui->textBrowser->textCursor().selectionStart(), flags); } else { return ui->textBrowser->document()->find(ui->lineSearchWhat->text(), ui->textBrowser->textCursor().selectionStart(), flags); } }; ui->lineSearchWhat->setStyleSheet(""); QTextDocument::FindFlags find_flags = 0; // если поиск по action'у QAction* action = qobject_cast<QAction*>(sender()); if(action && action->objectName() == "actionSearchBackward") find_flags |= QTextDocument::FindBackward; if(ui->bnCaseSencitive->isChecked()) find_flags |= QTextDocument::FindCaseSensitively; if(ui->bnSearchWholeWord->isChecked()) find_flags |= QTextDocument::FindWholeWords; QTextCursor old_cursor = ui->textBrowser->textCursor(); QTextCursor new_cursor; // ищем в тексте new_cursor = find(find_flags); // если поиск ничего не дал... то это еще ничего не значит // этот поиск ищет от курсора и до конца или начала документа ( взависимости от флага QTextDocument::FindBackward) // поэтому ... if(!ui->lineSearchWhat->text().isEmpty() && new_cursor.isNull()) { // ищем еще раз! этот поиск не круговой, поэтому передвигаем курсор, соответственно в конец или в начало, и ищем еще раз if(find_flags & QTextDocument::FindBackward) ui->textBrowser->moveCursor(QTextCursor::End); // ui->textBrowser->toPlainText().length()); else ui->textBrowser->moveCursor(QTextCursor::Start); new_cursor = find(find_flags); } // здесь если точно ничего не нашли, то подсвечиваем красным if(!ui->lineSearchWhat->text().isEmpty()) { if(new_cursor.isNull()) { ui->lineSearchWhat->setStyleSheet("background-color: rgb(255, 200, 200);"); ui->textBrowser->setTextCursor(old_cursor); } else ui->textBrowser->setTextCursor(new_cursor); } if(!ui->lineSearchWhat->hasFocus()) ui->textBrowser->setFocus(); } void SvTextEditor::on_textBrowser_cursorPositionChanged() { ui->labelDocumentInfo->setText(QString("Всего строк: %1, Строка: %2, Кол: %3") .arg(ui->textBrowser->document()->lineCount()) .arg(ui->textBrowser->textCursor().blockNumber()) .arg(ui->textBrowser->textCursor().columnNumber())); } void SvTextEditor::securityError(const QString& error) { setStatusLabel(ui->labelSecurityInfo, statusImages.value(Status::SecurityBad)); QMessageBox::critical(this, "Файлы могли быть изменены", error); }