File indexing completed on 2024-04-28 07:28:48
0001 /************************************************************************************* 0002 * Copyright (C) 2007-2017 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 "consolemodel.h" 0020 0021 #include <KLocalizedString> 0022 #include <QFile> 0023 #include <QFontMetrics> 0024 #include <QGuiApplication> 0025 #include <QPalette> 0026 #include <QUrl> 0027 #include <QUrlQuery> 0028 0029 using namespace Qt::Literals::StringLiterals; 0030 0031 Q_GLOBAL_STATIC_WITH_ARGS(QByteArray, 0032 s_css, 0033 ("<style type=\"text/css\">\n" 0034 "\thtml { background-color: " 0035 + qGuiApp->palette().color(QPalette::Active, QPalette::Base).name().toLatin1() 0036 + "; }\n" 0037 "\t.error { border-style: solid; border-width: 1px; border-color: #ff3b21; background-color: #ffe9c4; padding:7px;}\n" 0038 "\t.last { border-style: solid; border-width: 1px; border-color: #2020ff; background-color: #e0e0ff; padding:7px;}\n" 0039 "\t.before { text-align:right; }\n" 0040 "\t.op { font-weight: bold; }\n" 0041 // "\t.normal:hover { border-style: solid; border-width: 1px; border-color: #777; }\n"; 0042 "\t.normal:hover { background-color: #f7f7f7; }\n" 0043 "\t.cont { color: #560000; }\n" 0044 "\t.num { color: #0000C4; }\n" 0045 "\t.sep { font-weight: bold; color: #0000FF; }\n" 0046 "\t.var { color: #640000; }\n" 0047 "\t.keyword { color: #000064; }\n" 0048 "\t.func { color: #008600; }\n" 0049 "\t.result { padding-left: 10%; }\n" 0050 "\t.options { font-size: small; text-align:right }\n" 0051 "\t.string { color: #bb0000 }\n" 0052 "\tli { padding-left: 12px; padding-bottom: 4px; list-style-position: inside; }\n" 0053 "\t.exp { color: #000000 }\n" 0054 "\ta { color: #0000ff }\n" 0055 "\ta:link {text-decoration:none;}\n" 0056 "\ta:visited {text-decoration:none;}\n" 0057 "\ta:hover {text-decoration:underline;}\n" 0058 "\ta:active {text-decoration:underline;}\n" 0059 "\tp { font-size: " 0060 + QByteArray::number(QFontMetrics(QGuiApplication::font()).height()) 0061 + "px; }\n" 0062 "</style>\n")) 0063 0064 ConsoleModel::ConsoleModel(QObject *parent) 0065 : QObject(parent) 0066 { 0067 } 0068 0069 bool ConsoleModel::addOperation(const QString &input) 0070 { 0071 return addOperation(Analitza::Expression(input), input); 0072 } 0073 0074 bool ConsoleModel::addOperation(const Analitza::Expression &e, const QString &input) 0075 { 0076 Analitza::Expression res; 0077 0078 a.setExpression(e); 0079 if (a.isCorrect()) { 0080 if (m_mode == ConsoleModel::Evaluation) { 0081 res = a.evaluate(); 0082 } else { 0083 res = a.calculate(); 0084 } 0085 } 0086 0087 if (a.isCorrect()) { 0088 a.insertVariable(u"ans"_s, res); 0089 m_script += e; // Script won't have the errors 0090 Q_EMIT operationSuccessful(e, res); 0091 0092 const auto result = res.toHtml(); 0093 addMessage(QStringLiteral("<a title='%1' href='kalgebra:/query?id=copy&func=%2'><span class='exp'>%3</span></a><br />= <a title='kalgebra:%1' " 0094 "href='kalgebra:/query?id=copy&func=%4'><span class='result'>%5</span></a>") 0095 .arg(i18n("Paste to Input"), e.toString(), e.toHtml(), res.toString(), result), 0096 e, 0097 res); 0098 } else { 0099 addMessage(i18n("<ul class='error'>Error: <b>%1</b><li>%2</li></ul>", input.toHtmlEscaped(), a.errors().join(u"</li>\n<li>"_s)), {}, {}); 0100 } 0101 0102 return a.isCorrect(); 0103 } 0104 0105 bool ConsoleModel::loadScript(const QUrl &path) 0106 { 0107 Q_ASSERT(!path.isEmpty() && path.isLocalFile()); 0108 0109 // FIXME: We have expression-only script support 0110 bool correct = false; 0111 QFile file(path.toLocalFile()); 0112 0113 if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { 0114 QTextStream stream(&file); 0115 0116 a.importScript(&stream); 0117 correct = a.isCorrect(); 0118 } 0119 0120 if (correct) 0121 addMessage(i18n("Imported: %1", path.toDisplayString()), {}, {}); 0122 else 0123 addMessage(i18n("<ul class='error'>Error: Could not load %1. <br /> %2</ul>", path.toDisplayString(), a.errors().join(u"<br/>"_s)), {}, {}); 0124 0125 return correct; 0126 } 0127 0128 bool ConsoleModel::saveScript(const QUrl &savePath) 0129 { 0130 Q_ASSERT(!savePath.isEmpty()); 0131 0132 QFile file(savePath.toLocalFile()); 0133 bool correct = file.open(QIODevice::WriteOnly | QIODevice::Text); 0134 0135 if (correct) { 0136 QTextStream out(&file); 0137 for (const Analitza::Expression &exp : std::as_const(m_script)) { 0138 out << exp.toString() << QLatin1Char('\n'); 0139 } 0140 } 0141 0142 return correct; 0143 } 0144 0145 void ConsoleModel::setMode(ConsoleMode mode) 0146 { 0147 if (m_mode != mode) { 0148 m_mode = mode; 0149 Q_EMIT modeChanged(mode); 0150 } 0151 } 0152 0153 void ConsoleModel::setVariables(const QSharedPointer<Analitza::Variables> &vars) 0154 { 0155 a.setVariables(vars); 0156 } 0157 0158 void ConsoleModel::addMessage(const QString &msg, const Analitza::Expression &operation, const Analitza::Expression &result) 0159 { 0160 m_htmlLog += msg.toUtf8(); 0161 Q_EMIT updateView(); 0162 Q_EMIT message(msg, operation, result); 0163 } 0164 0165 bool ConsoleModel::saveLog(const QUrl &savePath) const 0166 { 0167 Q_ASSERT(savePath.isLocalFile()); 0168 // FIXME: We have to choose between txt and html 0169 QFile file(savePath.toLocalFile()); 0170 bool correct = file.open(QIODevice::WriteOnly | QIODevice::Text); 0171 0172 if (correct) { 0173 QTextStream out(&file); 0174 out << "<html>\n<head>" << *s_css << "</head>" << QLatin1Char('\n'); 0175 out << "<body>" << QLatin1Char('\n'); 0176 for (const QByteArray &entry : std::as_const(m_htmlLog)) { 0177 out << "<p>" << entry << "</p>" << QLatin1Char('\n'); 0178 } 0179 out << "</body>\n</html>" << QLatin1Char('\n'); 0180 } 0181 0182 return correct; 0183 } 0184 0185 void ConsoleModel::clear() 0186 { 0187 m_script.clear(); 0188 m_htmlLog.clear(); 0189 } 0190 0191 QByteArray ConsoleModel::css() const 0192 { 0193 return *s_css; 0194 } 0195 0196 QString ConsoleModel::readContent(const QUrl &url) 0197 { 0198 return QUrlQuery(url).queryItemValue(u"func"_s); 0199 }