File indexing completed on 2024-05-12 17:21:07
0001 /* 0002 * SPDX-FileCopyrightText: 2020-2021 Han Young <hanyoung@protonmail.com> 0003 * SPDX-FileCopyrightText: 2021-2022 Rohan Asokan <rohan.asokan@students.iiit.ac.in> 0004 * 0005 * SPDX-License-Identifier: GPL-3.0-or-later 0006 */ 0007 #include "inputmanager.h" 0008 #include "historymanager.h" 0009 #include "mathengine.h" 0010 #include <QDebug> 0011 #include <QLocale> 0012 0013 InputManager::InputManager() 0014 { 0015 0016 KNumber::setDefaultFloatOutput(true); 0017 0018 QLocale locale; 0019 m_groupSeparator = locale.groupSeparator(); 0020 0021 // change non breaking space into classic space, because UI converts it to classic space 0022 if (m_groupSeparator == QString(QChar(160))) { 0023 m_groupSeparator = QLatin1Char(32); 0024 } 0025 m_decimalPoint = locale.decimalPoint(); 0026 } 0027 0028 InputManager *InputManager::inst() 0029 { 0030 static InputManager singleton; 0031 return &singleton; 0032 } 0033 0034 const QString &InputManager::expression() const 0035 { 0036 return m_expression; 0037 } 0038 void InputManager::setExpression(const QString &expression) 0039 { 0040 m_expression = expression; 0041 } 0042 0043 const QString &InputManager::result() const 0044 { 0045 return m_result; 0046 } 0047 0048 const QString &InputManager::binaryResult() const 0049 { 0050 return m_binaryResult; 0051 } 0052 0053 const QString &InputManager::hexResult() const 0054 { 0055 return m_hexResult; 0056 } 0057 0058 bool InputManager::moveFromResult() const 0059 { 0060 return m_moveFromResult; 0061 } 0062 0063 int InputManager::getCursorPosition() const 0064 { 0065 int position = m_inputPosition; 0066 0067 // account for group separators in expression 0068 int i = 0; 0069 while (i < position && i < m_expression.size()) { 0070 if (m_expression.at(i) == m_groupSeparator) { 0071 position++; 0072 } 0073 i++; 0074 } 0075 return position; 0076 } 0077 0078 void InputManager::setCursorPosition(int position) 0079 { 0080 m_inputPosition = position; 0081 0082 int i = 0; 0083 while (i < position && i < m_expression.size()) { 0084 if (m_expression.at(i) == m_groupSeparator) { 0085 m_inputPosition--; 0086 } 0087 i++; 0088 } 0089 } 0090 0091 int InputManager::idealCursorPosition(int position) const 0092 { 0093 // position cursor ahead of group separator 0094 if (position > 1 && position < m_expression.size()) { 0095 if (m_expression.at(position - 1) == m_groupSeparator) { 0096 position++; 0097 return position; 0098 } 0099 } 0100 0101 // position cursor around functions not between 0102 QRegularExpression re(QStringLiteral(R"([^\d\+−×÷\!,\^\(\) ]{2,})")); 0103 QRegularExpressionMatch match = re.match(m_expression.mid(position - 1, 2)); 0104 if (match.hasMatch()) { 0105 if (position == m_expression.size()) { 0106 // at end, do nothing 0107 } else if (m_expression.at(position - 1) == m_expression.at(position)) { 0108 // same char, do nothing 0109 } else { 0110 // check nearest left 0111 int posLeft = position - 1; 0112 while (posLeft > 0) { 0113 if (m_expression.at(posLeft).isDigit()) { 0114 break; 0115 } 0116 posLeft--; 0117 } 0118 0119 // check nearest right 0120 int posRight = position + 1; 0121 while (posRight < m_expression.size()) { 0122 if (m_expression.at(posRight).isDigit()) { 0123 break; 0124 } 0125 posRight++; 0126 } 0127 0128 // prefer the closest side 0129 if (position - posLeft < posRight - position) { 0130 position -= position - posLeft - 1; 0131 } else { 0132 position += posRight - position; 0133 } 0134 0135 return position; 0136 } 0137 } 0138 0139 return position; 0140 } 0141 0142 void InputManager::append(const QString &subexpression) 0143 { 0144 // if expression was from result and input is numeric, clear expression 0145 if (m_moveFromResult && subexpression.size() == 1 && m_inputPosition == m_input.size()) { 0146 if(subexpression.at(0).isDigit() || subexpression.at(0) == QLatin1Char('.')) 0147 { 0148 m_input.clear(); 0149 } 0150 } 0151 m_moveFromResult = false; 0152 0153 QString temp = m_input; 0154 temp.insert(m_inputPosition, subexpression); 0155 temp.remove(m_groupSeparator); 0156 0157 // Call the corresponding parser based on the type of expression. 0158 MathEngine * engineInstance = MathEngine::inst(); 0159 if (m_isBinaryMode) { 0160 engineInstance->parseBinaryExpression(temp); 0161 } else { 0162 engineInstance->parse(temp); 0163 } 0164 0165 if (!MathEngine::inst()->error()) { 0166 KNumber result = MathEngine::inst()->result(); 0167 m_output = result.toQString(); 0168 m_binaryResult = result.toBinaryString(0); 0169 m_hexResult = result.toHexString(0); 0170 m_input = temp; 0171 m_inputPosition += subexpression.size(); 0172 m_expression = formatNumbers(m_input); 0173 m_result = formatNumbers(m_output); 0174 0175 Q_EMIT resultChanged(); 0176 Q_EMIT binaryResultChanged(); 0177 Q_EMIT hexResultChanged(); 0178 Q_EMIT expressionChanged(); 0179 } 0180 } 0181 0182 void InputManager::backspace() 0183 { 0184 if (m_inputPosition > 0) { 0185 // delete entire function 0186 QRegularExpression re(QStringLiteral(R"([^\d\+−×÷\!,\^ ]{2,})")); 0187 QRegularExpressionMatch match = re.match(m_input.mid(m_inputPosition - 2, 2)); 0188 if (match.hasMatch()) { 0189 // check backwards 0190 int posBack = m_inputPosition - 2; 0191 while (posBack >= 0) { 0192 if (m_input.at(posBack).isDigit() || m_input.at(posBack) == QStringLiteral("(")) { 0193 break; 0194 } else if (posBack > 0 && m_input.at(posBack - 1) == m_input.at(posBack)) { 0195 posBack--; 0196 break; 0197 } 0198 posBack--; 0199 } 0200 0201 const int diff = m_inputPosition - posBack; 0202 m_input.remove(m_inputPosition - diff + 1, diff - 1); 0203 m_inputPosition = m_inputPosition - diff + 1; 0204 } else { 0205 m_input.remove(m_inputPosition - 1, 1); 0206 m_inputPosition--; 0207 } 0208 0209 m_expression = formatNumbers(m_input); 0210 0211 Q_EMIT expressionChanged(); 0212 0213 if (m_input.length() == 0) { 0214 clear(); 0215 return; 0216 } 0217 0218 // Call the corresponding parser based on the type of expression. 0219 MathEngine * engineInstance = MathEngine::inst(); 0220 if (m_isBinaryMode) { 0221 engineInstance->parseBinaryExpression(m_input); 0222 } else { 0223 engineInstance->parse(m_input); 0224 } 0225 0226 if(!MathEngine::inst()->error()) 0227 { 0228 KNumber result = MathEngine::inst()->result(); 0229 m_output = result.toQString(); 0230 m_binaryResult = result.toBinaryString(0); 0231 m_hexResult = result.toHexString(0); 0232 m_result = formatNumbers(m_output); 0233 Q_EMIT resultChanged(); 0234 Q_EMIT binaryResultChanged(); 0235 Q_EMIT hexResultChanged(); 0236 } 0237 } 0238 } 0239 0240 void InputManager::equal() 0241 { 0242 HistoryManager::inst()->addHistory(m_expression + QStringLiteral(" = ") + m_result); 0243 0244 m_input = m_output; 0245 m_output.clear(); 0246 m_expression = m_result; 0247 m_result.clear(); 0248 m_binaryResult.clear(); 0249 m_hexResult.clear(); 0250 0251 m_moveFromResult = true; 0252 m_inputPosition = m_input.size(); 0253 Q_EMIT expressionChanged(); 0254 Q_EMIT resultChanged(); 0255 Q_EMIT binaryResultChanged(); 0256 Q_EMIT hexResultChanged(); 0257 } 0258 0259 void InputManager::clear() 0260 { 0261 m_input.clear(); 0262 m_output.clear(); 0263 m_expression.clear(); 0264 m_result.clear(); 0265 m_binaryResult.clear(); 0266 m_hexResult.clear(); 0267 0268 m_inputPosition = 0; 0269 Q_EMIT expressionChanged(); 0270 Q_EMIT resultChanged(); 0271 Q_EMIT binaryResultChanged(); 0272 Q_EMIT hexResultChanged(); 0273 } 0274 0275 void InputManager::fromHistory(const QString &result) 0276 { 0277 m_input = result; 0278 m_input.remove(m_groupSeparator); 0279 m_output.clear(); 0280 m_expression = result; 0281 m_result.clear(); 0282 m_binaryResult.clear(); 0283 m_hexResult.clear(); 0284 0285 m_moveFromResult = true; 0286 m_inputPosition = m_input.size(); 0287 Q_EMIT expressionChanged(); 0288 Q_EMIT resultChanged(); 0289 Q_EMIT binaryResultChanged(); 0290 Q_EMIT hexResultChanged(); 0291 } 0292 0293 void InputManager::setBinaryMode(bool active) { 0294 m_isBinaryMode = active; 0295 clear(); 0296 } 0297 bool InputManager::binaryMode() 0298 { 0299 return m_isBinaryMode; 0300 } 0301 0302 QString InputManager::formatNumbers(const QString &text) 0303 { 0304 QString formatted; 0305 QString number; 0306 for (const auto ch : text) { 0307 if (ch.isDigit() || ch == m_decimalPoint) { 0308 number.append(ch); 0309 } else { 0310 if (!number.isEmpty()) { 0311 addNumberSeparators(number); 0312 formatted.append(number); 0313 number.clear(); 0314 } 0315 formatted.append(ch); 0316 } 0317 } 0318 0319 if (!number.isEmpty()) { 0320 addNumberSeparators(number); 0321 formatted.append(number); 0322 } 0323 0324 formatted.replace(QStringLiteral("log10"), QStringLiteral("log₁₀")); 0325 formatted.replace(QStringLiteral("log2"), QStringLiteral("log₂")); 0326 0327 return formatted; 0328 } 0329 0330 void InputManager::addNumberSeparators(QString &number) 0331 { 0332 const int idx = number.indexOf(m_decimalPoint); 0333 0334 if (idx >= 0 && idx < number.size() - 3) { 0335 QString left = number.left(idx); 0336 QString right = number.right(number.size() - idx); 0337 left.replace(QRegularExpression(QStringLiteral(R"(\B(?=(\d{3})+(?!\d)))")), m_groupSeparator); 0338 number = left + right; 0339 } else if (number.size() > 3) { 0340 number.replace(QRegularExpression(QStringLiteral(R"(\B(?=(\d{3})+(?!\d)))")), m_groupSeparator); 0341 } 0342 }