File indexing completed on 2024-05-12 03:48:24
0001 /* 0002 File : CantorWorksheetView.cpp 0003 Project : LabPlot 0004 Description : View class for CantorWorksheet 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2015 Garvit Khatri <garvitdelhi@gmail.com> 0007 SPDX-FileCopyrightText: 2016-2023 Alexander Semke <alexander.semke@web.de> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "CantorWorksheetView.h" 0012 #include "backend/cantorWorksheet/CantorWorksheet.h" 0013 #include "backend/core/column/Column.h" 0014 #include "kdefrontend/spreadsheet/PlotDataDialog.h" 0015 #include "kdefrontend/spreadsheet/StatisticsDialog.h" 0016 0017 #include <QHBoxLayout> 0018 #include <QLabel> 0019 #include <QMenu> 0020 #include <QTimer> 0021 #include <QToolBar> 0022 0023 #include <KLocalizedString> 0024 #include <KParts/ReadWritePart> 0025 #include <KToggleAction> 0026 0027 CantorWorksheetView::CantorWorksheetView(CantorWorksheet* worksheet) 0028 : QWidget() 0029 , m_worksheet(worksheet) { 0030 auto* layout = new QHBoxLayout(this); 0031 layout->setContentsMargins(0, 0, 0, 0); 0032 m_part = worksheet->part(); 0033 if (m_part) { 0034 layout->addWidget(m_part->widget()); 0035 initActions(); 0036 connect(m_worksheet, &CantorWorksheet::requestProjectContextMenu, this, &CantorWorksheetView::createContextMenu); 0037 connect(m_worksheet, &CantorWorksheet::statusChanged, this, &CantorWorksheetView::statusChanged); 0038 } else { 0039 QString msg = QStringLiteral("<b>") + i18n("Failed to initialize %1.", m_worksheet->backendName()) + QStringLiteral("</b><br>"); 0040 msg += worksheet->error(); 0041 QLabel* label = new QLabel(msg); 0042 label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); 0043 layout->addWidget(label); 0044 } 0045 } 0046 0047 void CantorWorksheetView::initActions() { 0048 m_actionGroup = new QActionGroup(this); 0049 m_actionGroup->setExclusive(false); 0050 0051 // general notebook specific actions 0052 m_zoomIn = new QAction(QIcon::fromTheme(QLatin1String("zoom-in")), i18n("Zoom In"), m_actionGroup); 0053 m_zoomIn->setData(QStringLiteral("view_zoom_in")); 0054 m_zoomIn->setShortcut(Qt::CTRL + Qt::Key_Plus); 0055 0056 m_zoomOut = new QAction(QIcon::fromTheme(QLatin1String("zoom-out")), i18n("Zoom Out"), m_actionGroup); 0057 m_zoomOut->setData(QStringLiteral("view_zoom_out")); 0058 m_zoomOut->setShortcut(Qt::CTRL + Qt::Key_Minus); 0059 0060 m_find = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Find"), m_actionGroup); 0061 m_find->setData(QStringLiteral("edit_find")); 0062 m_find->setShortcut(Qt::CTRL + Qt::Key_F); 0063 0064 m_replace = new QAction(QIcon::fromTheme(QLatin1String("edit-find-replace")), i18n("Replace"), m_actionGroup); 0065 m_replace->setData(QStringLiteral("edit_replace")); 0066 m_replace->setShortcut(Qt::CTRL + Qt::Key_R); 0067 0068 m_restartBackendAction = new QAction(QIcon::fromTheme(QLatin1String("system-reboot")), i18n("Restart Backend"), m_actionGroup); 0069 m_restartBackendAction->setData(QStringLiteral("restart_backend")); 0070 0071 m_evaluateWorsheetAction = new QAction(QIcon::fromTheme(QLatin1String("system-run")), i18n("Evaluate Notebook"), m_actionGroup); 0072 m_evaluateWorsheetAction->setData(QStringLiteral("evaluate_worksheet")); 0073 0074 // all other actions are initialized in initMenus() since they are only required for the main and context menu 0075 0076 connect(m_actionGroup, &QActionGroup::triggered, this, &CantorWorksheetView::triggerAction); 0077 } 0078 0079 void CantorWorksheetView::initMenus() { 0080 // initialize the remaining actions 0081 0082 // entry specific actions 0083 m_evaluateEntryAction = new QAction(QIcon::fromTheme(QLatin1String("media-playback-start")), i18n("Evaluate Entry"), m_actionGroup); 0084 m_evaluateEntryAction->setShortcut(Qt::SHIFT + Qt::Key_Return); 0085 m_evaluateEntryAction->setData(QStringLiteral("evaluate_current")); 0086 0087 m_removeCurrentEntryAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Remove Current Entry"), m_actionGroup); 0088 m_removeCurrentEntryAction->setData(QStringLiteral("remove_current")); 0089 0090 // actions for the "Add New" menu 0091 auto* insertCommandEntryAction = new QAction(QIcon::fromTheme(QLatin1String("run-build")), i18n("Command"), m_actionGroup); 0092 insertCommandEntryAction->setData(QStringLiteral("insert_command_entry")); 0093 insertCommandEntryAction->setShortcut(Qt::CTRL + Qt::Key_Return); 0094 0095 auto* insertTextEntryAction = new QAction(QIcon::fromTheme(QLatin1String("draw-text")), i18n("Text"), m_actionGroup); 0096 insertTextEntryAction->setData(QStringLiteral("insert_text_entry")); 0097 0098 // markdown entry is only available if cantor was compiled with libdiscovery (cantor 18.12 and later) 0099 QAction* insertMarkdownEntryAction = nullptr; 0100 if (m_part->action("insert_markdown_entry")) { 0101 insertMarkdownEntryAction = new QAction(QIcon::fromTheme(QLatin1String("text-x-markdown")), i18n("Markdown"), m_actionGroup); 0102 insertMarkdownEntryAction->setData(QStringLiteral("insert_markdown_entry")); 0103 } 0104 0105 auto* insertLatexEntryAction = new QAction(QIcon::fromTheme(QLatin1String("text-x-tex")), i18n("LaTeX"), m_actionGroup); 0106 insertLatexEntryAction->setData(QStringLiteral("insert_latex_entry")); 0107 0108 auto* insertImageEntryAction = new QAction(QIcon::fromTheme(QLatin1String("image-x-generic")), i18n("Image"), m_actionGroup); 0109 insertImageEntryAction->setData(QStringLiteral("insert_image_entry")); 0110 0111 auto* insertPageBreakAction = new QAction(QIcon::fromTheme(QLatin1String("go-next-view-page")), i18n("Page Break"), m_actionGroup); 0112 insertPageBreakAction->setData(QStringLiteral("insert_page_break_entry")); 0113 0114 // auto* insertHorizLineAction = new QAction(QIcon::fromTheme(QLatin1String("newline")), i18n("Horizontal Line"), m_actionGroup); 0115 // insertHorizLineAction->setData(QStringLiteral("insert_horizontal_line_entry")); 0116 0117 // auto* insertHierarchyEntryAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-tree")), i18n("Hierarchy"), m_actionGroup); 0118 // insertHierarchyEntryAction->setData(QStringLiteral("insert_hierarchy_entry")); 0119 0120 // actions for "assistants", that are backend specific and not always available 0121 QAction* computeEigenvectorsAction = nullptr; 0122 if (m_part->action("eigenvectors_assistant")) { 0123 computeEigenvectorsAction = new QAction(i18n("Compute Eigenvectors"), m_actionGroup); 0124 computeEigenvectorsAction->setData(QStringLiteral("eigenvectors_assistant")); 0125 } 0126 0127 QAction* createMatrixAction = nullptr; 0128 if (m_part->action("creatematrix_assistant")) { 0129 createMatrixAction = new QAction(i18n("Create Matrix"), m_actionGroup); 0130 createMatrixAction->setData(QStringLiteral("creatematrix_assistant")); 0131 } 0132 0133 QAction* computeEigenvaluesAction = nullptr; 0134 if (m_part->action("eigenvalues_assistant")) { 0135 computeEigenvaluesAction = new QAction(i18n("Compute Eigenvalues"), m_actionGroup); 0136 computeEigenvaluesAction->setData(QStringLiteral("eigenvalues_assistant")); 0137 } 0138 0139 QAction* invertMatrixAction = nullptr; 0140 if (m_part->action("invertmatrix_assistant")) { 0141 invertMatrixAction = new QAction(i18n("Invert Matrix"), m_actionGroup); 0142 invertMatrixAction->setData(QStringLiteral("invertmatrix_assistant")); 0143 } 0144 0145 QAction* differentiationAction = nullptr; 0146 if (m_part->action("differentiate_assistant")) { 0147 differentiationAction = new QAction(i18n("Differentiation"), m_actionGroup); 0148 differentiationAction->setData(QStringLiteral("differentiate_assistant")); 0149 } 0150 0151 QAction* integrationAction = nullptr; 0152 if (m_part->action("integrate_assistant")) { 0153 integrationAction = new QAction(i18n("Integration"), m_actionGroup); 0154 integrationAction->setData(QStringLiteral("integrate_assistant")); 0155 } 0156 0157 QAction* solveEquationsAction = nullptr; 0158 if (m_part->action("solve_assistant")) { 0159 solveEquationsAction = new QAction(i18n("Solve Equations"), m_actionGroup); 0160 solveEquationsAction->setData(QStringLiteral("solve_assistant")); 0161 } 0162 0163 // menus 0164 0165 //"Add New" 0166 m_addNewMenu = new QMenu(i18n("Add New"), m_part->widget()); 0167 m_addNewMenu->setIcon(QIcon::fromTheme(QLatin1String("list-add"))); 0168 0169 m_addNewMenu->addAction(insertCommandEntryAction); 0170 m_addNewMenu->addAction(insertTextEntryAction); 0171 if (insertMarkdownEntryAction) 0172 m_addNewMenu->addAction(insertMarkdownEntryAction); 0173 m_addNewMenu->addAction(insertLatexEntryAction); 0174 m_addNewMenu->addAction(insertImageEntryAction); 0175 m_addNewMenu->addSeparator(); 0176 m_addNewMenu->addAction(insertPageBreakAction); 0177 // m_addNewMenu->addAction(insertHorizLineAction); 0178 // m_addNewMenu->addAction(insertHierarchyEntryAction); 0179 0180 //"Assistants" 0181 if (invertMatrixAction || createMatrixAction || computeEigenvectorsAction || computeEigenvaluesAction) { 0182 m_linearAlgebraMenu = new QMenu(i18n("Linear Algebra"), m_part->widget()); 0183 if (invertMatrixAction) 0184 m_linearAlgebraMenu->addAction(invertMatrixAction); 0185 if (createMatrixAction) 0186 m_linearAlgebraMenu->addAction(createMatrixAction); 0187 if (computeEigenvectorsAction) 0188 m_linearAlgebraMenu->addAction(computeEigenvectorsAction); 0189 if (computeEigenvaluesAction) 0190 m_linearAlgebraMenu->addAction(computeEigenvaluesAction); 0191 } 0192 0193 if (solveEquationsAction || integrationAction || differentiationAction) { 0194 m_calculateMenu = new QMenu(i18n("Calculate"), m_part->widget()); 0195 if (solveEquationsAction) 0196 m_calculateMenu->addAction(solveEquationsAction); 0197 if (integrationAction) 0198 m_calculateMenu->addAction(integrationAction); 0199 if (differentiationAction) 0200 m_calculateMenu->addAction(differentiationAction); 0201 } 0202 0203 //"Notebook Settings" 0204 m_settingsMenu = new QMenu(i18n("Settings"), m_part->widget()); 0205 m_settingsMenu->setIcon(QIcon::fromTheme(QLatin1String("settings-configure"))); 0206 m_settingsMenu->addAction(m_part->action("enable_expression_numbers")); 0207 m_settingsMenu->addAction(m_part->action("enable_highlighting")); 0208 m_settingsMenu->addAction(m_part->action("enable_completion")); 0209 m_settingsMenu->addAction(m_part->action("enable_animations")); 0210 m_settingsMenu->addSeparator(); 0211 m_settingsMenu->addAction(m_part->action("enable_typesetting")); 0212 } 0213 0214 /*! 0215 * Populates the menu \c menu with the CantorWorksheet and CantorWorksheet view relevant actions. 0216 * The menu is used 0217 * - as the context menu in CantorWorksheetView 0218 * - as the "CantorWorksheet menu" in the main menu-bar (called form MainWin) 0219 * - as a part of the CantorWorksheet context menu in project explorer 0220 */ 0221 void CantorWorksheetView::createContextMenu(QMenu* menu) { 0222 Q_ASSERT(menu); 0223 if (!m_part) 0224 return; 0225 0226 QAction* firstAction = nullptr; 0227 // if we're populating the context menu for the project explorer, then 0228 // there're already actions available there. Skip the first title-action 0229 // and insert the action at the beginning of the menu. 0230 if (menu->actions().size() > 1) 0231 firstAction = menu->actions().at(1); 0232 0233 if (!m_addNewMenu) 0234 initMenus(); 0235 0236 menu->insertAction(firstAction, m_evaluateWorsheetAction); 0237 menu->insertSeparator(firstAction); 0238 menu->insertAction(firstAction, m_evaluateEntryAction); 0239 menu->addSeparator(); 0240 menu->insertMenu(firstAction, m_addNewMenu); 0241 menu->insertSeparator(firstAction); 0242 menu->insertAction(firstAction, m_removeCurrentEntryAction); 0243 0244 // results related actions 0245 menu->insertSeparator(firstAction); 0246 menu->addAction(m_part->action("all_entries_collapse_results")); 0247 menu->addAction(m_part->action("all_entries_uncollapse_results")); 0248 menu->addAction(m_part->action("all_entries_remove_all_results")); 0249 0250 // assistants, if available 0251 if (m_linearAlgebraMenu || m_calculateMenu) { 0252 menu->insertSeparator(firstAction); 0253 auto* menuAssistants = new QMenu(i18n("Assistants"), m_part->widget()); 0254 menuAssistants->setIcon(QIcon::fromTheme(QLatin1String("quickwizard"))); 0255 if (m_linearAlgebraMenu) 0256 menuAssistants->addMenu(m_linearAlgebraMenu); 0257 if (m_calculateMenu) 0258 menuAssistants->addMenu(m_calculateMenu); 0259 0260 menu->insertMenu(firstAction, menuAssistants); 0261 } 0262 0263 menu->insertSeparator(firstAction); 0264 menu->insertAction(firstAction, m_zoomIn); 0265 menu->insertAction(firstAction, m_zoomOut); 0266 menu->insertSeparator(firstAction); 0267 menu->insertAction(firstAction, m_find); 0268 menu->insertAction(firstAction, m_replace); 0269 menu->insertSeparator(firstAction); 0270 menu->insertMenu(firstAction, m_settingsMenu); 0271 menu->insertSeparator(firstAction); 0272 menu->insertAction(firstAction, m_restartBackendAction); 0273 menu->insertSeparator(firstAction); 0274 } 0275 0276 /*! 0277 * adds column specific actions in SpreadsheetView to the context menu shown in the project explorer. 0278 */ 0279 void CantorWorksheetView::fillColumnContextMenu(QMenu* menu, Column* column) { 0280 if (!column) 0281 return; // should never happen, since the sender is always a Column 0282 0283 m_contextMenuColumn = column; 0284 0285 if (!m_plotDataMenu) { 0286 auto* plotDataActionGroup = new QActionGroup(this); 0287 connect(plotDataActionGroup, &QActionGroup::triggered, this, &CantorWorksheetView::plotData); 0288 m_plotDataMenu = new QMenu(i18n("Plot Data"), this); 0289 PlotDataDialog::fillMenu(m_plotDataMenu, plotDataActionGroup); 0290 0291 m_statisticsAction = new QAction(QIcon::fromTheme(QStringLiteral("view-statistics")), i18n("Variable Statistics..."), this); 0292 connect(m_statisticsAction, &QAction::triggered, this, &CantorWorksheetView::showStatistics); 0293 } 0294 0295 const bool hasValues = column->hasValues(); 0296 const bool plottable = column->isPlottable(); 0297 0298 QAction* firstAction = menu->actions().at(1); 0299 menu->insertMenu(firstAction, m_plotDataMenu); 0300 menu->insertSeparator(firstAction); 0301 m_plotDataMenu->setEnabled(plottable && hasValues); 0302 0303 menu->insertSeparator(firstAction); 0304 menu->insertAction(firstAction, m_statisticsAction); 0305 m_statisticsAction->setEnabled(hasValues); 0306 } 0307 0308 void CantorWorksheetView::fillToolBar(QToolBar* toolbar) { 0309 if (!m_part) 0310 return; 0311 toolbar->addAction(m_evaluateWorsheetAction); 0312 toolbar->addAction(m_find); 0313 toolbar->addAction(m_zoomIn); 0314 toolbar->addAction(m_zoomOut); 0315 toolbar->addSeparator(); 0316 toolbar->addAction(m_restartBackendAction); 0317 } 0318 0319 /*! 0320 * Slot for actions triggered 0321 */ 0322 void CantorWorksheetView::triggerAction(QAction* action) { 0323 const auto& name = action->data().toString(); 0324 if (!name.isEmpty()) { 0325 auto* action = m_part->action(name.toStdString().c_str()); 0326 if (action) 0327 action->trigger(); 0328 } 0329 } 0330 0331 CantorWorksheetView::~CantorWorksheetView() { 0332 if (m_part) 0333 m_part->widget()->setParent(nullptr); 0334 } 0335 0336 void CantorWorksheetView::statusChanged(Cantor::Session::Status status) { 0337 if (status == Cantor::Session::Running) { 0338 m_evaluateWorsheetAction->setText(i18n("Interrupt")); 0339 m_evaluateWorsheetAction->setIcon(QIcon::fromTheme(QLatin1String("dialog-close"))); 0340 Q_EMIT m_worksheet->statusInfo(i18n("Calculating...")); 0341 } else { 0342 m_evaluateWorsheetAction->setText(i18n("Evaluate Notebook")); 0343 m_evaluateWorsheetAction->setIcon(QIcon::fromTheme(QLatin1String("system-run"))); 0344 Q_EMIT m_worksheet->statusInfo(i18n("Ready")); 0345 } 0346 } 0347 0348 void CantorWorksheetView::plotData(QAction* action) { 0349 if (!m_contextMenuColumn) 0350 return; 0351 0352 auto type = static_cast<PlotDataDialog::PlotType>(action->data().toInt()); 0353 auto* dlg = new PlotDataDialog(m_worksheet, type); 0354 dlg->setSelectedColumns(QVector<Column*>({m_contextMenuColumn})); 0355 dlg->exec(); 0356 } 0357 0358 void CantorWorksheetView::showStatistics() { 0359 if (!m_contextMenuColumn) 0360 return; 0361 0362 QString dlgTitle(i18n("%1: variable statistics", m_contextMenuColumn->name())); 0363 auto* dlg = new StatisticsDialog(dlgTitle, QVector<Column*>({m_contextMenuColumn})); 0364 dlg->setModal(true); 0365 dlg->show(); 0366 QApplication::processEvents(QEventLoop::AllEvents, 0); 0367 QTimer::singleShot(0, this, [=]() { 0368 dlg->showStatistics(); 0369 }); 0370 }