20 return QStringLiteral(
"recentFileList");
25 return QStringLiteral(
"file");
36 QCoreApplication::instance()->installEventFilter(
this);
47 projectBrowser->hide();
48 m_mdiArea->closeAllSubWindows();
49 if (m_mdiArea->currentSubWindow())
57 QCoreApplication::instance()->removeEventFilter(
this);
64 m_mdiArea->addSubWindow(child);
71 const QString fileName =
72 QFileDialog::getOpenFileName(
this,
"Open Python Script", QString(),
"Python Script (*.py)");
73 if (!fileName.isEmpty())
81 const QString folderName = QFileDialog::getExistingDirectory(
this,
"Open folder");
82 if (!folderName.isEmpty())
84 projectBrowser->show();
85 projectTreeView->setRootPath(folderName);
91 QWidget::changeEvent(e);
94 case QEvent::LanguageChange:
104 if (QMdiSubWindow *existing = findChildCodeEditor(fileName))
106 m_mdiArea->setActiveSubWindow(existing);
109 const bool succeeded = loadFile(fileName);
112 statusBar()->showMessage(tr(
"File loaded"), 2000);
119 const QString
path = projectTreeView->relativePathAt(index);
120 if (QFileInfo(
path).isFile())
126 bool PythonEditor::loadFile(
const QString &fileName)
129 const bool succeeded = child->
loadFile(fileName);
132 m_mdiArea->addSubWindow(child);
137 m_mdiArea->removeSubWindow(child);
141 PythonEditor::prependToRecentFiles(fileName);
149 for (
int i = 0; i <
count; ++i)
151 settings.setArrayIndex(i);
160 const int count = files.size();
162 for (
int i = 0; i <
count; ++i)
164 settings.setArrayIndex(i);
165 settings.setValue(
FileKey(), files.at(i));
170 bool PythonEditor::HasRecentFiles()
178 void PythonEditor::prependToRecentFiles(
const QString &fileName)
183 QStringList recentFiles = oldRecentFiles;
184 recentFiles.removeAll(fileName);
185 recentFiles.prepend(fileName);
186 if (oldRecentFiles != recentFiles)
189 setRecentFilesVisible(!recentFiles.isEmpty());
192 void PythonEditor::setRecentFilesVisible(
bool visible)
194 m_recentFileSubMenuAct->setVisible(visible);
195 m_recentFileSeparator->setVisible(visible);
205 for (; i <
count; ++i)
207 const QString fileName = QFileInfo(recentFiles.at(i)).fileName();
208 m_recentFileActs[i]->setText(tr(
"&%1 %2").arg(i + 1).arg(fileName));
209 m_recentFileActs[i]->setData(recentFiles.at(i));
210 m_recentFileActs[i]->setVisible(
true);
214 m_recentFileActs[i]->setVisible(
false);
220 if (
const auto *action = qobject_cast<const QAction *>(sender()))
222 openFile(action->data().toString());
230 case QEvent::Shortcut:
232 auto *sev =
static_cast<QShortcutEvent *
>(e);
233 if (sev->isAmbiguous())
235 for (
const auto &action : actions())
237 if (action->shortcut() == sev->key())
254 if (activeChildCodeEditor() && activeChildCodeEditor()->
save())
256 statusBar()->showMessage(tr(
"File saved"), 2000);
263 if (child && child->
saveAs())
265 statusBar()->showMessage(tr(
"File saved"), 2000);
266 PythonEditor::prependToRecentFiles(child->
currentFile());
270 #ifndef QT_NO_CLIPBOARD
273 if (activeChildCodeEditor())
275 activeChildCodeEditor()->cut();
281 if (activeChildCodeEditor())
283 activeChildCodeEditor()->copy();
289 if (activeChildCodeEditor())
291 activeChildCodeEditor()->paste();
298 if (activeChildCodeEditor())
300 activeChildCodeEditor()->
comment();
305 if (activeChildCodeEditor())
312 if (activeChildCodeEditor())
319 if (activeChildCodeEditor())
327 actionRun->setEnabled(
false);
328 if (activeChildCodeEditor())
330 statusbar->showMessage(QString(
"Executing %1").arg(activeChildCodeEditor()->windowTitle()));
336 actionRun->setEnabled(
true);
337 statusbar->clearMessage();
340 void PythonEditor::setupUi()
342 Ui::PythonEditor::setupUi(
this);
345 setCentralWidget(m_mdiArea);
346 m_mdiArea->showMaximized();
347 connect(m_mdiArea, &QMdiArea::subWindowActivated,
this, &PythonEditor::updateMenus);
350 viewsToolBar->setMovable(
false);
351 projectBrowser->toggleViewAction()->setEnabled(
true);
352 viewsToolBar->addAction(projectBrowser->toggleViewAction());
353 scriptOutputConsoleDock->toggleViewAction()->setEnabled(
true);
354 viewsToolBar->addAction(scriptOutputConsoleDock->toggleViewAction());
357 projectBrowser->hide();
358 connect(projectTreeView,
359 &ProjectView::doubleClicked,
363 scriptOutputConsoleDock->hide();
366 void PythonEditor::createActions()
373 connect(actionRun, &QAction::triggered,
this, &PythonEditor::runExecute);
374 connect(actionClose, &QAction::triggered,
this, [=]() { close(); });
375 connect(actionSettings, &QAction::triggered, m_settings, &EditorSettings::show);
377 menuFile->addSeparator();
379 QMenu *recentMenu = menuFile->addMenu(tr(
"Recent..."));
381 m_recentFileSubMenuAct = recentMenu->menuAction();
383 for (
auto &recentFileAct : m_recentFileActs)
385 recentFileAct = recentMenu->addAction(QString());
387 recentFileAct->setVisible(
false);
390 m_recentFileSeparator = menuFile->addSeparator();
392 setRecentFilesVisible(PythonEditor::HasRecentFiles());
393 menuFile->addSeparator();
395 actionCut->setShortcuts(QKeySequence::Cut);
397 actionPaste->setShortcuts(QKeySequence::Paste);
407 m_windowMenu = menuBar()->addMenu(tr(
"&Window"));
410 m_closeAct =
new QAction(tr(
"Cl&ose"),
this);
411 m_closeAct->setStatusTip(tr(
"Close the active window"));
412 connect(m_closeAct, &QAction::triggered, m_mdiArea, &QMdiArea::closeActiveSubWindow);
414 m_closeAllAct =
new QAction(tr(
"Close &All"),
this);
415 m_closeAllAct->setStatusTip(tr(
"Close all the windows"));
416 connect(m_closeAllAct, &QAction::triggered, m_mdiArea, &QMdiArea::closeAllSubWindows);
418 m_tileAct =
new QAction(tr(
"&Tile"),
this);
419 m_tileAct->setStatusTip(tr(
"Tile the windows"));
420 connect(m_tileAct, &QAction::triggered, m_mdiArea, &QMdiArea::tileSubWindows);
422 m_cascadeAct =
new QAction(tr(
"&Cascade"),
this);
423 m_cascadeAct->setStatusTip(tr(
"Cascade the windows"));
424 connect(m_cascadeAct, &QAction::triggered, m_mdiArea, &QMdiArea::cascadeSubWindows);
426 m_nextAct =
new QAction(tr(
"Ne&xt"),
this);
427 m_nextAct->setShortcuts(QKeySequence::NextChild);
428 m_nextAct->setStatusTip(tr(
"Move the focus to the next window"));
429 connect(m_nextAct, &QAction::triggered, m_mdiArea, &QMdiArea::activateNextSubWindow);
431 m_previousAct =
new QAction(tr(
"Pre&vious"),
this);
432 m_previousAct->setShortcuts(QKeySequence::PreviousChild);
433 m_previousAct->setStatusTip(tr(
"Move the focus to the previous "
435 connect(m_previousAct, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow);
437 m_windowMenuSeparatorAct =
new QAction(
this);
438 m_windowMenuSeparatorAct->setSeparator(
true);
442 menuBar()->addSeparator();
444 addAction(actionNew);
445 addAction(actionSave);
446 addAction(actionOpen);
447 addAction(actionSaveAs);
448 addAction(actionRun);
449 addAction(actionClose);
450 addAction(actionComment);
451 addAction(actionUncomment);
452 addAction(actionIndentMore);
453 addAction(actionIndentLess);
456 void PythonEditor::updateMenus()
458 const bool hasChildCodeEditor = (activeChildCodeEditor() !=
nullptr);
459 actionSave->setEnabled(hasChildCodeEditor);
460 actionSaveAs->setEnabled(hasChildCodeEditor);
461 actionRun->setEnabled(hasChildCodeEditor);
462 m_closeAct->setEnabled(hasChildCodeEditor);
463 m_closeAllAct->setEnabled(hasChildCodeEditor);
464 m_tileAct->setEnabled(hasChildCodeEditor);
465 m_cascadeAct->setEnabled(hasChildCodeEditor);
466 m_nextAct->setEnabled(hasChildCodeEditor);
467 m_previousAct->setEnabled(hasChildCodeEditor);
468 m_windowMenuSeparatorAct->setVisible(hasChildCodeEditor);
469 actionComment->setEnabled(hasChildCodeEditor);
470 actionUncomment->setEnabled(hasChildCodeEditor);
471 actionIndentMore->setEnabled(hasChildCodeEditor);
472 actionIndentLess->setEnabled(hasChildCodeEditor);
474 #ifndef QT_NO_CLIPBOARD
475 actionPaste->setEnabled(hasChildCodeEditor);
476 const bool hasSelection =
477 (activeChildCodeEditor() && activeChildCodeEditor()->textCursor().hasSelection());
478 actionCut->setEnabled(hasSelection);
479 actionCopy->setEnabled(hasSelection);
485 m_windowMenu->clear();
486 m_windowMenu->addAction(m_closeAct);
487 m_windowMenu->addAction(m_closeAllAct);
488 m_windowMenu->addSeparator();
489 m_windowMenu->addAction(m_tileAct);
490 m_windowMenu->addAction(m_cascadeAct);
491 m_windowMenu->addSeparator();
492 m_windowMenu->addAction(m_nextAct);
493 m_windowMenu->addAction(m_previousAct);
494 m_windowMenu->addAction(m_windowMenuSeparatorAct);
496 QList<QMdiSubWindow *> windows = m_mdiArea->subWindowList();
497 m_windowMenuSeparatorAct->setVisible(!windows.isEmpty());
499 for (
int i = 0; i < windows.size(); ++i)
501 QMdiSubWindow *mdiSubWindow = windows.at(i);
502 auto *child = qobject_cast<CodeEditor *>(mdiSubWindow->widget());
513 QAction *action = m_windowMenu->addAction(text);
517 [
this, mdiSubWindow]() { m_mdiArea->setActiveSubWindow(mdiSubWindow); });
518 action->setCheckable(
true);
519 action->setChecked(child == activeChildCodeEditor());
525 auto *child =
new CodeEditor(this->m_settings);
527 #ifndef QT_NO_CLIPBOARD
528 connect(child, &QPlainTextEdit::copyAvailable, actionCut, &QAction::setEnabled);
529 connect(child, &QPlainTextEdit::copyAvailable, actionCopy, &QAction::setEnabled);
535 void PythonEditor::readSettings()
538 const QByteArray geometry = settings.value(
"geometry", QByteArray()).toByteArray();
539 if (geometry.isEmpty())
542 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
543 const QRect availableGeometry = screen()->availableGeometry();
545 const QRect availableGeometry = QApplication::desktop()->availableGeometry(
this);
547 resize(availableGeometry.width() / 3, availableGeometry.height() / 2);
548 move((availableGeometry.width() -
width()) / 2,
549 (availableGeometry.height() -
height()) / 2);
553 restoreGeometry(geometry);
557 void PythonEditor::writeSettings()
560 settings.setValue(
"geometry", saveGeometry());
563 CodeEditor *PythonEditor::activeChildCodeEditor()
const
565 if (QMdiSubWindow *activeSubWindow = m_mdiArea->activeSubWindow())
567 return qobject_cast<CodeEditor *>(activeSubWindow->widget());
572 QMdiSubWindow *PythonEditor::findChildCodeEditor(
const QString &fileName)
const
574 QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
576 for (QMdiSubWindow *window : m_mdiArea->subWindowList())
578 auto *mdiChild = qobject_cast<CodeEditor *>(window->widget());
579 if (mdiChild->currentFile() == canonicalFilePath)
589 return QString(QCoreApplication::applicationName()).append(
":PythonRuntime");
592 void PythonEditor::runExecute()
594 if (activeChildCodeEditor())
596 scriptOutputConsoleDock->show();
597 this->scriptOutputConsole->clear();
598 Q_EMIT
executionCalled(qPrintable(activeChildCodeEditor()->toPlainText()),
599 this->scriptOutputConsole);
static void writeRecentFiles(const QStringList &files, QSettings &settings)
static QString RecentFilesKey()
static QStringList readRecentFiles(QSettings &settings)
bool loadFile(const QString &fileName)
QString userFriendlyCurrentFile()
QString currentFile() const
void promptForFileToOpen()
bool eventFilter(QObject *obj, QEvent *e) override
void closeEvent(QCloseEvent *event) override
void updateRecentFileActions()
CodeEditor * createChildCodeEditor()
PythonEditor(PythonInterpreter *interpreter)
void changeEvent(QEvent *e) override
void executionCalled(const std::string &evalStatement, QListWidget *output)
static QString SettingsApplicationName()
bool openFile(const QString &fileName)
void promptForFolderToOpen()
void projectTreeDoubleClicked(const QModelIndex &index)
void executeCode(const std::string &code, QListWidget *output)
static const std::string path
bool Copy(const std::string &from, const std::string &to, bool include_parent_dir=false, const std::string &extname="")
Copy a file or directory.
std::string toString(T x)