"Professional CMake" book suggest the following: "Targets should build successfully with or without compiler support for precompiled headers. It should be considered an optimization, not a requirement. In particular, do not explicitly include a precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically generated precompile header on the compiler command line instead. This is more portable across the major compilers and is likely to be easier to maintain. It will also avoid warnings being generated from certain code checking tools like iwyu (include what you use)." Therefore, removed the "#include <PreCompiled.h>" from sources, also there is no need for the "#ifdef _PreComp_" anymore
926 lines
25 KiB
C++
926 lines
25 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library 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 Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <QApplication>
|
|
#include <QCheckBox>
|
|
#include <QClipboard>
|
|
#include <QDateTime>
|
|
#include <QHBoxLayout>
|
|
#include <QVBoxLayout>
|
|
#include <QLineEdit>
|
|
#include <QMessageBox>
|
|
#include <QPrinter>
|
|
#include <QPrintDialog>
|
|
#include <QPlainTextEdit>
|
|
#include <QPrintPreviewDialog>
|
|
#include <QSpacerItem>
|
|
#include <QStyle>
|
|
#include <QTextCursor>
|
|
#include <QTextDocument>
|
|
#include <QTextStream>
|
|
#include <QTimer>
|
|
#include <QToolButton>
|
|
|
|
#include "EditorView.h"
|
|
#include "Application.h"
|
|
#include "FileDialog.h"
|
|
#include "Macro.h"
|
|
#include "MainWindow.h"
|
|
#include "PythonEditor.h"
|
|
#include "PythonTracing.h"
|
|
#include "WaitCursor.h"
|
|
|
|
#include <Base/Exception.h>
|
|
#include <Base/Interpreter.h>
|
|
#include <Base/Parameter.h>
|
|
|
|
#include <Gui/PreferencePages/DlgSettingsPDF.h>
|
|
|
|
|
|
using namespace Gui;
|
|
namespace Gui
|
|
{
|
|
class EditorViewP
|
|
{
|
|
public:
|
|
TextEdit* textEdit;
|
|
SearchBar* searchBar;
|
|
QString fileName;
|
|
EditorView::DisplayName displayName;
|
|
QTimer* activityTimer;
|
|
qint64 timeStamp;
|
|
bool lock;
|
|
bool aboutToClose;
|
|
QStringList undos;
|
|
QStringList redos;
|
|
};
|
|
} // namespace Gui
|
|
|
|
// -------------------------------------------------------
|
|
|
|
/* TRANSLATOR Gui::EditorView */
|
|
|
|
TYPESYSTEM_SOURCE_ABSTRACT(Gui::EditorView, Gui::MDIView)
|
|
|
|
/**
|
|
* Constructs a EditorView which is a child of 'parent', with the
|
|
* name 'name'.
|
|
*/
|
|
EditorView::EditorView(TextEdit* editor, QWidget* parent)
|
|
: MDIView(nullptr, parent, Qt::WindowFlags())
|
|
, WindowParameter("Editor")
|
|
{
|
|
d = new EditorViewP;
|
|
d->lock = false;
|
|
d->aboutToClose = false;
|
|
d->displayName = EditorView::FullName;
|
|
|
|
// create the editor first
|
|
d->textEdit = editor;
|
|
d->textEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
|
|
|
|
d->searchBar = new SearchBar();
|
|
d->searchBar->setEditor(editor);
|
|
|
|
// clang-format off
|
|
// update editor actions on request
|
|
Gui::MainWindow* mw = Gui::getMainWindow();
|
|
connect(editor, &QPlainTextEdit::undoAvailable, mw, &MainWindow::updateEditorActions);
|
|
connect(editor, &QPlainTextEdit::redoAvailable, mw, &MainWindow::updateEditorActions);
|
|
connect(editor, &QPlainTextEdit::copyAvailable, mw, &MainWindow::updateEditorActions);
|
|
|
|
connect(editor, &TextEdit::showSearchBar, d->searchBar, &SearchBar::activate);
|
|
connect(editor, &TextEdit::findNext, d->searchBar, &SearchBar::findNext);
|
|
connect(editor, &TextEdit::findPrevious, d->searchBar, &SearchBar::findPrevious);
|
|
// clang-format on
|
|
|
|
// Create the layout containing the workspace and a tab bar
|
|
auto hbox = new QFrame(this);
|
|
hbox->setFrameShape(QFrame::StyledPanel);
|
|
hbox->setFrameShadow(QFrame::Sunken);
|
|
auto layout = new QVBoxLayout();
|
|
layout->setContentsMargins(1, 1, 1, 1);
|
|
layout->addWidget(d->textEdit);
|
|
layout->addWidget(d->searchBar);
|
|
d->textEdit->setParent(hbox);
|
|
d->searchBar->setParent(hbox);
|
|
hbox->setLayout(layout);
|
|
setCentralWidget(hbox);
|
|
|
|
setCurrentFileName(QString());
|
|
d->textEdit->setFocus();
|
|
|
|
setWindowIcon(d->textEdit->windowIcon());
|
|
|
|
ParameterGrp::handle hPrefGrp = getWindowParameter();
|
|
hPrefGrp->Attach(this);
|
|
hPrefGrp->NotifyAll();
|
|
|
|
d->activityTimer = new QTimer(this);
|
|
// clang-format off
|
|
connectionList <<
|
|
connect(d->activityTimer, &QTimer::timeout,
|
|
this, &EditorView::checkTimestamp) <<
|
|
connect(d->textEdit->document(), &QTextDocument::modificationChanged,
|
|
this, &EditorView::setWindowModified) <<
|
|
connect(d->textEdit->document(), &QTextDocument::undoAvailable,
|
|
this, &EditorView::undoAvailable) <<
|
|
connect(d->textEdit->document(), &QTextDocument::redoAvailable,
|
|
this, &EditorView::redoAvailable) <<
|
|
connect(d->textEdit->document(), &QTextDocument::contentsChange,
|
|
this, &EditorView::contentsChange);
|
|
// clang-format on
|
|
}
|
|
|
|
/** Destroys the object and frees any allocated resources */
|
|
EditorView::~EditorView()
|
|
{
|
|
d->activityTimer->stop();
|
|
// to avoid the assert introduced a debug version of Qt >6.3. See QTBUG-105473
|
|
for (auto conn : connectionList) { // NOLINT(performance-for-range-copy)
|
|
disconnect(conn);
|
|
}
|
|
delete d->activityTimer;
|
|
delete d;
|
|
getWindowParameter()->Detach(this);
|
|
}
|
|
|
|
QPlainTextEdit* EditorView::getEditor() const
|
|
{
|
|
return d->textEdit;
|
|
}
|
|
|
|
void EditorView::showEvent(QShowEvent* event)
|
|
{
|
|
Gui::MainWindow* mw = Gui::getMainWindow();
|
|
mw->updateEditorActions();
|
|
MDIView::showEvent(event);
|
|
}
|
|
|
|
void EditorView::hideEvent(QHideEvent* event)
|
|
{
|
|
MDIView::hideEvent(event);
|
|
}
|
|
|
|
void EditorView::closeEvent(QCloseEvent* event)
|
|
{
|
|
MDIView::closeEvent(event);
|
|
if (event->isAccepted()) {
|
|
d->aboutToClose = true;
|
|
Gui::MainWindow* mw = Gui::getMainWindow();
|
|
mw->updateEditorActions();
|
|
}
|
|
}
|
|
|
|
void EditorView::OnChange(Base::Subject<const char*>& rCaller, const char* rcReason)
|
|
{
|
|
Q_UNUSED(rCaller);
|
|
ParameterGrp::handle hPrefGrp = getWindowParameter();
|
|
if (strcmp(rcReason, "EnableLineNumber") == 0) {
|
|
// bool show = hPrefGrp->GetBool( "EnableLineNumber", true );
|
|
}
|
|
}
|
|
|
|
void EditorView::checkTimestamp()
|
|
{
|
|
QFileInfo fi(d->fileName);
|
|
qint64 timeStamp = fi.lastModified().toSecsSinceEpoch();
|
|
if (timeStamp != d->timeStamp) {
|
|
switch (QMessageBox::question(this,
|
|
tr("Modified file"),
|
|
tr("%1.\n\nThis has been modified outside of the source "
|
|
"editor. Reload it?")
|
|
.arg(d->fileName),
|
|
QMessageBox::Yes | QMessageBox::No,
|
|
QMessageBox::Yes)) {
|
|
case QMessageBox::Yes:
|
|
// updates time stamp and timer
|
|
open(d->fileName);
|
|
return;
|
|
case QMessageBox::No:
|
|
d->timeStamp = timeStamp;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
d->activityTimer->setSingleShot(true);
|
|
d->activityTimer->start(3000);
|
|
}
|
|
|
|
/**
|
|
* Runs the action specified by \a pMsg.
|
|
*/
|
|
bool EditorView::onMsg(const char* pMsg, const char** /*ppReturn*/)
|
|
{
|
|
// don't allow any actions if the editor is being closed
|
|
if (d->aboutToClose) {
|
|
return false;
|
|
}
|
|
|
|
if (strcmp(pMsg, "Save") == 0) {
|
|
saveFile();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "SaveAs") == 0) {
|
|
saveAs();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "Cut") == 0) {
|
|
cut();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "Copy") == 0) {
|
|
copy();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "Paste") == 0) {
|
|
paste();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "Undo") == 0) {
|
|
undo();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "Redo") == 0) {
|
|
redo();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "ViewFit") == 0) {
|
|
// just ignore this
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if the action \a pMsg is available. This is for enabling/disabling
|
|
* the corresponding buttons or menu items for this action.
|
|
*/
|
|
bool EditorView::onHasMsg(const char* pMsg) const
|
|
{
|
|
// don't allow any actions if the editor is being closed
|
|
if (d->aboutToClose) {
|
|
return false;
|
|
}
|
|
if (strcmp(pMsg, "Run") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "DebugStart") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "DebugStop") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "SaveAs") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "Print") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "PrintPreview") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "PrintPdf") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "Save") == 0) {
|
|
return d->textEdit->document()->isModified();
|
|
}
|
|
else if (strcmp(pMsg, "Cut") == 0) {
|
|
bool canWrite = !d->textEdit->isReadOnly();
|
|
return (canWrite && (d->textEdit->textCursor().hasSelection()));
|
|
}
|
|
else if (strcmp(pMsg, "Copy") == 0) {
|
|
return (d->textEdit->textCursor().hasSelection());
|
|
}
|
|
else if (strcmp(pMsg, "Paste") == 0) {
|
|
QClipboard* cb = QApplication::clipboard();
|
|
QString text;
|
|
|
|
// Copy text from the clipboard (paste)
|
|
text = cb->text();
|
|
|
|
bool canWrite = !d->textEdit->isReadOnly();
|
|
return (!text.isEmpty() && canWrite);
|
|
}
|
|
else if (strcmp(pMsg, "Undo") == 0) {
|
|
return d->textEdit->document()->isUndoAvailable();
|
|
}
|
|
else if (strcmp(pMsg, "Redo") == 0) {
|
|
return d->textEdit->document()->isRedoAvailable();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** Checking on close state. */
|
|
bool EditorView::canClose()
|
|
{
|
|
if (!d->textEdit->document()->isModified()) {
|
|
return true;
|
|
}
|
|
this->setFocus(); // raises the view to front
|
|
switch (QMessageBox::question(this,
|
|
tr("Unsaved document"),
|
|
tr("The document has been modified.\n"
|
|
"Save all changes?"),
|
|
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
|
|
QMessageBox::Cancel)) {
|
|
case QMessageBox::Yes:
|
|
return saveFile();
|
|
case QMessageBox::No:
|
|
return true;
|
|
case QMessageBox::Cancel:
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void EditorView::setDisplayName(EditorView::DisplayName type)
|
|
{
|
|
d->displayName = type;
|
|
}
|
|
|
|
/**
|
|
* Saves the content of the editor to a file specified by the appearing file dialog.
|
|
*/
|
|
bool EditorView::saveAs()
|
|
{
|
|
QString fn = FileDialog::getSaveFileName(
|
|
this,
|
|
QObject::tr("Save Macro"),
|
|
QString(),
|
|
QStringLiteral("%1 (*.FCMacro);;Python (*.py)").arg(tr("FreeCAD macro")));
|
|
if (fn.isEmpty()) {
|
|
return false;
|
|
}
|
|
setCurrentFileName(fn);
|
|
return saveFile();
|
|
}
|
|
|
|
/**
|
|
* Opens the file \a fileName.
|
|
*/
|
|
bool EditorView::open(const QString& fileName)
|
|
{
|
|
if (!QFile::exists(fileName)) {
|
|
return false;
|
|
}
|
|
QFile file(fileName);
|
|
if (!file.open(QFile::ReadOnly)) {
|
|
return false;
|
|
}
|
|
|
|
d->lock = true;
|
|
d->textEdit->setPlainText(QString::fromUtf8(file.readAll()));
|
|
d->lock = false;
|
|
d->undos.clear();
|
|
d->redos.clear();
|
|
file.close();
|
|
|
|
QFileInfo fi(fileName);
|
|
d->timeStamp = fi.lastModified().toSecsSinceEpoch();
|
|
d->activityTimer->setSingleShot(true);
|
|
d->activityTimer->start(3000);
|
|
|
|
setCurrentFileName(fileName);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Copies the selected text to the clipboard and deletes it from the text edit.
|
|
* If there is no selected text nothing happens.
|
|
*/
|
|
void EditorView::cut()
|
|
{
|
|
d->textEdit->cut();
|
|
}
|
|
|
|
/**
|
|
* Copies any selected text to the clipboard.
|
|
*/
|
|
void EditorView::copy()
|
|
{
|
|
d->textEdit->copy();
|
|
}
|
|
|
|
/**
|
|
* Pastes the text from the clipboard into the text edit at the current cursor position.
|
|
* If there is no text in the clipboard nothing happens.
|
|
*/
|
|
void EditorView::paste()
|
|
{
|
|
d->textEdit->paste();
|
|
}
|
|
|
|
/**
|
|
* Undoes the last operation.
|
|
* If there is no operation to undo, i.e. there is no undo step in the undo/redo history, nothing
|
|
* happens.
|
|
*/
|
|
void EditorView::undo()
|
|
{
|
|
d->lock = true;
|
|
if (!d->undos.isEmpty()) {
|
|
d->redos << d->undos.back();
|
|
d->undos.pop_back();
|
|
}
|
|
d->textEdit->document()->undo();
|
|
d->lock = false;
|
|
}
|
|
|
|
/**
|
|
* Redoes the last operation.
|
|
* If there is no operation to undo, i.e. there is no undo step in the undo/redo history, nothing
|
|
* happens.
|
|
*/
|
|
void EditorView::redo()
|
|
{
|
|
d->lock = true;
|
|
if (!d->redos.isEmpty()) {
|
|
d->undos << d->redos.back();
|
|
d->redos.pop_back();
|
|
}
|
|
d->textEdit->document()->redo();
|
|
d->lock = false;
|
|
}
|
|
|
|
/**
|
|
* Shows the printer dialog.
|
|
*/
|
|
void EditorView::print()
|
|
{
|
|
QPrinter printer(QPrinter::ScreenResolution);
|
|
printer.setFullPage(true);
|
|
QPrintDialog dlg(&printer, this);
|
|
if (dlg.exec() == QDialog::Accepted) {
|
|
d->textEdit->document()->print(&printer);
|
|
}
|
|
}
|
|
|
|
void EditorView::printPreview()
|
|
{
|
|
QPrinter printer(QPrinter::ScreenResolution);
|
|
QPrintPreviewDialog dlg(&printer, this);
|
|
connect(&dlg,
|
|
&QPrintPreviewDialog::paintRequested,
|
|
this,
|
|
qOverload<QPrinter*>(&EditorView::print));
|
|
dlg.exec();
|
|
}
|
|
|
|
void EditorView::print(QPrinter* printer)
|
|
{
|
|
d->textEdit->document()->print(printer);
|
|
}
|
|
|
|
/**
|
|
* Prints the document into a Pdf file.
|
|
*/
|
|
void EditorView::printPdf()
|
|
{
|
|
QString filename =
|
|
FileDialog::getSaveFileName(this,
|
|
tr("Export PDF"),
|
|
QString(),
|
|
QStringLiteral("%1 (*.pdf)").arg(tr("PDF file")));
|
|
if (!filename.isEmpty()) {
|
|
QPrinter printer(QPrinter::ScreenResolution);
|
|
// setPdfVersion sets the printed PDF Version to what is chosen in
|
|
// Preferences/Import-Export/PDF more details under:
|
|
// https://www.kdab.com/creating-pdfa-documents-qt/
|
|
printer.setPdfVersion(Gui::Dialog::DlgSettingsPDF::evaluatePDFVersion());
|
|
printer.setOutputFormat(QPrinter::PdfFormat);
|
|
printer.setOutputFileName(filename);
|
|
printer.setCreator(QString::fromStdString(App::Application::getNameWithVersion()));
|
|
d->textEdit->document()->print(&printer);
|
|
}
|
|
}
|
|
|
|
void EditorView::setCurrentFileName(const QString& fileName)
|
|
{
|
|
d->fileName = fileName;
|
|
Q_EMIT changeFileName(d->fileName);
|
|
d->textEdit->document()->setModified(false);
|
|
|
|
QString name;
|
|
QFileInfo fi(fileName);
|
|
switch (d->displayName) {
|
|
case FullName:
|
|
name = fileName;
|
|
break;
|
|
case FileName:
|
|
name = fi.fileName();
|
|
break;
|
|
case BaseName:
|
|
name = fi.baseName();
|
|
break;
|
|
}
|
|
|
|
QString shownName;
|
|
if (fileName.isEmpty()) {
|
|
shownName = tr("untitled[*]");
|
|
}
|
|
else {
|
|
shownName = QStringLiteral("%1[*]").arg(name);
|
|
}
|
|
shownName += tr(" - Editor");
|
|
setWindowTitle(shownName);
|
|
setWindowModified(false);
|
|
}
|
|
|
|
QString EditorView::fileName() const
|
|
{
|
|
return d->fileName;
|
|
}
|
|
|
|
/**
|
|
* Saves the contents to a file.
|
|
*/
|
|
bool EditorView::saveFile()
|
|
{
|
|
if (d->fileName.isEmpty()) {
|
|
return saveAs();
|
|
}
|
|
|
|
QFile file(d->fileName);
|
|
if (!file.open(QFile::WriteOnly)) {
|
|
return false;
|
|
}
|
|
QTextStream ts(&file);
|
|
#if QT_VERSION < 0x060000
|
|
ts.setCodec("UTF-8");
|
|
#endif
|
|
ts << d->textEdit->document()->toPlainText();
|
|
file.close();
|
|
d->textEdit->document()->setModified(false);
|
|
|
|
QFileInfo fi(d->fileName);
|
|
d->timeStamp = fi.lastModified().toSecsSinceEpoch();
|
|
return true;
|
|
}
|
|
|
|
void EditorView::undoAvailable(bool undo)
|
|
{
|
|
if (!undo) {
|
|
d->undos.clear();
|
|
}
|
|
}
|
|
|
|
void EditorView::redoAvailable(bool redo)
|
|
{
|
|
if (!redo) {
|
|
d->redos.clear();
|
|
}
|
|
}
|
|
|
|
void EditorView::contentsChange(int position, int charsRemoved, int charsAdded)
|
|
{
|
|
Q_UNUSED(position);
|
|
if (d->lock) {
|
|
return;
|
|
}
|
|
if (charsRemoved > 0 && charsAdded > 0) {
|
|
return; // syntax highlighting
|
|
}
|
|
else if (charsRemoved > 0) {
|
|
d->undos << tr("%1 chars removed").arg(charsRemoved);
|
|
}
|
|
else if (charsAdded > 0) {
|
|
d->undos << tr("%1 chars added").arg(charsAdded);
|
|
}
|
|
else {
|
|
d->undos << tr("Formatted");
|
|
}
|
|
d->redos.clear();
|
|
}
|
|
|
|
/**
|
|
* Get the undo history.
|
|
*/
|
|
QStringList EditorView::undoActions() const
|
|
{
|
|
return d->undos;
|
|
}
|
|
|
|
/**
|
|
* Get the redo history.
|
|
*/
|
|
QStringList EditorView::redoActions() const
|
|
{
|
|
return d->redos;
|
|
;
|
|
}
|
|
|
|
void EditorView::focusInEvent(QFocusEvent*)
|
|
{
|
|
d->textEdit->setFocus();
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
|
|
TYPESYSTEM_SOURCE_ABSTRACT(Gui::PythonEditorView, Gui::EditorView)
|
|
|
|
PythonEditorView::PythonEditorView(PythonEditor* editor, QWidget* parent)
|
|
: EditorView(editor, parent)
|
|
, _pye(editor)
|
|
{
|
|
connect(this, &PythonEditorView::changeFileName, editor, &PythonEditor::setFileName);
|
|
watcher = new PythonTracingWatcher(this);
|
|
}
|
|
|
|
PythonEditorView::~PythonEditorView()
|
|
{
|
|
delete watcher;
|
|
}
|
|
|
|
/**
|
|
* Runs the action specified by \a pMsg.
|
|
*/
|
|
bool PythonEditorView::onMsg(const char* pMsg, const char** ppReturn)
|
|
{
|
|
if (strcmp(pMsg, "Run") == 0) {
|
|
executeScript();
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "StartDebug") == 0) {
|
|
QTimer::singleShot(300, this, &PythonEditorView::startDebug);
|
|
return true;
|
|
}
|
|
else if (strcmp(pMsg, "ToggleBreakpoint") == 0) {
|
|
toggleBreakpoint();
|
|
return true;
|
|
}
|
|
return EditorView::onMsg(pMsg, ppReturn);
|
|
}
|
|
|
|
/**
|
|
* Checks if the action \a pMsg is available. This is for enabling/disabling
|
|
* the corresponding buttons or menu items for this action.
|
|
*/
|
|
bool PythonEditorView::onHasMsg(const char* pMsg) const
|
|
{
|
|
if (strcmp(pMsg, "Run") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "StartDebug") == 0) {
|
|
return true;
|
|
}
|
|
if (strcmp(pMsg, "ToggleBreakpoint") == 0) {
|
|
return true;
|
|
}
|
|
return EditorView::onHasMsg(pMsg);
|
|
}
|
|
|
|
/**
|
|
* Runs the opened script in the macro manager.
|
|
*/
|
|
void PythonEditorView::executeScript()
|
|
{
|
|
// always save the macro when it is modified
|
|
if (EditorView::onHasMsg("Save")) {
|
|
EditorView::onMsg("Save", nullptr);
|
|
}
|
|
try {
|
|
getMainWindow()->setCursor(Qt::WaitCursor);
|
|
PythonTracingLocker tracelock(watcher->getTrace());
|
|
Application::Instance->macroManager()->run(Gui::MacroManager::File, fileName().toUtf8());
|
|
getMainWindow()->unsetCursor();
|
|
}
|
|
catch (const Base::SystemExitException&) {
|
|
// handle SystemExit exceptions
|
|
Base::PyGILStateLocker locker;
|
|
Base::PyException e;
|
|
e.reportException();
|
|
getMainWindow()->unsetCursor();
|
|
}
|
|
}
|
|
|
|
void PythonEditorView::startDebug()
|
|
{
|
|
_pye->startDebug();
|
|
}
|
|
|
|
void PythonEditorView::toggleBreakpoint()
|
|
{
|
|
_pye->toggleBreakpoint();
|
|
}
|
|
|
|
void PythonEditorView::showDebugMarker(int line)
|
|
{
|
|
_pye->showDebugMarker(line);
|
|
}
|
|
|
|
void PythonEditorView::hideDebugMarker()
|
|
{
|
|
_pye->hideDebugMarker();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
SearchBar::SearchBar(QWidget* parent)
|
|
: QWidget(parent)
|
|
, textEditor(nullptr)
|
|
{
|
|
horizontalLayout = new QHBoxLayout(this);
|
|
horizontalLayout->setSpacing(3);
|
|
|
|
closeButton = new QToolButton(this);
|
|
closeButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
|
|
closeButton->setAutoRaise(true);
|
|
connect(closeButton, &QToolButton::clicked, this, &SearchBar::deactivate);
|
|
|
|
horizontalLayout->addWidget(closeButton);
|
|
|
|
searchText = new QLineEdit(this);
|
|
searchText->setClearButtonEnabled(true);
|
|
horizontalLayout->addWidget(searchText);
|
|
connect(searchText, &QLineEdit::returnPressed, this, &SearchBar::findNext);
|
|
connect(searchText, &QLineEdit::textChanged, this, &SearchBar::findCurrent);
|
|
connect(searchText, &QLineEdit::textChanged, this, &SearchBar::updateButtons);
|
|
|
|
prevButton = new QToolButton(this);
|
|
prevButton->setIcon(style()->standardIcon(QStyle::SP_ArrowBack));
|
|
prevButton->setAutoRaise(true);
|
|
prevButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
horizontalLayout->addWidget(prevButton);
|
|
connect(prevButton, &QToolButton::clicked, this, &SearchBar::findPrevious);
|
|
|
|
nextButton = new QToolButton(this);
|
|
nextButton->setIcon(style()->standardIcon(QStyle::SP_ArrowForward));
|
|
nextButton->setAutoRaise(true);
|
|
nextButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
horizontalLayout->addWidget(nextButton);
|
|
connect(nextButton, &QToolButton::clicked, this, &SearchBar::findNext);
|
|
|
|
matchCase = new QCheckBox(this);
|
|
horizontalLayout->addWidget(matchCase);
|
|
connect(matchCase, &QCheckBox::toggled, this, &SearchBar::findCurrent);
|
|
|
|
matchWord = new QCheckBox(this);
|
|
horizontalLayout->addWidget(matchWord);
|
|
connect(matchWord, &QCheckBox::toggled, this, &SearchBar::findCurrent);
|
|
|
|
horizontalSpacer = new QSpacerItem(192, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
horizontalLayout->addItem(horizontalSpacer);
|
|
|
|
retranslateUi();
|
|
|
|
setMinimumWidth(minimumSizeHint().width());
|
|
updateButtons();
|
|
hide();
|
|
}
|
|
|
|
void SearchBar::setEditor(QPlainTextEdit* textEdit)
|
|
{
|
|
textEditor = textEdit;
|
|
}
|
|
|
|
void SearchBar::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
if (event->key() == Qt::Key_Escape) {
|
|
hide();
|
|
return;
|
|
}
|
|
|
|
QWidget::keyPressEvent(event);
|
|
}
|
|
|
|
void SearchBar::retranslateUi()
|
|
{
|
|
prevButton->setText(tr("Previous"));
|
|
nextButton->setText(tr("Next"));
|
|
matchCase->setText(tr("Case sensitive"));
|
|
matchWord->setText(tr("Whole words"));
|
|
}
|
|
|
|
void SearchBar::activate()
|
|
{
|
|
show();
|
|
searchText->selectAll();
|
|
searchText->setFocus(Qt::ShortcutFocusReason);
|
|
}
|
|
|
|
void SearchBar::deactivate()
|
|
{
|
|
if (textEditor) {
|
|
textEditor->setFocus();
|
|
}
|
|
hide();
|
|
}
|
|
|
|
void SearchBar::findPrevious()
|
|
{
|
|
findText(true, false, searchText->text());
|
|
}
|
|
|
|
void SearchBar::findNext()
|
|
{
|
|
findText(true, true, searchText->text());
|
|
}
|
|
|
|
void SearchBar::findCurrent()
|
|
{
|
|
findText(false, true, searchText->text());
|
|
}
|
|
|
|
void SearchBar::findText(bool skip, bool next, const QString& str)
|
|
{
|
|
if (!textEditor) {
|
|
return;
|
|
}
|
|
|
|
QTextCursor cursor = textEditor->textCursor();
|
|
QTextDocument* doc = textEditor->document();
|
|
if (!doc || cursor.isNull()) {
|
|
return;
|
|
}
|
|
|
|
if (cursor.hasSelection()) {
|
|
cursor.setPosition((skip && next) ? cursor.position() : cursor.anchor());
|
|
}
|
|
|
|
bool found = true;
|
|
QTextCursor newCursor = cursor;
|
|
if (!str.isEmpty()) {
|
|
QTextDocument::FindFlags options;
|
|
if (!next) {
|
|
options |= QTextDocument::FindBackward;
|
|
}
|
|
if (matchCase->isChecked()) {
|
|
options |= QTextDocument::FindCaseSensitively;
|
|
}
|
|
if (matchWord->isChecked()) {
|
|
options |= QTextDocument::FindWholeWords;
|
|
}
|
|
|
|
newCursor = doc->find(str, cursor, options);
|
|
if (newCursor.isNull()) {
|
|
QTextCursor ac(doc);
|
|
ac.movePosition(options & QTextDocument::FindBackward ? QTextCursor::End
|
|
: QTextCursor::Start);
|
|
newCursor = doc->find(str, ac, options);
|
|
if (newCursor.isNull()) {
|
|
found = false;
|
|
newCursor = cursor;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isVisible()) {
|
|
show();
|
|
}
|
|
|
|
textEditor->setTextCursor(newCursor);
|
|
|
|
QString styleSheet;
|
|
if (!found) {
|
|
styleSheet = QStringLiteral(" QLineEdit {\n"
|
|
" background-color: rgb(221,144,161);\n"
|
|
" }\n");
|
|
}
|
|
|
|
searchText->setStyleSheet(styleSheet);
|
|
}
|
|
|
|
void SearchBar::updateButtons()
|
|
{
|
|
bool empty = searchText->text().isEmpty();
|
|
prevButton->setDisabled(empty);
|
|
nextButton->setDisabled(empty);
|
|
}
|
|
|
|
void SearchBar::changeEvent(QEvent* event)
|
|
{
|
|
if (event->type() == QEvent::LanguageChange) {
|
|
retranslateUi();
|
|
}
|
|
|
|
QWidget::changeEvent(event);
|
|
}
|
|
|
|
#include "moc_EditorView.cpp"
|