File indexing completed on 2024-04-21 03:41:34

0001 /*************************************************************************************
0002  *  Copyright (C) 2007 by Aleix Pol <aleixpol@kde.org>                               *
0003  *                                                                                   *
0004  *  This program is free software; you can redistribute it and/or                    *
0005  *  modify it under the terms of the GNU General Public License                      *
0006  *  as published by the Free Software Foundation; either version 2                   *
0007  *  of the License, or (at your option) any later version.                           *
0008  *                                                                                   *
0009  *  This program is distributed in the hope that it will be useful,                  *
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
0012  *  GNU General Public License for more details.                                     *
0013  *                                                                                   *
0014  *  You should have received a copy of the GNU General Public License                *
0015  *  along with this program; if not, write to the Free Software                      *
0016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
0017  *************************************************************************************/
0018 
0019 #include "kalgebra.h"
0020 #include "askname.h"
0021 #include "consolehtml.h"
0022 #include "dictionary.h"
0023 #include "functionedit.h"
0024 #include "varedit.h"
0025 #include "variablesdelegate.h"
0026 #include "viewportwidget.h"
0027 #include <analitzagui/plotsview3d_es.h>
0028 
0029 #include <analitza/value.h>
0030 #include <analitza/variables.h>
0031 #include <analitzagui/expressionedit.h>
0032 #include <analitzagui/operatorsmodel.h>
0033 #include <analitzagui/plotsview2d.h>
0034 #include <analitzagui/variablesmodel.h>
0035 #include <analitzaplot/functiongraph.h>
0036 #include <analitzaplot/planecurve.h>
0037 #include <analitzaplot/plotsfactory.h>
0038 #include <analitzaplot/plotsmodel.h>
0039 
0040 #include <KConfig>
0041 #include <KConfigGroup>
0042 #include <KHelpMenu>
0043 #include <KLocalizedString>
0044 #include <KRecentFilesAction>
0045 #include <KStandardAction>
0046 #include <KToggleFullScreenAction>
0047 #include <QAction>
0048 #include <QActionGroup>
0049 #include <QApplication>
0050 #include <QDockWidget>
0051 #include <QFileDialog>
0052 #include <QHeaderView>
0053 #include <QMenuBar>
0054 #include <QPointer>
0055 #include <QProcess>
0056 #include <QRandomGenerator>
0057 #include <QStatusBar>
0058 #include <QTableView>
0059 #include <QToolButton>
0060 #include <QVBoxLayout>
0061 
0062 using namespace Qt::Literals::StringLiterals;
0063 
0064 class Add2DOption : public InlineOptions
0065 {
0066 public:
0067     Add2DOption(KAlgebra *c)
0068         : m_kalgebra(c)
0069     {
0070     }
0071 
0072     QString id() const override
0073     {
0074         return QStringLiteral("add2d");
0075     }
0076     bool matchesExpression(const Analitza::Expression &exp) const override
0077     {
0078         return Analitza::PlotsFactory::self()->requestPlot(exp, Analitza::Dim2D).canDraw();
0079     }
0080 
0081     QString caption() const override
0082     {
0083         return i18n("Plot 2D");
0084     }
0085 
0086     void triggerOption(const Analitza::Expression &exp) override
0087     {
0088         m_kalgebra->add2D(exp);
0089     }
0090 
0091 private:
0092     KAlgebra *m_kalgebra;
0093 };
0094 
0095 class Add3DOption : public InlineOptions
0096 {
0097 public:
0098     Add3DOption(KAlgebra *c)
0099         : m_kalgebra(c)
0100     {
0101     }
0102 
0103     QString id() const override
0104     {
0105         return QStringLiteral("add3d");
0106     }
0107     bool matchesExpression(const Analitza::Expression &exp) const override
0108     {
0109         return Analitza::PlotsFactory::self()->requestPlot(exp, Analitza::Dim3D).canDraw();
0110     }
0111 
0112     QString caption() const override
0113     {
0114         return i18n("Plot 3D");
0115     }
0116 
0117     void triggerOption(const Analitza::Expression &exp) override
0118     {
0119         m_kalgebra->add3D(exp);
0120     }
0121 
0122 private:
0123     KAlgebra *m_kalgebra;
0124 };
0125 
0126 QColor randomFunctionColor()
0127 {
0128     return QColor::fromHsv(QRandomGenerator::global()->bounded(255), 255, 255);
0129 }
0130 
0131 KAlgebra::KAlgebra(QWidget *parent)
0132     : QMainWindow(parent)
0133 {
0134     resize(900, 500);
0135 
0136     m_tabs = new QTabWidget;
0137     setCentralWidget(m_tabs);
0138 
0139     setStatusBar(new QStatusBar(this));
0140     setMenuBar(new QMenuBar(this));
0141 
0142     KToggleFullScreenAction *fullScreenAction = KStandardAction::fullScreen(this, SLOT(fullScreen(bool)), this, this);
0143 
0144     QMenu *g_menu = menuBar()->addMenu(i18n("Session"));
0145     g_menu->addAction(KStandardAction::openNew(this, SLOT(newInstance()), this));
0146     g_menu->addAction(fullScreenAction);
0147     g_menu->addSeparator();
0148     g_menu->addAction(KStandardAction::quit(this, SLOT(close()), this));
0149 
0150     QToolButton *fullScreenButton = new QToolButton(this);
0151     fullScreenButton->setDefaultAction(fullScreenAction);
0152     m_tabs->setCornerWidget(fullScreenButton);
0153 
0154     m_status = new QLabel(this);
0155     statusBar()->insertWidget(0, m_status);
0156     menuBar()->addAction(QStringLiteral("|"))->setEnabled(false);
0157 
0158     ///////Console
0159     QWidget *console = new QWidget(m_tabs);
0160     QVBoxLayout *c_layo = new QVBoxLayout(console);
0161     c_results = new ConsoleHtml(console);
0162     c_results->setFocusPolicy(Qt::NoFocus);
0163     c_dock_vars = new QDockWidget(i18n("Variables"), this);
0164     c_dock_vars->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
0165     addDockWidget(Qt::RightDockWidgetArea, c_dock_vars);
0166 
0167     c_varsModel = new Analitza::VariablesModel(c_results->analitza()->variables());
0168     c_varsModel->setEditable(false);
0169 
0170     c_variables = new QTreeView(c_dock_vars);
0171     c_variables->setModel(c_varsModel);
0172     c_variables->setRootIsDecorated(false);
0173     c_variables->header()->setStretchLastSection(true);
0174     c_variables->setSelectionBehavior(QAbstractItemView::SelectRows);
0175     c_variables->setSelectionMode(QAbstractItemView::SingleSelection);
0176 
0177     c_exp = new Analitza::ExpressionEdit(console);
0178     c_exp->setAnalitza(c_results->analitza());
0179     c_exp->setExamples(QStringList() << QStringLiteral("square:=x->x**2") << QStringLiteral("fib:=n->piecewise { eq(n,0)?0, eq(n,1)?1, ?fib(n-1)+fib(n-2) }"));
0180     c_dock_vars->setWidget(c_variables);
0181 
0182     m_tabs->addTab(console, i18n("&Calculator"));
0183     console->setLayout(c_layo);
0184     c_layo->addWidget(c_results);
0185     c_layo->addWidget(c_exp);
0186 
0187     connect(c_exp, &Analitza::ExpressionEdit::returnPressed, this, &KAlgebra::operate);
0188     connect(c_results, &ConsoleHtml::status, this, &KAlgebra::changeStatusBar);
0189     connect(c_results, &ConsoleHtml::changed, this, &KAlgebra::updateInformation);
0190     connect(c_results, SIGNAL(changed()), c_exp, SLOT(updateCompleter()));
0191     connect(c_results, SIGNAL(paste(QString)), c_exp, SLOT(insertText(QString)));
0192     connect(c_variables, &QAbstractItemView::clicked, this, &KAlgebra::edit_var);
0193     ////////menu
0194     c_menu = menuBar()->addMenu(i18n("C&alculator"));
0195     c_menu->addAction(QIcon::fromTheme(QStringLiteral("document-open")),
0196                       i18nc("@item:inmenu", "&Load Script..."),
0197                       Qt::CTRL | Qt::Key_L,
0198                       this,
0199                       SLOT(loadScript()));
0200     c_recentScripts = new KRecentFilesAction(QIcon::fromTheme(QStringLiteral("document-open-recent")), i18n("Recent Scripts"), this);
0201     connect(c_recentScripts, SIGNAL(urlSelected(QUrl)), this, SLOT(loadScript(QUrl)));
0202     c_menu->addAction(c_recentScripts);
0203 
0204     c_menu->addAction(QIcon::fromTheme(QStringLiteral("document-save")),
0205                       i18nc("@item:inmenu", "&Save Script..."),
0206                       Qt::CTRL | Qt::Key_G,
0207                       this,
0208                       &KAlgebra::saveScript);
0209     c_menu->addAction(QIcon::fromTheme(QStringLiteral("document-save")), i18nc("@item:inmenu", "&Export Log..."), QKeySequence::Save, this, &KAlgebra::saveLog);
0210     c_menu->addSeparator();
0211     c_menu->addAction(i18nc("@item:inmenu", "&Insert ans..."), Qt::Key_F3, this, &KAlgebra::insertAns);
0212     c_menu->addSeparator()->setText(i18n("Execution Mode"));
0213     QActionGroup *execGroup = new QActionGroup(c_menu);
0214     QAction *calc = c_menu->addAction(i18nc("@item:inmenu", "Calculate"), this, &KAlgebra::consoleCalculate);
0215     QAction *eval = c_menu->addAction(i18nc("@item:inmenu", "Evaluate"), this, &KAlgebra::consoleEvaluate);
0216 
0217     calc->setCheckable(true);
0218     eval->setCheckable(true);
0219     eval->setChecked(true);
0220     execGroup->addAction(calc);
0221     execGroup->addAction(eval);
0222     c_menu->addSeparator();
0223     c_menu->addAction(KStandardAction::clear(c_results, SLOT(clear()), this));
0224     initializeRecentScripts();
0225     ////////////
0226     //////EOConsola
0227 
0228     //////2D Graph
0229     b_funcsModel = new Analitza::PlotsModel(this);
0230 
0231     m_graph2d = new Analitza::PlotsView2D(m_tabs);
0232     m_graph2d->setTicksShown(Qt::Orientation(0));
0233     m_graph2d->setModel(b_funcsModel);
0234 
0235     b_dock_funcs = new QDockWidget(i18n("Functions"), this);
0236     b_tools = new QTabWidget(b_dock_funcs);
0237     b_tools->setTabPosition(QTabWidget::South);
0238     addDockWidget(Qt::RightDockWidgetArea, b_dock_funcs);
0239 
0240     b_funcs = new QTreeView(b_tools);
0241     b_funcs->setRootIsDecorated(false);
0242     b_funcs->setModel(b_funcsModel);
0243     b_funcs->header()->resizeSections(QHeaderView::ResizeToContents);
0244     b_funcs->setSelectionMode(QAbstractItemView::SingleSelection);
0245     m_graph2d->setSelectionModel(b_funcs->selectionModel());
0246 
0247     b_tools->addTab(b_funcs, i18n("List"));
0248 
0249     b_funced = new FunctionEdit(b_tools);
0250     b_funced->setVariables(c_varsModel->variables());
0251     connect(b_funced, &FunctionEdit::accept, this, &KAlgebra::new_func);
0252     connect(b_funced, &FunctionEdit::removeEditingPlot, this, &KAlgebra::remove_func);
0253     b_tools->addTab(b_funced, QIcon::fromTheme(QStringLiteral("list-add")), i18n("&Add"));
0254 
0255     QTableView *b_varsView = new QTableView(b_tools);
0256     b_varsModel = new Analitza::VariablesModel(b_funced->variables());
0257     b_varsView->setModel(b_varsModel);
0258     b_varsView->setShowGrid(false);
0259     b_varsView->verticalHeader()->hide();
0260     b_varsView->horizontalHeader()->setStretchLastSection(true);
0261     b_varsView->setSelectionBehavior(QAbstractItemView::SelectRows);
0262     b_varsView->setContextMenuPolicy(Qt::CustomContextMenu);
0263     VariablesDelegate *delegate = new VariablesDelegate(b_varsView);
0264     b_varsView->setItemDelegate(delegate);
0265     b_tools->addTab(b_varsView, i18n("Variables"));
0266 
0267     ViewportWidget *b_viewport = new ViewportWidget(this);
0268     b_viewport->setViewport(m_graph2d->definedViewport());
0269     b_tools->addTab(b_viewport, i18n("Viewport"));
0270 
0271     b_dock_funcs->setWidget(b_tools);
0272     b_dock_funcs->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
0273     m_tabs->addTab(m_graph2d, i18n("&2D Graph"));
0274     c_results->addOptionsObserver(new Add2DOption(this));
0275 
0276     connect(b_varsModel, &QAbstractItemModel::dataChanged, this, &KAlgebra::valueChanged);
0277     connect(b_funcs, &QAbstractItemView::doubleClicked, this, &KAlgebra::edit_func);
0278     connect(b_tools, &QTabWidget::currentChanged, this, &KAlgebra::functools);
0279     connect(m_graph2d, &Analitza::PlotsView2D::status, this, &KAlgebra::changeStatusBar);
0280     connect(m_graph2d, SIGNAL(viewportChanged(QRectF)), b_viewport, SLOT(setViewport(QRectF)));
0281     connect(b_viewport, SIGNAL(viewportChange(QRectF)), m_graph2d, SLOT(changeViewport(QRectF)));
0282     connect(b_varsView, &QWidget::customContextMenuRequested, this, &KAlgebra::varsContextMenu);
0283 
0284     ////////menu
0285     b_menu = menuBar()->addMenu(i18n("2&D Graph"));
0286     QAction *b_actions[6];
0287     b_actions[0] = b_menu->addAction(i18n("&Grid"), this, &KAlgebra::toggleSquares);
0288     b_actions[1] = b_menu->addAction(i18n("&Keep Aspect Ratio"), this, &KAlgebra::toggleKeepAspect);
0289     b_menu->addAction(KStandardAction::save(this, SLOT(saveGraph()), this));
0290     b_menu->addSeparator();
0291     b_menu->addAction(KStandardAction::zoomIn(m_graph2d, SLOT(zoomIn()), this));
0292     b_menu->addAction(KStandardAction::zoomOut(m_graph2d, SLOT(zoomOut()), this));
0293     QAction *ac = KStandardAction::actualSize(m_graph2d, SLOT(resetViewport()), this);
0294     ac->setShortcut({Qt::ControlModifier | Qt::Key_0});
0295     b_menu->addAction(ac);
0296     b_menu->addSeparator()->setText(i18n("Resolution"));
0297     b_actions[2] = b_menu->addAction(i18nc("@item:inmenu", "Poor"), this, &KAlgebra::set_res_low);
0298     b_actions[3] = b_menu->addAction(i18nc("@item:inmenu", "Normal"), this, &KAlgebra::set_res_std);
0299     b_actions[4] = b_menu->addAction(i18nc("@item:inmenu", "Fine"), this, &KAlgebra::set_res_fine);
0300     b_actions[5] = b_menu->addAction(i18nc("@item:inmenu", "Very Fine"), this, &KAlgebra::set_res_vfine);
0301     m_graph2d->setContextMenuPolicy(Qt::ActionsContextMenu);
0302     m_graph2d->addActions(b_menu->actions());
0303 
0304     QActionGroup *res = new QActionGroup(b_menu);
0305     res->addAction(b_actions[2]);
0306     res->addAction(b_actions[3]);
0307     res->addAction(b_actions[4]);
0308     res->addAction(b_actions[5]);
0309 
0310     b_actions[0]->setCheckable(true);
0311     b_actions[0]->setChecked(true);
0312     b_actions[1]->setCheckable(true);
0313     b_actions[1]->setChecked(true);
0314     b_actions[2]->setCheckable(true);
0315     b_actions[3]->setCheckable(true);
0316     b_actions[3]->setChecked(true);
0317     b_actions[4]->setCheckable(true);
0318     b_actions[5]->setCheckable(true);
0319     set_res_std();
0320     //////EO2D Graph
0321 
0322     /////3DGraph
0323     QWidget *tridim = new QWidget(m_tabs);
0324     QVBoxLayout *t_layo = new QVBoxLayout(tridim);
0325     t_exp = new Analitza::ExpressionEdit(tridim);
0326     t_exp->setExamples(Analitza::PlotsFactory::self()->examples(Analitza::Dim3D));
0327     t_exp->setAns(QStringLiteral("x"));
0328     t_model3d = new Analitza::PlotsModel(this);
0329     m_graph3d = new Analitza::PlotsView3DES(tridim);
0330     m_graph3d->setModel(t_model3d);
0331     m_graph3d->setUseSimpleRotation(true);
0332 
0333     tridim->setLayout(t_layo);
0334     m_tabs->addTab(tridim, i18n("&3D Graph"));
0335     t_layo->addWidget(m_graph3d);
0336     t_layo->addWidget(t_exp);
0337 
0338     connect(t_exp, &Analitza::ExpressionEdit::returnPressed, this, &KAlgebra::new_func3d);
0339     c_results->addOptionsObserver(new Add3DOption(this));
0340 
0341     ////////menu
0342     t_menu = menuBar()->addMenu(i18n("3D &Graph"));
0343     QAction *t_actions[5];
0344     t_menu->addAction(KStandardAction::save(this, SLOT(save3DGraph()), this));
0345     t_menu->addAction(QIcon::fromTheme(QStringLiteral("zoom-original")), i18n("&Reset View"), m_graph3d, [this]() {
0346         m_graph3d->resetViewport();
0347     });
0348     t_menu->addSeparator();
0349     t_actions[2] = t_menu->addAction(i18n("Dots"), this, &KAlgebra::set_dots);
0350     t_actions[3] = t_menu->addAction(i18n("Lines"), this, &KAlgebra::set_lines);
0351     t_actions[4] = t_menu->addAction(i18n("Solid"), this, &KAlgebra::set_solid);
0352 
0353     QActionGroup *t_type = new QActionGroup(t_menu);
0354     t_type->addAction(t_actions[2]);
0355     t_type->addAction(t_actions[3]);
0356     t_type->addAction(t_actions[4]);
0357 
0358     t_actions[2]->setCheckable(true);
0359     t_actions[3]->setCheckable(true);
0360     t_actions[4]->setCheckable(true);
0361     t_actions[4]->setChecked(true);
0362 
0363     ////////////
0364     //////EO3D Graph
0365     menuBar()->addAction(QStringLiteral("|"))->setEnabled(false);
0366 
0367     // Dictionary tab
0368     d_dock = new QDockWidget(i18n("Operations"), this);
0369     d_dock->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable);
0370     addDockWidget(Qt::RightDockWidgetArea, d_dock);
0371     Dictionary *dic = new Dictionary(m_tabs);
0372     m_tabs->addTab(dic, i18n("&Dictionary"));
0373 
0374     QWidget *w = new QWidget;
0375     QLayout *leftLayo = new QVBoxLayout(w);
0376     d_filter = new QLineEdit(w);
0377     d_filter->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
0378     connect(d_filter, &QLineEdit::textChanged, dic, &Dictionary::setFilter);
0379     connect(d_filter, &QLineEdit::textChanged, this, &KAlgebra::dictionaryFilterChanged);
0380     d_list = new QListView(w);
0381     d_list->setModel(dic->model());
0382     d_list->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding));
0383     leftLayo->addWidget(new QLabel(i18n("Look for:"), d_dock));
0384     leftLayo->addWidget(d_filter);
0385     leftLayo->addWidget(d_list);
0386     d_dock->setWidget(w);
0387 
0388     connect(d_list->selectionModel(), &QItemSelectionModel::currentChanged, dic, &Dictionary::activated);
0389 
0390     // EODictionary
0391     // Ego's reminder
0392     KHelpMenu *help = new KHelpMenu(this);
0393     menuBar()->addMenu(help->menu());
0394 
0395 #pragma message("TODO: Port to PlotsModel")
0396     //     connect(b_funcsModel, SIGNAL(functionModified(QString,Analitza::Expression)),
0397     //             c_results, SLOT(modifyVariable(QString,Analitza::Expression)));
0398     //     connect(b_funcsModel, SIGNAL(functionRemoved(QString)),
0399     //             c_results, SLOT(removeVariable(QString)));
0400 
0401     connect(m_tabs, &QTabWidget::currentChanged, this, &KAlgebra::tabChanged);
0402     tabChanged(0);
0403 }
0404 
0405 KAlgebra::~KAlgebra()
0406 {
0407     KConfig conf(QStringLiteral("kalgebrarc"));
0408     KConfigGroup config(&conf, QStringLiteral("Default"));
0409     QStringList urls;
0410     const auto recentScripts = c_recentScripts->urls();
0411     for (const QUrl &url : recentScripts)
0412         urls += url.toDisplayString();
0413 
0414     config.writeEntry("recent", urls);
0415 }
0416 
0417 void KAlgebra::initializeRecentScripts()
0418 {
0419     KConfig conf(QStringLiteral("kalgebrarc"));
0420     KConfigGroup config(&conf, QStringLiteral("Default"));
0421 
0422     const QStringList urls = config.readEntry("recent", QStringList());
0423     for (const QString &url : urls) {
0424         c_recentScripts->addUrl(QUrl(url));
0425     }
0426 }
0427 
0428 void KAlgebra::newInstance()
0429 {
0430     QProcess::startDetached(QApplication::applicationFilePath(), QStringList());
0431 }
0432 
0433 void KAlgebra::add2D(const Analitza::Expression &exp)
0434 {
0435     qDebug() << "adding" << exp.toString();
0436 
0437     Analitza::PlotBuilder req = Analitza::PlotsFactory::self()->requestPlot(exp, Analitza::Dim2D, c_results->analitza()->variables());
0438     Analitza::PlotItem *curve = req.create(randomFunctionColor(), b_funcsModel->freeId());
0439     b_funcsModel->addPlot(curve);
0440 
0441     m_tabs->setCurrentIndex(1);
0442 }
0443 
0444 void KAlgebra::new_func()
0445 {
0446     Analitza::FunctionGraph *f = b_funced->createFunction();
0447 
0448     if (b_funced->editing()) {
0449         QModelIndex idx = b_funcsModel->indexForName(f->name());
0450         b_funcsModel->updatePlot(idx.row(), f);
0451     } else {
0452         b_funcsModel->addPlot(f);
0453     }
0454 
0455     b_funced->setEditing(false);
0456     b_funced->clear();
0457     b_tools->setCurrentIndex(0);
0458     b_funcs->setCurrentIndex(b_funcsModel->indexForName(f->name()));
0459     m_graph2d->setFocus();
0460 }
0461 
0462 void KAlgebra::remove_func()
0463 {
0464     Q_ASSERT(b_funced->editing());
0465     b_funcsModel->removeRow(b_funcs->currentIndex().row());
0466 
0467     b_funced->setEditing(false);
0468     b_funced->clear();
0469     b_tools->setCurrentIndex(0);
0470     b_funcs->setCurrentIndex(QModelIndex());
0471     m_graph2d->setFocus();
0472 }
0473 
0474 void KAlgebra::edit_func(const QModelIndex &idx)
0475 {
0476     b_tools->setTabText(1, i18n("&Editing"));
0477     b_tools->setCurrentIndex(1);
0478     b_funced->setName(b_funcsModel->data(idx.sibling(idx.row(), 0)).toString());
0479     b_funced->setFunction(b_funcsModel->data(idx.sibling(idx.row(), 1)).toString());
0480     b_funced->setEditing(true);
0481     b_funced->setFocus();
0482 }
0483 
0484 void KAlgebra::functools(int i)
0485 {
0486     if (i == 0)
0487         b_tools->setTabText(1, i18n("&Add"));
0488     else {
0489         b_funced->setName(b_funcsModel->freeId());
0490         b_funced->setColor(randomFunctionColor());
0491         b_funced->setEditing(false);
0492         b_funced->setFocus();
0493     }
0494 }
0495 
0496 void KAlgebra::edit_var(const QModelIndex &idx)
0497 {
0498     if (idx.column() == 0) {
0499         c_exp->insertText(idx.data().toString());
0500     } else {
0501         QModelIndex idxName = idx.sibling(idx.row(), 0);
0502 
0503         QPointer<VarEdit> e(new VarEdit(this, false));
0504         QString var = c_variables->model()->data(idxName, Qt::DisplayRole).toString();
0505 
0506         e->setAnalitza(c_results->analitza());
0507         e->setName(var);
0508 
0509         if (e->exec() == QDialog::Accepted) {
0510             QString str = var + u" := "_s + e->val().toString();
0511             c_results->addOperation(Analitza::Expression(str, false), str);
0512         }
0513         delete e;
0514     }
0515 }
0516 
0517 void KAlgebra::operate()
0518 {
0519     if (!c_exp->text().isEmpty()) {
0520         c_exp->setCorrect(c_results->addOperation(c_exp->expression(), c_exp->toPlainText()));
0521         c_exp->selectAll();
0522     }
0523 }
0524 
0525 void KAlgebra::changeStatusBar(const QString &msg)
0526 {
0527     //     statusBar()->showMessage(msg);
0528     m_status->setText(msg);
0529 }
0530 
0531 void KAlgebra::loadScript()
0532 {
0533     QUrl path = QFileDialog::getOpenFileUrl(this, i18n("Choose a script"), QUrl(), i18n("Script (*.kal)"));
0534 
0535     if (!path.isEmpty())
0536         loadScript(path);
0537 }
0538 
0539 void KAlgebra::loadScript(const QUrl &path)
0540 {
0541     bool loaded = c_results->loadScript(path);
0542 
0543     if (loaded)
0544         c_recentScripts->addUrl(path);
0545 }
0546 
0547 void KAlgebra::saveScript()
0548 {
0549     QUrl path = QFileDialog::getSaveFileUrl(this, QString(), QUrl(), i18n("Script (*.kal)"));
0550     bool loaded = false;
0551     if (!path.isEmpty())
0552         loaded = c_results->saveScript(path);
0553 
0554     if (loaded)
0555         c_recentScripts->addUrl(path);
0556 }
0557 
0558 void KAlgebra::saveLog()
0559 {
0560     QUrl path = QFileDialog::getSaveFileUrl(this, QString(), QUrl(), i18n("HTML File (*.html)"));
0561     if (!path.isEmpty())
0562         c_results->saveLog(path);
0563 }
0564 
0565 void KAlgebra::insertAns()
0566 {
0567     c_exp->insertText(QStringLiteral("ans"));
0568 }
0569 
0570 void KAlgebra::set_res_low()
0571 {
0572     b_funcsModel->setResolution(416);
0573 }
0574 void KAlgebra::set_res_std()
0575 {
0576     b_funcsModel->setResolution(832);
0577 }
0578 void KAlgebra::set_res_fine()
0579 {
0580     b_funcsModel->setResolution(1664);
0581 }
0582 void KAlgebra::set_res_vfine()
0583 {
0584     b_funcsModel->setResolution(3328);
0585 }
0586 
0587 void KAlgebra::new_func3d()
0588 {
0589     Analitza::Expression exp = t_exp->expression();
0590     Analitza::PlotBuilder plot = Analitza::PlotsFactory::self()->requestPlot(exp, Analitza::Dim3D, c_results->analitza()->variables());
0591     if (plot.canDraw()) {
0592         t_model3d->clear();
0593         t_model3d->addPlot(plot.create(Qt::yellow, QStringLiteral("func3d")));
0594     } else
0595         changeStatusBar(i18n("Errors: %1", plot.errors().join(i18n(", "))));
0596 }
0597 
0598 void KAlgebra::set_dots()
0599 {
0600     m_graph3d->setPlotStyle(Analitza::Dots);
0601 }
0602 
0603 void KAlgebra::set_lines()
0604 {
0605     m_graph3d->setPlotStyle(Analitza::Wired);
0606 }
0607 
0608 void KAlgebra::set_solid()
0609 {
0610     m_graph3d->setPlotStyle(Analitza::Solid);
0611 }
0612 
0613 void KAlgebra::save3DGraph()
0614 {
0615     QString path = QFileDialog::getSaveFileName(this, QString(), QString(), m_graph3d->filters().join(QStringLiteral(";;")));
0616     if (!path.isEmpty()) {
0617         m_graph3d->save(QUrl::fromLocalFile(path));
0618     }
0619 }
0620 
0621 void KAlgebra::toggleSquares()
0622 {
0623     m_graph2d->setTicksShown(m_graph2d->ticksShown() ? (Qt::Horizontal | Qt::Vertical) : ~(Qt::Horizontal | Qt::Vertical));
0624 }
0625 
0626 void KAlgebra::toggleKeepAspect()
0627 {
0628     m_graph2d->setKeepAspectRatio(!m_graph2d->keepAspectRatio());
0629 }
0630 
0631 void KAlgebra::saveGraph()
0632 {
0633     QPointer<QFileDialog> dialog =
0634         new QFileDialog(this, i18n("Select where to put the rendered plot"), QString(), i18n("Image File (*.png);;SVG File (*.svg)"));
0635     dialog->setOption(QFileDialog::DontConfirmOverwrite, false);
0636     if (dialog->exec() && !dialog->selectedFiles().isEmpty()) {
0637         QString filter = dialog->selectedNameFilter();
0638         QString filename = dialog->selectedFiles().first();
0639 
0640         bool isSvg = filename.endsWith(QLatin1String(".svg")) || (!filename.endsWith(QLatin1String(".png")) && filter.mid(2, 3) == QLatin1String("svg"));
0641         Analitza::PlotsView2D::Format f = isSvg ? Analitza::PlotsView2D::SVG : Analitza::PlotsView2D::PNG;
0642         m_graph2d->toImage(filename, f);
0643     }
0644     delete dialog;
0645 }
0646 
0647 void menuEnabledHelper(QMenu *m, bool enabled)
0648 {
0649     m->setEnabled(enabled);
0650     const auto actions = m->actions();
0651     for (QAction *action : actions) {
0652         action->setEnabled(enabled);
0653     }
0654 }
0655 
0656 void KAlgebra::tabChanged(int n)
0657 {
0658     c_dock_vars->hide();
0659     b_dock_funcs->hide();
0660     d_dock->hide();
0661 
0662     menuEnabledHelper(c_menu, n == 0);
0663     menuEnabledHelper(b_menu, n == 1);
0664     menuEnabledHelper(t_menu, n == 2);
0665 
0666     m_status->clear();
0667 
0668     switch (n) {
0669     case 0:
0670         c_dock_vars->show();
0671         c_dock_vars->raise();
0672         c_exp->setFocus();
0673         break;
0674     case 1:
0675         b_dock_funcs->show();
0676         b_dock_funcs->raise();
0677 
0678         if (b_funcsModel->rowCount() == 0)
0679             b_tools->setCurrentIndex(1); // We set the Add tab
0680         //             b_add->setFocus();
0681         break;
0682     case 2:
0683         t_exp->setFocus();
0684         break;
0685     case 3:
0686         d_filter->setFocus();
0687         d_dock->show();
0688         d_dock->raise();
0689     default:
0690         break;
0691     }
0692     changeStatusBar(i18nc("@info:status", "Ready"));
0693 }
0694 
0695 void KAlgebra::select(const QModelIndex &idx)
0696 {
0697     b_funcs->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
0698 }
0699 
0700 void KAlgebra::updateInformation()
0701 {
0702     c_varsModel->updateInformation();
0703     c_variables->header()->resizeSections(QHeaderView::ResizeToContents);
0704 }
0705 
0706 void KAlgebra::consoleCalculate()
0707 {
0708     c_results->setMode(ConsoleModel::Calculation);
0709 }
0710 
0711 void KAlgebra::consoleEvaluate()
0712 {
0713     c_results->setMode(ConsoleModel::Evaluation);
0714 }
0715 
0716 void KAlgebra::valueChanged()
0717 {
0718     // FIXME: Should only repaint the affected ones.
0719     if (b_funcsModel->rowCount() > 0)
0720         m_graph2d->updateFunctions(QModelIndex(), 0, b_funcsModel->rowCount() - 1);
0721 }
0722 
0723 void KAlgebra::varsContextMenu(const QPoint &p)
0724 {
0725     QPointer<QMenu> m = new QMenu;
0726     m->addAction(i18n("Add variable"));
0727     QAction *ac = m->exec(b_dock_funcs->widget()->mapToGlobal(p));
0728 
0729     if (ac) {
0730         QPointer<AskName> a = new AskName(i18n("Enter a name for the new variable"), nullptr);
0731 
0732         if (a->exec() == QDialog::Accepted)
0733             b_varsModel->insertVariable(a->name(), Analitza::Expression(Analitza::Cn(0)));
0734         delete a;
0735     }
0736     delete m;
0737 }
0738 
0739 void KAlgebra::add3D(const Analitza::Expression &exp)
0740 {
0741     t_model3d->clear();
0742     Analitza::PlotBuilder plot = Analitza::PlotsFactory::self()->requestPlot(exp, Analitza::Dim3D, c_results->analitza()->variables());
0743     Q_ASSERT(plot.canDraw());
0744     t_model3d->addPlot(plot.create(Qt::yellow, QStringLiteral("func3d_console")));
0745     m_tabs->setCurrentIndex(2);
0746 }
0747 
0748 void KAlgebra::dictionaryFilterChanged(const QString &)
0749 {
0750     if (d_list->model()->rowCount() == 1)
0751         d_list->setCurrentIndex(d_list->model()->index(0, 0));
0752 }
0753 
0754 void KAlgebra::fullScreen(bool isFull)
0755 {
0756     m_tabs->setDocumentMode(isFull);
0757     m_tabs->setTabEnabled(3, !isFull);
0758     if (isFull) {
0759         hide();
0760         m_tabs->setParent(nullptr);
0761         setCentralWidget(nullptr);
0762         m_tabs->showFullScreen();
0763     } else {
0764         setCentralWidget(m_tabs);
0765         show();
0766     }
0767 }