From 97f3970901230bf0d356128eadda8e68f87a88bb Mon Sep 17 00:00:00 2001 From: "Hanna K." Date: Mon, 20 May 2024 18:14:04 +0200 Subject: [PATCH] Option (enabled by default) to show expression status (parsed expression and calculated value) as you type in history list, instead of in tooltip; Show parse error in tooltip; Modify behaviour of tooltip with function hint --- data/scalable/warning.svg | 47 +++++++ icons.qrc | 2 + src/expressionedit.cpp | 152 ++++++++++++--------- src/expressionedit.h | 10 +- src/historyview.cpp | 256 +++++++++++++++++++++++------------- src/historyview.h | 12 +- src/preferencesdialog.cpp | 39 +++--- src/preferencesdialog.h | 9 +- src/qalculateqtsettings.cpp | 31 ++++- src/qalculateqtsettings.h | 3 +- src/qalculatewindow.cpp | 86 +++++++++++- src/qalculatewindow.h | 4 +- 12 files changed, 468 insertions(+), 183 deletions(-) create mode 100644 data/scalable/warning.svg diff --git a/data/scalable/warning.svg b/data/scalable/warning.svg new file mode 100644 index 0000000..9a71dd3 --- /dev/null +++ b/data/scalable/warning.svg @@ -0,0 +1,47 @@ + + + + + + + + diff --git a/icons.qrc b/icons.qrc index bb20c10..0266807 100644 --- a/icons.qrc +++ b/icons.qrc @@ -26,6 +26,7 @@ data/scalable/rpn-swap.svg data/scalable/table.svg data/scalable/units.svg + data/scalable/warning.svg data/scalable/calendars-dark.svg data/scalable/configure-dark.svg data/scalable/convert-dark.svg @@ -51,6 +52,7 @@ data/scalable/rpn-swap-dark.svg data/scalable/table-dark.svg data/scalable/units-dark.svg + data/scalable/warning.svg data/16/qalculate-qt.png data/22/qalculate-qt.png data/32/qalculate-qt.png diff --git a/src/expressionedit.cpp b/src/expressionedit.cpp index 816a7e9..d2fb929 100644 --- a/src/expressionedit.cpp +++ b/src/expressionedit.cpp @@ -1881,8 +1881,12 @@ void ExpressionEdit::contextMenuEvent(QContextMenuEvent *e) { menu = cmenu->addMenu(tr("Expression Status")); group = new QActionGroup(this); action = menu->addAction(tr("Off"), this, SLOT(onStatusModeChanged())); action->setData(0); action->setCheckable(true); group->addAction(action); statusOffAction = action; - action = menu->addAction(tr("With delay"), this, SLOT(onStatusModeChanged())); action->setData(1); action->setCheckable(true); group->addAction(action); statusDelayAction = action; - action = menu->addAction(tr("Without delay"), this, SLOT(onStatusModeChanged())); action->setData(2); action->setCheckable(true); group->addAction(action); statusNoDelayAction = action; + action = menu->addAction(tr("In history list"), this, SLOT(onStatusModeChanged())); action->setData(1); action->setCheckable(true); group->addAction(action); statusHistoryAction = action; + action = menu->addAction(tr("In expression field"), this, SLOT(onStatusModeChanged())); action->setData(2); action->setCheckable(true); group->addAction(action); statusExpressionAction = action; + menu->addSeparator(); + group = new QActionGroup(this); + action = menu->addAction(tr("With delay"), this, SLOT(onStatusModeChanged())); action->setData(3); action->setCheckable(true); group->addAction(action); statusDelayAction = action; + action = menu->addAction(tr("Without delay"), this, SLOT(onStatusModeChanged())); action->setData(4); action->setCheckable(true); group->addAction(action); statusNoDelayAction = action; #ifndef _WIN32 QAction *enableIMAction = cmenu->addAction(tr("Use input method"), this, SLOT(enableIM())); enableIMAction->setCheckable(true); enableIMAction->setChecked(settings->enable_input_method); @@ -1900,7 +1904,9 @@ void ExpressionEdit::contextMenuEvent(QContextMenuEvent *e) { clearAction->setEnabled(!b_empty); clearHistoryAction->setEnabled(!settings->expression_history.empty()); if(!settings->display_expression_status) statusOffAction->setChecked(true); - else if(settings->expression_status_delay > 0) statusDelayAction->setChecked(true); + else if(settings->status_in_history) statusHistoryAction->setChecked(true); + else statusExpressionAction->setChecked(true); + if(settings->expression_status_delay > 0) statusDelayAction->setChecked(true); else statusNoDelayAction->setChecked(true); cmenu->popup(e->globalPos()); } @@ -1956,10 +1962,13 @@ void ExpressionEdit::onCompletionModeChanged() { } void ExpressionEdit::onStatusModeChanged() { int i = qobject_cast(sender())->data().toInt(); - settings->display_expression_status = (i > 0); - if(i == 1) settings->expression_status_delay = 1000; - else if(i == 2) settings->expression_status_delay = 0; - emit expressionStatusModeChanged(); + if(i == 3) settings->expression_status_delay = 1000; + else if(i == 4) settings->expression_status_delay = 0; + if(i < 3) { + settings->display_expression_status = (i > 0); + settings->status_in_history = (i == 1); + } + emit expressionStatusModeChanged(i < 3); } void ExpressionEdit::editUndo() { if(undo_index == 0) return; @@ -2025,40 +2034,14 @@ void ExpressionEdit::blockUndo(bool b) { if(b) block_add_to_undo++; else block_add_to_undo--; } -void remove_spaces(std::string &str) { - size_t i = 0; - while(true) { - i = str.find(' ', i); - if(i != std::string::npos) str.erase(i, 1); - else break; - } - i = 0; - while(true) { - i = str.find(THIN_SPACE, i); - if(i != std::string::npos) str.erase(i, strlen(THIN_SPACE)); - else break; - } - i = 0; - while(true) { - i = str.find(NNBSP, i); - if(i != std::string::npos) str.erase(i, strlen(NNBSP)); - else break; - } - i = 0; - while(true) { - i = str.find(NBSP, i); - if(i != std::string::npos) str.erase(i, strlen(NBSP)); - else break; - } -} void ExpressionEdit::showCurrentStatus() { - if(!expression_has_changed || current_status_text.isEmpty() || (completionView->isVisible() && completionView->selectionModel()->hasSelection())) { + if(!expression_has_changed || current_status_text.isEmpty() || (completionView->isVisible() && (completionView->selectionModel()->hasSelection() || current_status_type == 3))) { HIDE_TOOLTIP } else { QString str = current_status_text; std::string str_nohtml = unhtmlize(current_status_text.toStdString()); std::string current_text = toPlainText().toStdString(); - if(current_status_is_expression && settings->auto_calculate && str_nohtml.length() <= 2000 && !contains_plot_or_save(current_text)) { + if(current_status_type == 1 && settings->auto_calculate && str_nohtml.length() <= 2000 && !contains_plot_or_save(current_text)) { bool b_comp = false, is_approximate = false; PrintOptions po = settings->printops; po.is_approximate = &is_approximate; @@ -2085,14 +2068,14 @@ void ExpressionEdit::showCurrentStatus() { if(str_nohtml == current_text || str_nohtml.length() > 2000) { HIDE_TOOLTIP } else if(tipLabel && tipLabel->isVisible()) { - tipLabel->reuseTip(str, mapToGlobal(cursorRect().bottomRight())); - if(!tipLabel->placeTip(mapToGlobal(cursorRect().bottomRight()), completionView->isVisible() ? completionView->geometry(): QRect())) { + tipLabel->reuseTip(str, mapToGlobal((current_status_type == 2 ? function_pos : cursorRect().bottomRight()))); + if(!tipLabel->placeTip(mapToGlobal((current_status_type == 2 ? function_pos : cursorRect().bottomRight())), completionView->isVisible() ? completionView->geometry(): QRect())) { HIDE_TOOLTIP } } else { if(tipLabel) tipLabel->deleteLater(); - tipLabel = new ExpressionTipLabel(str, mapToGlobal(cursorRect().bottomRight()), this); - if(tipLabel->placeTip(mapToGlobal(cursorRect().bottomRight()), completionView->isVisible() ? completionView->geometry(): QRect())) { + tipLabel = new ExpressionTipLabel(str, mapToGlobal((current_status_type == 2 ? function_pos : cursorRect().bottomRight())), this); + if(tipLabel->placeTip(mapToGlobal((current_status_type == 2 ? function_pos : cursorRect().bottomRight())), completionView->isVisible() ? completionView->geometry(): QRect())) { tipLabel->showNormal(); } else { HIDE_TOOLTIP @@ -2100,14 +2083,15 @@ void ExpressionEdit::showCurrentStatus() { } } } -void ExpressionEdit::setStatusText(const QString &text, bool is_expression) { +void ExpressionEdit::setStatusText(const QString &text, int stype) { if(toolTipTimer) toolTipTimer->stop(); - if(text.isEmpty()) { + if(text.isEmpty() || !settings->display_expression_status) { HIDE_TOOLTIP - } else if(settings->display_expression_status) { + } else { current_status_text = text; - current_status_is_expression = is_expression; - if(settings->expression_status_delay > 0) { + bool prev_func = (current_status_type == 2); + current_status_type = stype; + if(settings->expression_status_delay > 0 && (current_status_type != 2 || !prev_func || !tipLabel || !tipLabel->isVisible())) { if(tipLabel) tipLabel->hideTip(); if(!toolTipTimer) { toolTipTimer = new QTimer(this); @@ -2128,12 +2112,15 @@ bool ExpressionEdit::displayFunctionHint(MathFunction *f, int arg_index) { if((iargs == 0 && arg_index == 0) || (iargs == 1 && arg_index == 1)) return false; const ExpressionName *ename = &f->preferredName(false, settings->printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) parent()); bool last_is_vctr = f->getArgumentDefinition(iargs) && f->getArgumentDefinition(iargs)->type() == ARGUMENT_TYPE_VECTOR; + if(arg_index == 0 || function_pos.isNull()) function_pos = cursorRect().bottomRight(); if(iargs == 0 || (arg_index > iargs && iargs >= 0 && (!last_is_vctr || iargs == 1))) { if(last_is_vctr || (iargs == 1 && f->getArgumentDefinition(1) && f->getArgumentDefinition(1)->handlesVector())) { + function_pos = QPoint(); return false; + } else { + setStatusText(tr("Too many arguments for %1().").arg(QString::fromStdString(ename->formattedName(TYPE_FUNCTION, true, true))), 2); + return true; } - setStatusText(tr("Too many arguments for %1().").arg(QString::fromStdString(ename->formattedName(TYPE_FUNCTION, true, true)))); - return true; } if(arg_index <= 0) arg_index = 1; Argument *arg; @@ -2200,7 +2187,7 @@ bool ExpressionEdit::displayFunctionHint(MathFunction *f, int arg_index) { } } str += ")"; - setStatusText(str); + setStatusText(str, 2); return true; } @@ -2256,7 +2243,9 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { cdata->current_function = NULL; if(block_display_parse) return; if(document()->isEmpty()) { + function_pos = QPoint(); setStatusText(""); + if(settings->status_in_history) emit statusChanged(QString(), false, false); prev_parsed_expression = ""; expression_has_changed2 = false; return; @@ -2274,6 +2263,7 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { else text += to_str; text += ")"; } else if(text.empty()) { + function_pos = QPoint(); setStatusText(""); prev_parsed_expression = ""; expression_has_changed2 = false; @@ -2283,20 +2273,50 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { if(text[0] == '/' && text.length() > 1) { size_t i = text.find_first_not_of(SPACES, 1); if(i != std::string::npos && (signed char) text[i] > 0 && is_not_in(NUMBER_ELEMENTS OPERATORS, text[i])) { - if(show_tooltip) setStatusText("qalc command"); + if(settings->status_in_history) { + setStatusText(""); + emit statusChanged("qalc command", false, false); + } else if(show_tooltip) { + setStatusText("qalc command"); + function_pos = QPoint(); + } return; } } else if(text == "MC") { - if(show_tooltip) setStatusText(tr("MC (memory clear)")); + if(settings->status_in_history) { + setStatusText(""); + emit statusChanged(tr("MC (memory clear)"), false, false); + } else if(show_tooltip) { + setStatusText(tr("MC (memory clear)")); + function_pos = QPoint(); + } return; } else if(text == "MS") { - if(show_tooltip) setStatusText(tr("MS (memory store)")); + if(settings->status_in_history) { + setStatusText(""); + emit statusChanged(tr("MS (memory store)"), false, false); + } else if(show_tooltip) { + setStatusText(tr("MS (memory store)")); + function_pos = QPoint(); + } return; } else if(text == "M+") { - if(show_tooltip) setStatusText(tr("M+ (memory plus)")); + if(settings->status_in_history) { + setStatusText(""); + emit statusChanged(tr("M+ (memory plus)"), false, false); + } else if(show_tooltip) { + setStatusText(tr("M+ (memory plus)")); + function_pos = QPoint(); + } return; } else if(text == "M-" || text == "M−") { - if(show_tooltip) setStatusText(tr("M− (memory minus)")); + if(settings->status_in_history) { + setStatusText(""); + emit statusChanged(tr("M− (memory minus)"), false, false); + } else if(show_tooltip) { + setStatusText(tr("M− (memory minus)")); + function_pos = QPoint(); + } return; } gsub(ID_WRAP_LEFT, LEFT_PARENTHESIS, text); @@ -2359,6 +2379,7 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { b_func = displayFunctionHint(mfunc.function(), mfunc.countChildren()); } } + if(!b_func) function_pos = QPoint(); if(expression_has_changed2) { bool last_is_space = false; parsed_expression_tooltip = ""; @@ -2429,17 +2450,17 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { MathStructure mwhere; CALCULATOR->parseExpressionAndWhere(&mparse, &mwhere, str_e, str_w, settings->evalops.parse_options); mparse.format(po); - parsed_expression = mparse.print(po, true, false, TAG_TYPE_HTML); + parsed_expression = mparse.print(po, true, settings->status_in_history ? settings->color : false, TAG_TYPE_HTML); parsed_expression += CALCULATOR->localWhereString(); mwhere.format(po); - parsed_expression += mwhere.print(po, true, false, TAG_TYPE_HTML); + parsed_expression += mwhere.print(po, true, settings->status_in_history ? settings->color : false, TAG_TYPE_HTML); CALCULATOR->endTemporaryStopMessages(); } else if(str_e.empty()) { parsed_expression = ""; } else { CALCULATOR->beginTemporaryStopMessages(); mparse.format(po); - parsed_expression = mparse.print(po, true, false, TAG_TYPE_HTML); + parsed_expression = mparse.print(po, true, settings->status_in_history ? settings->color : false, TAG_TYPE_HTML); CALCULATOR->endTemporaryStopMessages(); } if(!str_u.empty()) { @@ -2671,7 +2692,7 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { parsed_expression += p->preferredDisplayName(po.abbreviate_names, po.use_unicode_signs, false, false, po.can_display_unicode_string_function, po.can_display_unicode_string_arg).formattedName(-1, true, TAG_TYPE_HTML, 0, true, po.hide_underscore_spaces); } else { CALCULATOR->beginTemporaryStopMessages(); - parsed_expression += mparse.print(po, true, false, TAG_TYPE_HTML); + parsed_expression += mparse.print(po, true, settings->status_in_history ? settings->color : false, TAG_TYPE_HTML); CALCULATOR->endTemporaryStopMessages(); } had_to_conv = true; @@ -2682,7 +2703,7 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { } if(mparse2) { mparse2->format(po); - parsed_expression.replace(0, parse_l, mparse2->print(po, true, false, TAG_TYPE_HTML)); + parsed_expression.replace(0, parse_l, mparse2->print(po, true, settings->status_in_history ? settings->color : false, TAG_TYPE_HTML)); mparse2->unref(); } } @@ -2697,6 +2718,7 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { parsed_expression_tooltip += "
• "; } parsed_expression_tooltip += CALCULATOR->message()->message(); + had_errors = true; message_n++; } else if(mtype == MESSAGE_WARNING) { had_warnings = true; @@ -2704,17 +2726,25 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) { } CALCULATOR->nextMessage(); } + bool status_error = had_errors; parsed_had_errors = had_errors; parsed_had_warnings = had_warnings; if(!str_f.empty()) {str_f += " "; parsed_expression.insert(0, str_f);} if(had_errors) prev_parsed_expression = QString::fromStdString(parsed_expression_tooltip); else prev_parsed_expression = QString::fromStdString(parsed_expression); settings->evalops.parse_options.preserve_format = false; - if(!b_func && show_tooltip) setStatusText(settings->chain_mode ? "" : prev_parsed_expression, true); + if(settings->status_in_history) { + emit statusChanged(QString::fromStdString(parsed_expression), !str_e.empty(), status_error); + if(!b_func) setStatusText(settings->chain_mode || !had_errors ? "" : prev_parsed_expression, had_errors ? 3 : 0); + } else if(!b_func && show_tooltip) { + setStatusText(settings->chain_mode ? "" : prev_parsed_expression, had_errors ? 3 : 1); + } expression_has_changed2 = false; } else if(!b_func) { CALCULATOR->clearMessages(); settings->evalops.parse_options.preserve_format = false; - if(prev_func && show_tooltip) setStatusText(settings->chain_mode ? "" : prev_parsed_expression, true); + if(prev_func && show_tooltip) { + setStatusText((settings->status_in_history && !parsed_had_errors) || settings->chain_mode ? "" : prev_parsed_expression, parsed_had_errors ? 3 : 1); + } } else { settings->evalops.parse_options.preserve_format = false; } @@ -2728,7 +2758,7 @@ void ExpressionEdit::onTextChanged() { previous_pos = textCursor().position(); tabbed_index = -1; if(completionTimer) completionTimer->stop(); - if(tipLabel && settings->expression_status_delay > 0) tipLabel->hideTip(); + if(tipLabel && settings->expression_status_delay > 0 && current_status_type != 2) tipLabel->hideTip(); if(block_text_change) return; if(expression_undo_buffer.isEmpty() || str != expression_undo_buffer.last()) { if(expression_undo_buffer.isEmpty()) { @@ -3104,7 +3134,7 @@ void ExpressionEdit::onCursorPositionChanged() { tabbed_index = -1; if(completionTimer) completionTimer->stop(); if(toolTipTimer) toolTipTimer->stop(); - if(tipLabel && settings->expression_status_delay > 0) tipLabel->hideTip(); + if(tipLabel && settings->expression_status_delay > 0 && current_status_type != 2) tipLabel->hideTip(); if(block_text_change) return; cursor_has_moved = true; int epos = document()->characterCount() - 1 - textCursor().position(); diff --git a/src/expressionedit.h b/src/expressionedit.h index 9df168b..bf9389a 100644 --- a/src/expressionedit.h +++ b/src/expressionedit.h @@ -61,7 +61,7 @@ class ExpressionEdit : public QPlainTextEdit { QStandardItemModel *sourceModel; QTableView *completionView; QMenu *cmenu; - QAction *undoAction, *redoAction, *cutAction, *copyAction, *pasteAction, *deleteAction, *selectAllAction, *clearAction, *statusOffAction, *statusDelayAction, *statusNoDelayAction, *clearHistoryAction; + QAction *undoAction, *redoAction, *cutAction, *copyAction, *pasteAction, *deleteAction, *selectAllAction, *clearAction, *statusOffAction, *statusDelayAction, *statusNoDelayAction, *statusHistoryAction, *statusExpressionAction, *clearHistoryAction; QTimer *completionTimer, *toolTipTimer; ExpressionTipLabel *tipLabel; QWidget *tb; @@ -86,16 +86,17 @@ class ExpressionEdit : public QPlainTextEdit { bool disable_history_arrow_keys, dont_change_index, cursor_has_moved; int block_display_parse; QString prev_parsed_expression, parsed_expression_tooltip, current_status_text; - bool current_status_is_expression; + int current_status_type; bool expression_has_changed, expression_has_changed2; bool parsed_had_errors, parsed_had_warnings; int previous_epos; + QPoint function_pos; bool parentheses_highlighted; int default_frame; QRect prev_rect; void setCurrentObject(); - void setStatusText(const QString &text, bool is_expression = false); + void setStatusText(const QString &text, int = 0); bool displayFunctionHint(MathFunction *f, int arg_index = 1); void highlightParentheses(); @@ -164,7 +165,8 @@ class ExpressionEdit : public QPlainTextEdit { void returnPressed(); void toConversionRequested(std::string); void calculateRPNRequest(int); - void expressionStatusModeChanged(); + void expressionStatusModeChanged(bool); + void statusChanged(QString, bool, bool); }; diff --git a/src/historyview.cpp b/src/historyview.cpp index fda9f8f..7fe3752 100644 --- a/src/historyview.cpp +++ b/src/historyview.cpp @@ -155,6 +155,7 @@ HistoryView::HistoryView(QWidget *parent) : QTextEdit(parent) { i_pos = 0; i_pos_p = 0; previous_cursor = 0; + previous_temporary = 0; last_ans = 0; #ifdef _WIN32 has_lock_symbol = 1; @@ -176,43 +177,43 @@ HistoryView::HistoryView(QWidget *parent) : QTextEdit(parent) { } HistoryView::~HistoryView() {} -void HistoryView::replaceColors(QString &s_text, QColor prev_text_color) { +void HistoryView::replaceColors(QString &str, QColor prev_text_color) { if(settings->color == 2) { - s_text.replace("color: #585858", "color: #AAAAAA"); - s_text.replace("color: #800000", "color: #FFAAAA"); - s_text.replace("color: #000080", "color: #AAAAFF"); - s_text.replace("color:#005858", "color:#AAFFFF"); - s_text.replace("color:#585800", "color:#FFFFAA"); - s_text.replace("color:#580058", "color:#FFAAFF"); - s_text.replace("color:#800000", "color:#FFAAAA"); - s_text.replace("color:#000080", "color:#AAAAFF"); - s_text.replace("color:#008000", "color:#BBFFBB"); - s_text.replace("color:#668888", "color:#88AAAA"); - s_text.replace("color:#888866", "color:#AAAA88"); - s_text.replace("color:#886688", "color:#AA88AA"); - s_text.replace("color:#666699", "color:#AA8888"); - s_text.replace("color:#669966", "color:#99BB99"); - s_text.replace("color: #666666", "color: #AAAAAA"); - s_text.replace(prev_text_color.name(QColor::HexRgb), text_color.name(QColor::HexRgb)); - s_text.replace(":/icons/actions", ":/icons/dark/actions"); + str.replace("color: #585858", "color: #AAAAAA"); + str.replace("color: #800000", "color: #FFAAAA"); + str.replace("color: #000080", "color: #AAAAFF"); + str.replace("color:#005858", "color:#AAFFFF"); + str.replace("color:#585800", "color:#FFFFAA"); + str.replace("color:#580058", "color:#FFAAFF"); + str.replace("color:#800000", "color:#FFAAAA"); + str.replace("color:#000080", "color:#AAAAFF"); + str.replace("color:#008000", "color:#BBFFBB"); + str.replace("color:#668888", "color:#88AAAA"); + str.replace("color:#888866", "color:#AAAA88"); + str.replace("color:#886688", "color:#AA88AA"); + str.replace("color:#666699", "color:#AA8888"); + str.replace("color:#669966", "color:#99BB99"); + str.replace("color: #666666", "color: #AAAAAA"); + str.replace(prev_text_color.name(QColor::HexRgb), text_color.name(QColor::HexRgb)); + str.replace(":/icons/actions", ":/icons/dark/actions"); } else { - s_text.replace("color: #AAAAAA", "color: #585858"); - s_text.replace("color: #AAAAFF", "color: #000080"); - s_text.replace("color: #FFAAAA", "color: #800000"); - s_text.replace("color:#AAFFFF", "color:#005858"); - s_text.replace("color:#FFFFAA", "color:#585800"); - s_text.replace("color:#FFAAFF", "color:#580058"); - s_text.replace("color:#FFAAAA", "color:#800000"); - s_text.replace("color:#AAAAFF", "color:#000080"); - s_text.replace("color:#BBFFBB", "color:#008000"); - s_text.replace("color:#88AAAA", "color:#668888"); - s_text.replace("color:#AAAA88", "color:#888866"); - s_text.replace("color:#AA88AA", "color:#886688"); - s_text.replace("color:#AA8888", "color:#666699"); - s_text.replace("color:#99BB99", "color:#669966"); - s_text.replace("color: #AAAAAA", "color: #666666"); - s_text.replace(prev_text_color.name(QColor::HexRgb), text_color.name(QColor::HexRgb)); - s_text.replace(":/icons/dark/actions", ":/icons/actions"); + str.replace("color: #AAAAAA", "color: #585858"); + str.replace("color: #AAAAFF", "color: #000080"); + str.replace("color: #FFAAAA", "color: #800000"); + str.replace("color:#AAFFFF", "color:#005858"); + str.replace("color:#FFFFAA", "color:#585800"); + str.replace("color:#FFAAFF", "color:#580058"); + str.replace("color:#FFAAAA", "color:#800000"); + str.replace("color:#AAAAFF", "color:#000080"); + str.replace("color:#BBFFBB", "color:#008000"); + str.replace("color:#88AAAA", "color:#668888"); + str.replace("color:#AAAA88", "color:#888866"); + str.replace("color:#AA88AA", "color:#886688"); + str.replace("color:#AA8888", "color:#666699"); + str.replace("color:#99BB99", "color:#669966"); + str.replace("color: #AAAAAA", "color: #666666"); + str.replace(prev_text_color.name(QColor::HexRgb), text_color.name(QColor::HexRgb)); + str.replace(":/icons/dark/actions", ":/icons/actions"); } } void replace_colors(std::string &str) { @@ -303,10 +304,16 @@ void HistoryView::loadInitial(bool reload) { if(reload && !initial_loaded) return; s_text.clear(); i_pos = 0; - previous_html.clear(); i_pos_p = 0; + previous_html.clear(); last_ans = 0; last_ref = ""; + i_pos2 = 0; + i_pos_p2 = 0; + previous_html2.clear(); + last_ans2 = 0; + last_ref2 = ""; + previous_temporary = false; for(size_t i = 0; i < settings->v_expression.size() && !reload;) { if(i > 0 && !settings->v_protected[i] && settings->v_expression[i] == settings->v_expression[i - 1] && settings->v_parse[i] == settings->v_parse[i - 1] && settings->v_result[i] == settings->v_result[i - 1] && settings->v_pexact[i] == settings->v_pexact[i] && settings->v_exact[i] == settings->v_exact[i - 1] && settings->v_value[i] == settings->v_value[i - 1] && settings->v_parse[i].find("rand") == std::string::npos && settings->v_expression[i].find("rand") == std::string::npos) { settings->v_expression.erase(settings->v_expression.begin() + i); @@ -365,7 +372,45 @@ void HistoryView::loadInitial(bool reload) { int paste_h = fm.ascent() * 0.75; \ if(paste_h < 12) paste_h = 12; -void HistoryView::addResult(std::vector values, std::string expression, bool pexact, std::string parse, int exact, bool dual_approx, const QString &image, bool *implicit_warning, int initial_load, size_t index) { +void HistoryView::clearTemporary() { + if(!previous_temporary || previous_html.isEmpty()) return; + QTextCursor cur = textCursor(); + cur.setPosition(previous_cursor); + cur.setPosition(0, QTextCursor::KeepAnchor); + setTextCursor(cur); + cur.deleteChar(); + if(!previous_html2.isEmpty()) { + cur.setPosition(previous_cursor2); + cur.setPosition(0, QTextCursor::KeepAnchor); + setTextCursor(cur); + cur.deleteChar(); + insertHtml("" + previous_html2 + "
"); + previous_html = previous_html2; + previous_cursor = textCursor().position(); + } else { + previous_html.clear(); + previous_cursor = 0; + } + previous_temporary = false; + i_pos = i_pos2; + i_pos_p = i_pos_p2; + last_ans = last_ans2; + last_ref = last_ref2; + previous_cursor2 = 0; + i_pos2 = 0; + i_pos_p2 = 0; + previous_html2.clear(); + last_ans2 = 0; + last_ref2 = ""; +} + +void HistoryView::addResult(std::vector values, std::string expression, bool pexact, std::string parse, int exact, bool dual_approx, const QString &image, bool *implicit_warning, int initial_load, size_t index, bool temporary) { + if(temporary && !previous_temporary) { + previous_cursor2 = previous_cursor; + } + if(!temporary && previous_temporary && expression.empty() && parse.empty()) { + clearTemporary(); + } QString serror; bool b_parse_error = (initial_load && !expression.empty() && settings->v_parseerror[index] && !settings->v_messages[index].isEmpty()); if(!initial_load && CALCULATOR->message()) { @@ -373,30 +418,40 @@ void HistoryView::addResult(std::vector values, std::string express if(CALCULATOR->message()->category() == MESSAGE_CATEGORY_IMPLICIT_MULTIPLICATION && (settings->implicit_question_asked || implicit_warning)) { if(!settings->implicit_question_asked) *implicit_warning = true; } else { - MessageType mtype = CALCULATOR->message()->type(); - serror += "color == 2) serror += "#FFAAAA"; - else serror += "#800000"; - } else { - if(settings->color == 2) serror += "#AAAAFF"; - else serror += "#000080"; - } - serror += ""; - if(CALCULATOR->message()->stage() == MESSAGE_STAGE_PARSING) b_parse_error = true; - } - serror += "\">"; QString mstr = QString::fromStdString(CALCULATOR->message()->message()); - if(!mstr.startsWith("-")) serror += "- "; if(settings->printops.use_unicode_signs) { mstr.replace(">=", SIGN_GREATER_OR_EQUAL); mstr.replace("<=", SIGN_LESS_OR_EQUAL); mstr.replace("!=", SIGN_NOT_EQUAL); } - serror += mstr.toHtmlEscaped(); - serror += ""; + MessageType mtype = CALCULATOR->message()->type(); + if(temporary) { + if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) { + if(!serror.isEmpty()) { + if(!serror.startsWith("• ")) serror = "• " + serror; + serror += "\n• "; + } + serror += mstr; + } + } else { + serror += "color == 2) serror += "#FFAAAA"; + else serror += "#800000"; + } else { + if(settings->color == 2) serror += "#AAAAFF"; + else serror += "#000080"; + } + serror += ""; + if(CALCULATOR->message()->stage() == MESSAGE_STAGE_PARSING) b_parse_error = true; + } + serror += "\">"; + if(!mstr.startsWith("-")) serror += "- "; + serror += mstr.toHtmlEscaped(); + if(!temporary) serror += ""; + } } } while(CALCULATOR->nextMessage()); if(serror.isEmpty() && values.empty() && expression.empty() && parse.empty()) return; @@ -406,7 +461,7 @@ void HistoryView::addResult(std::vector values, std::string express if(!expression.empty() || !parse.empty()) { if(initial_load && index != settings->v_expression.size() - 1) str += QString("").arg(paste_h / 4).arg(text_color.name()).arg(paste_h / 2); else str += QString("").arg(paste_h / 4).arg(text_color.name()); - if(!initial_load) { + if(!initial_load && !temporary) { settings->v_expression.push_back(expression); settings->v_parse.push_back(parse); settings->v_pexact.push_back(pexact); @@ -422,7 +477,7 @@ void HistoryView::addResult(std::vector values, std::string express } } gsub("", "", parse); - if(!expression.empty() && (settings->history_expression_type > 0 || parse.empty())) { + if(!temporary && !expression.empty() && (settings->history_expression_type > 0 || parse.empty())) { if(!parse.empty() && settings->history_expression_type > 1 && parse != expression) { str += QString("").arg(initial_load ? (int) index : settings->v_expression.size() - 1); str += QString::fromStdString(expression).toHtmlEscaped(); @@ -442,12 +497,12 @@ void HistoryView::addResult(std::vector values, std::string express str += ""; } } else { - str += QString("").arg(initial_load ? (int) index : settings->v_expression.size() - 1); + if(!temporary) str += QString("").arg(initial_load ? (int) index : settings->v_expression.size() - 1); if(!pexact) str += SIGN_ALMOST_EQUAL " "; if(!settings->colorize_result) str += QString::fromStdString(uncolorize(parse, false)); else str += QString::fromStdString(parse); - str += ""; - if(b_parse_error && !expression.empty()) { + if(!temporary) str += ""; + if(!temporary && b_parse_error && !expression.empty()) { str += "    "; if(settings->color == 2) str += ""; else str += ""; @@ -466,6 +521,10 @@ void HistoryView::addResult(std::vector values, std::string express if(has_lock_symbol) str += " 🔒"; else str += " [P]"; } + if(temporary) { + temporary_error = serror; + if(!serror.isEmpty()) str += QString("\"temporary_error\"/").arg(":/icons/actions/scalable/warning.svg").arg(fm.ascent()); + } str += ""; } else if(!initial_load && !settings->v_result.empty()) { for(size_t i = values.size(); i > 0; i--) { @@ -477,18 +536,21 @@ void HistoryView::addResult(std::vector values, std::string express settings->v_messages[settings->v_messages.size() - 1] += serror; } } - str += serror; + if(!temporary) str += serror; if(initial_load) str += settings->v_messages[index]; str.replace("", ""); - if(!expression.empty() || !parse.empty()) { + if((!expression.empty() || !parse.empty()) && !temporary) { i_pos = str.length(); if(initial_load != 1) i_pos_p = i_pos; } size_t i_answer_pre = 0; + if(temporary && values.empty()) { + values.push_back(""); + } for(size_t i = 0; i < values.size(); i++) { size_t i_answer = 0; if(initial_load) i_answer = settings->v_value[index][i]; - else i_answer = dual_approx && i == 0 ? settings->history_answer.size() - 1 : settings->history_answer.size(); + else if(!temporary) i_answer = dual_approx && i == 0 ? settings->history_answer.size() - 1 : settings->history_answer.size(); QFontMetrics fm(font()); int w = fm.boundingRect("#9999").width(); str += ""; @@ -537,10 +599,14 @@ void HistoryView::addResult(std::vector values, std::string express if(exact < 0) b_exact = -1; else if(exact == 0 && i == values.size() - 1) b_exact = 0; } - if(i_answer == 0) str += QString("").arg(initial_load ? index : settings->v_expression.size() - 1).arg(initial_load ? (int) i : settings->v_result[settings->v_result.size() - 1].size() - i - 1); - else str += QString("").arg(i_answer).arg(initial_load ? index : settings->v_expression.size() - 1).arg(initial_load ? (int) i : settings->v_result[settings->v_result.size() - 1].size() - i - 1); - if(b_exact > 0) str += "= "; - else if(b_exact == 0) str += SIGN_ALMOST_EQUAL " "; + if(!temporary) { + if(i_answer == 0) str += QString("").arg(initial_load ? index : settings->v_expression.size() - 1).arg(initial_load ? (int) i : settings->v_result[settings->v_result.size() - 1].size() - i - 1); + else str += QString("").arg(i_answer).arg(initial_load ? index : settings->v_expression.size() - 1).arg(initial_load ? (int) i : settings->v_result[settings->v_result.size() - 1].size() - i - 1); + } + if(!temporary || !values[i].empty()) { + if(b_exact > 0) str += "= "; + else if(b_exact == 0) str += SIGN_ALMOST_EQUAL " "; + } if(!settings->colorize_result) str += QString::fromStdString(uncolorize(values[i], false)); else str += QString::fromStdString(values[i]); if(!image.isEmpty() && i == values.size() - 1 && w * 2 <= width()) { @@ -567,11 +633,19 @@ void HistoryView::addResult(std::vector values, std::string express } str += "/>"; } - str += ""; + if(!temporary) str += ""; + str += ""; i_answer_pre = i_answer; } str.replace("\n", "
"); int i = 0; + if(!previous_temporary && (!expression.empty() || !parse.empty())) { + previous_html2 = previous_html; + i_pos2 = i_pos; + i_pos_p2 = i_pos_p; + last_ans2 = last_ans; + last_ref2 = last_ref; + } if(!initial_load) { if(settings->format_result) { previous_html.replace("font-size:normal", "font-size:small "); @@ -617,6 +691,11 @@ void HistoryView::addResult(std::vector values, std::string express previous_html.insert(i_pos_p, str); insertHtml("" + previous_html + "
"); previous_cursor = textCursor().position(); + previous_html2 = previous_html; + i_pos2 = i_pos; + i_pos_p2 = i_pos_p; + last_ans2 = last_ans; + last_ref2 = last_ref; } } else { if(initial_load) { @@ -633,7 +712,7 @@ void HistoryView::addResult(std::vector values, std::string express } else { if(previous_html.isEmpty()) { setHtml(""); - } else { + } else if(!previous_temporary) { if(settings->format_result) { previous_html.remove("; font-size:x-large"); previous_html.remove("; font-size:large"); @@ -650,6 +729,7 @@ void HistoryView::addResult(std::vector values, std::string express previous_cursor = textCursor().position(); } } + previous_temporary = temporary; } void HistoryView::changeEvent(QEvent *e) { if(e->type() == QEvent::PaletteChange || e->type() == QEvent::ApplicationPaletteChange) { @@ -678,27 +758,26 @@ void HistoryView::mouseMoveEvent(QMouseEvent *e) { QString str = anchorAt(e->pos()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) bool b_tooltip = false; - if(str.isEmpty() && e->pos().x() > width() - 150) { - if(document()->documentLayout()->imageAt(e->pos()).indexOf(":/data/flags") == 0) { - QPoint p(viewport()->mapFromParent(e->pos())); - for(int n = 0; n < 5; n++) { - QTextCursor cur = cursorForPosition(p); - cur.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); - QString str = cur.selection().toHtml(); - int i = str.lastIndexOf(" alt=\""); - if(i >= 0) { - i += 6; - int i2 = str.indexOf("\"", i + 1); - if(i2 > i) { - str = str.mid(i, i2 - i); - str.replace("\\n", "\n"); - QToolTip::showText(mapToGlobal(e->pos()), str); - b_tooltip = true; - } - break; + if(str.isEmpty() && (document()->documentLayout()->imageAt(e->pos()).indexOf(":/icons") == 0 || (e->pos().x() > width() - 150 && document()->documentLayout()->imageAt(e->pos()).indexOf(":/data/flags") == 0))) { + QPoint p(viewport()->mapFromParent(e->pos())); + for(int n = 0; n < 5; n++) { + QTextCursor cur = cursorForPosition(p); + cur.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); + QString str = cur.selection().toHtml(); + int i = str.lastIndexOf(" alt=\""); + if(i >= 0) { + i += 6; + int i2 = str.indexOf("\"", i + 1); + if(i2 > i) { + str = str.mid(i, i2 - i); + if(str == "temporary_error") str = temporary_error; + else str.replace("\\n", "\n"); + QToolTip::showText(mapToGlobal(e->pos()), str); + b_tooltip = true; } - p.rx() += QFontMetrics(font()).ascent() / 2; + break; } + p.rx() += QFontMetrics(font()).ascent() / 2; } } if(!b_tooltip) QToolTip::hideText(); @@ -857,6 +936,7 @@ void HistoryView::editMoveToTop() { void HistoryView::reloadHistory() { int vpos = verticalScrollBar()->value(); loadInitial(true); + emit historyReloaded(); verticalScrollBar()->setValue(vpos); } void HistoryView::editRemove() { diff --git a/src/historyview.h b/src/historyview.h index ea411ee..391d2ed 100644 --- a/src/historyview.h +++ b/src/historyview.h @@ -35,7 +35,8 @@ class HistoryView : public QTextEdit { QAction *findAction; QDialog *searchDialog; - void addResult(std::vector values, std::string expression = "", bool pexact = true, std::string parse = "", int exact = 1, bool dual_approx = false, const QString &image = QString(), bool *implicit_warning = NULL, int initial_load = 0, size_t index = 0); + void addResult(std::vector values, std::string expression = "", bool pexact = true, std::string parse = "", int exact = 1, bool dual_approx = false, const QString &image = QString(), bool *implicit_warning = NULL, int initial_load = 0, size_t index = 0, bool temporary = false); + void clearTemporary(); void addMessages(); void loadInitial(bool reload = false); void indexAtPos(const QPoint &pos, int *expression_index, int *result_index, int *value_index = NULL, QString *anchorstr = NULL); @@ -43,8 +44,8 @@ class HistoryView : public QTextEdit { protected: - QString s_text, previous_html; - int i_pos, i_pos_p, previous_cursor; + QString s_text, previous_html, previous_html2, temporary_error; + int i_pos, i_pos2, i_pos_p, i_pos_p2, previous_cursor, previous_cursor2, previous_temporary; int has_lock_symbol; QMenu *cmenu; QAction *insertTextAction, *insertValueAction, *copyAction, *copyFormattedAction, *copyAsciiAction, *selectAllAction, *delAction, *clearAction, *protectAction, *movetotopAction; @@ -52,8 +53,8 @@ class HistoryView : public QTextEdit { QRect prev_fonti; QPoint context_pos; QLineEdit *searchEdit; - size_t last_ans; - QString last_ref; + size_t last_ans, last_ans2; + QString last_ref, last_ref2; bool initial_loaded; void mouseDoubleClickEvent(QMouseEvent *e) override; @@ -86,6 +87,7 @@ class HistoryView : public QTextEdit { void insertTextRequested(std::string); void insertValueRequested(int); + void historyReloaded(); }; diff --git a/src/preferencesdialog.cpp b/src/preferencesdialog.cpp index 8613548..32fe36e 100644 --- a/src/preferencesdialog.cpp +++ b/src/preferencesdialog.cpp @@ -59,7 +59,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) : QDialog(parent) { tabs->addTab(w2, tr("Numbers && Operators")); tabs->addTab(w3, tr("Units && Currencies")); tabs->addTab(w4, tr("Parsing && Calculation")); - QCheckBox *box; QGridLayout *l; QComboBox *combo; + QAbstractButton *box; QGridLayout *l; QComboBox *combo; int r = 0; l = new QGridLayout(w1); l->setSizeConstraint(QLayout::SetFixedSize); BOX(tr("Ignore system language (requires restart)"), settings->ignore_locale, ignoreLocaleToggled(bool)); @@ -155,11 +155,18 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) : QDialog(parent) { l->setRowStretch(r, 1); r = 0; l = new QGridLayout(w4); l->setSizeConstraint(QLayout::SetFixedSize); - box = new QCheckBox(tr("Display expression status"), this); l->addWidget(box, r, 0); box->setChecked(settings->display_expression_status); connect(box, SIGNAL(toggled(bool)), this, SLOT(expressionStatusToggled(bool))); - statusBox = box; - QHBoxLayout *hbox = new QHBoxLayout(); - l->addLayout(hbox, r, 1); r++; - hbox->addWidget(new QLabel(tr("Delay:"))); + l->addWidget(new QLabel(tr("Expression status:"), this), r, 0); + combo = new QComboBox(this); + combo->addItem(tr("Off"), 0); + combo->addItem(tr("In history list"), 1); + combo->addItem(tr("In expression field"), 2); + if(!settings->display_expression_status) combo->setCurrentIndex(0); + else if(settings->status_in_history) combo->setCurrentIndex(1); + else combo->setCurrentIndex(2); + statusCombo = combo; + connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(statusModeChanged(int))); + l->addWidget(combo, r, 1); r++; + l->addWidget(new QLabel(tr("Status tooltip delay:")), r, 0); statusDelayWidget = new QSpinBox(this); statusDelayWidget->setRange(0, 10000); statusDelayWidget->setSingleStep(250); @@ -167,8 +174,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) : QDialog(parent) { statusDelayWidget->setSuffix(" " + tr("ms")); statusDelayWidget->setValue(settings->expression_status_delay); connect(statusDelayWidget, SIGNAL(valueChanged(int)), this, SLOT(statusDelayChanged(int))); - hbox->addWidget(statusDelayWidget); - hbox->addStretch(1); + l->addWidget(statusDelayWidget, r, 1); r++; l->addWidget(new QLabel(tr("Expression in history:"), this), r, 0); combo = new QComboBox(this); combo->addItem(tr("Parsed"), 0); @@ -413,10 +419,11 @@ void PreferencesDialog::tooltipsChanged(int i) { settings->enable_tooltips = qobject_cast(sender())->itemData(i).toInt(); emit enableTooltipsChanged(); } -void PreferencesDialog::expressionStatusToggled(bool b) { - settings->display_expression_status = b; - statusDelayWidget->setEnabled(b); - if(!b) QToolTip::hideText(); +void PreferencesDialog::statusModeChanged(int i) { + settings->display_expression_status = (i > 0); + settings->status_in_history = (i == 1); + statusDelayWidget->setEnabled(settings->display_expression_status); + emit statusModeChanged(); } void PreferencesDialog::statusDelayChanged(int v) { settings->expression_status_delay = v; @@ -826,12 +833,14 @@ void PreferencesDialog::updateParsingMode() { } void PreferencesDialog::updateExpressionStatus() { statusDelayWidget->blockSignals(true); - statusBox->blockSignals(true); + statusCombo->blockSignals(true); + if(!settings->display_expression_status) statusCombo->setCurrentIndex(0); + else if(settings->status_in_history) statusCombo->setCurrentIndex(1); + else statusCombo->setCurrentIndex(2); statusDelayWidget->setValue(settings->expression_status_delay); - statusBox->setChecked(settings->display_expression_status); statusDelayWidget->setEnabled(settings->display_expression_status); statusDelayWidget->blockSignals(false); - statusBox->blockSignals(false); + statusCombo->blockSignals(false); } void PreferencesDialog::updateTemperatureCalculation() { tcCombo->blockSignals(true); diff --git a/src/preferencesdialog.h b/src/preferencesdialog.h index 9e23b1e..625dcbb 100644 --- a/src/preferencesdialog.h +++ b/src/preferencesdialog.h @@ -16,7 +16,7 @@ #include -class QCheckBox; +class QAbstractButton; class QSpinBox; class QComboBox; @@ -26,9 +26,9 @@ class PreferencesDialog : public QDialog { protected: - QCheckBox *decimalCommaBox, *ignoreCommaBox, *ignoreDotBox, *statusBox, *ignoreLocaleBox, *variableUnitsBox, *conciseUncertaintyInputBox; + QAbstractButton *decimalCommaBox, *ignoreCommaBox, *ignoreDotBox, *ignoreLocaleBox, *variableUnitsBox, *conciseUncertaintyInputBox; QSpinBox *exratesSpin, *statusDelayWidget; - QComboBox *styleCombo, *parseCombo, *tcCombo, *langCombo, *complexFormCombo, *intervalDisplayCombo, *intervalCalculationCombo; + QComboBox *styleCombo, *parseCombo, *tcCombo, *langCombo, *complexFormCombo, *intervalDisplayCombo, *intervalCalculationCombo, *statusCombo; void closeEvent(QCloseEvent*) override; @@ -37,7 +37,7 @@ class PreferencesDialog : public QDialog { void ignoreLocaleToggled(bool); void keepAboveToggled(bool); void tooltipsChanged(int); - void expressionStatusToggled(bool); + void statusModeChanged(int); void statusDelayChanged(int); void binTwosToggled(bool); void hexTwosToggled(bool); @@ -135,6 +135,7 @@ class PreferencesDialog : public QDialog { void symbolsUpdated(); void historyExpressionTypeChanged(); void binaryBitsChanged(); + void statusModeChanged(); void dialogClosed(); }; diff --git a/src/qalculateqtsettings.cpp b/src/qalculateqtsettings.cpp index 4b74150..52f1876 100644 --- a/src/qalculateqtsettings.cpp +++ b/src/qalculateqtsettings.cpp @@ -71,7 +71,32 @@ bool item_in_calculator(ExpressionItem *item) { if(item->type() == TYPE_FUNCTION) return CALCULATOR->hasFunction((MathFunction*) item); return false; } - +void remove_spaces(std::string &str) { + size_t i = 0; + while(true) { + i = str.find(' ', i); + if(i != std::string::npos) str.erase(i, 1); + else break; + } + i = 0; + while(true) { + i = str.find(THIN_SPACE, i); + if(i != std::string::npos) str.erase(i, strlen(THIN_SPACE)); + else break; + } + i = 0; + while(true) { + i = str.find(NNBSP, i); + if(i != std::string::npos) str.erase(i, strlen(NNBSP)); + else break; + } + i = 0; + while(true) { + i = str.find(NBSP, i); + if(i != std::string::npos) str.erase(i, strlen(NBSP)); + else break; + } +} long int get_fixed_denominator_qt2(const std::string &str, int &to_fraction, char sgn, const QString &localized_fraction, bool qalc_command) { long int fden = 0; if(!qalc_command && (equalsIgnoreCase(str, "fraction") || equalsIgnoreCase(str, localized_fraction.toStdString()))) { @@ -791,6 +816,8 @@ void QalculateQtSettings::readPreferenceValue(const std::string &svar, const std implicit_question_asked = true; } else if(svar == "calculate_as_you_type") { auto_calculate = v; + } else if(svar == "status_in_history") { + status_in_history = v; } } } @@ -879,6 +906,7 @@ void QalculateQtSettings::loadPreferences() { title_type = TITLE_APP; programming_base_changed = false; auto_calculate = true; + status_in_history = true; dot_question_asked = false; implicit_question_asked = false; complex_angle_form = false; @@ -1482,6 +1510,7 @@ bool QalculateQtSettings::savePreferences(const char *filename, bool is_workspac fprintf(file, "local_currency_conversion=%i\n", evalops.local_currency_conversion); fprintf(file, "use_binary_prefixes=%i\n", CALCULATOR->usesBinaryPrefixes()); fprintf(file, "calculate_as_you_type=%i\n", auto_calculate); + fprintf(file, "status_in_history=%i\n", status_in_history); if(previous_precision > 0) fprintf(file, "previous_precision=%i\n", previous_precision); } if(read_default) { diff --git a/src/qalculateqtsettings.h b/src/qalculateqtsettings.h index ecb82cd..6d0ef4c 100644 --- a/src/qalculateqtsettings.h +++ b/src/qalculateqtsettings.h @@ -44,6 +44,7 @@ bool item_in_calculator(ExpressionItem *item); bool name_matches(ExpressionItem *item, const std::string &str); bool country_matches(Unit *u, const std::string &str, size_t minlength = 0); bool title_matches(ExpressionItem *item, const std::string &str, size_t minlength = 0); +void remove_spaces(std::string &str); bool contains_plot_or_save(const std::string &str); void base_from_string(std::string str, int &base, Number &nbase, bool input_base = false); long int get_fixed_denominator_qt(const std::string &str, int &to_fraction, const QString &localized_fraction, bool qalc_command = false); @@ -256,7 +257,7 @@ class QalculateQtSettings : QObject { bool keep_function_dialog_open; bool save_defs_on_exit, save_mode_on_exit, clear_history_on_exit; bool rpn_shown; - bool auto_calculate; + bool auto_calculate, status_in_history; int history_expression_type; bool copy_ascii, copy_ascii_without_units; bool close_with_esc; diff --git a/src/qalculatewindow.cpp b/src/qalculatewindow.cpp index a966d7e..c6a85b2 100644 --- a/src/qalculatewindow.cpp +++ b/src/qalculatewindow.cpp @@ -128,6 +128,8 @@ int to_caf = -1; unsigned int to_bits = 0; Number to_nbase; std::string result_bin, result_oct, result_dec, result_hex; +std::string auto_expression, auto_result; +bool auto_format_updated = false, auto_error = false; bool bases_is_result = false; Number max_bases, min_bases; bool title_modified = false; @@ -920,11 +922,13 @@ QalculateWindow::QalculateWindow() : QMainWindow() { connect(historyView, SIGNAL(insertTextRequested(std::string)), this, SLOT(onInsertTextRequested(std::string))); connect(historyView, SIGNAL(insertValueRequested(int)), this, SLOT(onInsertValueRequested(int))); + connect(historyView, SIGNAL(historyReloaded()), this, SLOT(onHistoryReloaded())); connect(expressionEdit, SIGNAL(returnPressed()), this, SLOT(calculate())); connect(expressionEdit, SIGNAL(textChanged()), this, SLOT(onExpressionChanged())); + connect(expressionEdit, SIGNAL(statusChanged(QString, bool, bool)), this, SLOT(onStatusChanged(QString, bool, bool))); connect(expressionEdit, SIGNAL(toConversionRequested(std::string)), this, SLOT(onToConversionRequested(std::string))); connect(expressionEdit, SIGNAL(calculateRPNRequest(int)), this, SLOT(calculateRPN(int))); - connect(expressionEdit, SIGNAL(expressionStatusModeChanged()), this, SLOT(onExpressionStatusModeChanged())); + connect(expressionEdit, SIGNAL(expressionStatusModeChanged(bool)), this, SLOT(onExpressionStatusModeChanged(bool))); connect(keypadDock, SIGNAL(visibilityChanged(bool)), this, SLOT(onKeypadVisibilityChanged(bool))); connect(basesDock, SIGNAL(visibilityChanged(bool)), this, SLOT(onBasesVisibilityChanged(bool))); connect(rpnDock, SIGNAL(visibilityChanged(bool)), this, SLOT(onRPNVisibilityChanged(bool))); @@ -3258,7 +3262,8 @@ void QalculateWindow::resultFormatUpdated(int delay) { settings->updateMessagePrintOptions(); workspace_changed = true; setResult(NULL, true, false, false); - if(!QToolTip::text().isEmpty()) expressionEdit->displayParseStatus(true); + auto_format_updated = true; + if(!QToolTip::text().isEmpty() || (settings->status_in_history && expressionEdit->expressionHasChanged())) expressionEdit->displayParseStatus(true); } void QalculateWindow::resultDisplayUpdated() { resultFormatUpdated(); @@ -3290,6 +3295,7 @@ void QalculateWindow::expressionCalculationUpdated(int delay) { return; } workspace_changed = true; + auto_format_updated = true; expressionEdit->displayParseStatus(true, !QToolTip::text().isEmpty()); settings->updateMessagePrintOptions(); if(!settings->rpn_mode) { @@ -6054,6 +6060,72 @@ void QalculateWindow::onExpressionChanged() { updateResultBases(); } +void QalculateWindow::onHistoryReloaded() { + if(settings->status_in_history && settings->display_expression_status) { + historyView->clearTemporary(); + auto_expression = ""; + expressionEdit->displayParseStatus(true); + } +} +void QalculateWindow::onStatusChanged(QString status, bool is_expression, bool had_error) { + if(!settings->status_in_history) return; + std::string current_text = expressionEdit->toPlainText().toStdString(); + remove_blank_ends(current_text); + if(status.isEmpty()) { + historyView->clearTemporary(); + auto_expression = ""; + auto_result = ""; + } else if(!is_expression || !settings->auto_calculate || contains_plot_or_save(current_text)) { + if(auto_result.empty() && auto_expression == status.toStdString()) return; + std::vector values; + auto_expression = status.toStdString(); + auto_result = ""; + historyView->addResult(values, current_text, true, auto_expression, false, false, QString(), NULL, 0, 0, true); + } else { + if(!had_error && !auto_error && !auto_format_updated && auto_expression == status.toStdString()) return; + bool b_comp = false, is_approximate = false; + PrintOptions po = settings->printops; + po.is_approximate = &is_approximate; + std::string result = CALCULATOR->unlocalizeExpression(current_text, settings->evalops.parse_options); + CALCULATOR->beginTemporaryStopMessages(); + if(!settings->simplified_percentage) settings->evalops.parse_options.parsing_mode = (ParsingMode) (settings->evalops.parse_options.parsing_mode | PARSE_PERCENT_AS_ORDINARY_CONSTANT); + result = CALCULATOR->calculateAndPrint(result, 50, settings->evalops, po, settings->dual_fraction == 0 ? AUTOMATIC_FRACTION_OFF : AUTOMATIC_FRACTION_SINGLE, settings->dual_approximation == 0 ? AUTOMATIC_APPROXIMATION_OFF : AUTOMATIC_APPROXIMATION_SINGLE, NULL, -1, &b_comp, true, settings->color, TAG_TYPE_HTML); + if(!settings->simplified_percentage) settings->evalops.parse_options.parsing_mode = (ParsingMode) (settings->evalops.parse_options.parsing_mode & ~PARSE_PERCENT_AS_ORDINARY_CONSTANT); + auto_format_updated = false; + std::vector messages; + CALCULATOR->endTemporaryStopMessages(false, &messages); + bool b_error = false, b_warning = false; + for(size_t i = 0; i < messages.size(); i++) { + if(messages[i].type() == MESSAGE_ERROR) { + b_error = true; + break; + } else if(messages[i].type() == MESSAGE_WARNING) { + b_warning = true; + } + } + if(!b_error && !b_warning && !auto_error && auto_result == result && auto_expression == status.toStdString()) return; + auto_error = b_error || b_warning; + std::vector values; + std::string status_nohtml = unhtmlize(status.toStdString()); + std::string result_nohtml = unhtmlize(result); + remove_spaces(result_nohtml); + remove_spaces(current_text); + if(!b_error && !result.empty() && result != CALCULATOR->timedOutString() && result_nohtml.length() < 500 && (!auto_result.empty() || (result_nohtml != status_nohtml && result_nohtml != current_text))) { + auto_result = result; + if(b_comp) { + result.insert(0, "("); + result += ")"; + } + values.push_back(result); + } else { + auto_result = ""; + } + auto_expression = status.toStdString(); + CALCULATOR->addMessages(&messages); + historyView->addResult(values, "", true, auto_expression, !is_approximate, false, QString(), NULL, 0, 0, true); + } +} + void QalculateWindow::onBinaryBitsChanged() { if(basesDock->isVisible() && bases_is_result && settings->current_result) { CALCULATOR->beginTemporaryStopMessages(); @@ -6562,6 +6634,8 @@ void QalculateWindow::setResult(Prefix *prefix, bool update_history, bool update } } if(b_add) { + auto_expression = ""; + auto_result = ""; historyView->addResult(alt_results, update_parse ? prev_result_text : "", !parsed_approx, update_parse ? parsed_text : "", b_exact, alt_results.size() > 1 && !mstruct_exact.isUndefined(), flag, !supress_dialog && update_parse && settings->evalops.parse_options.parsing_mode <= PARSING_MODE_CONVENTIONAL && update_history ? &implicit_warning : NULL); } else if(update_parse) { settings->history_answer.pop_back(); @@ -7447,8 +7521,13 @@ void QalculateWindow::onAppFontChanged() { } if(!settings->use_custom_keypad_font) keypad->setFont(QApplication::font()); } -void QalculateWindow::onExpressionStatusModeChanged() { +void QalculateWindow::onExpressionStatusModeChanged(bool b) { if(preferencesDialog) preferencesDialog->updateExpressionStatus(); + if(b) { + if(!settings->status_in_history) historyView->clearTemporary(); + auto_expression = ""; + expressionEdit->displayParseStatus(true); + } } void QalculateWindow::removeShortcutClicked() { QTreeWidgetItem *item = shortcutList->currentItem(); @@ -7867,6 +7946,7 @@ void QalculateWindow::editPreferences() { connect(preferencesDialog, SIGNAL(symbolsUpdated()), keypad, SLOT(updateSymbols())); connect(preferencesDialog, SIGNAL(historyExpressionTypeChanged()), historyView, SLOT(reloadHistory())); connect(preferencesDialog, SIGNAL(binaryBitsChanged()), this, SLOT(onBinaryBitsChanged())); + connect(preferencesDialog, SIGNAL(statusModeChanged()), this, SLOT(onExpressionStatusModeChanged())); connect(preferencesDialog, SIGNAL(dialogClosed()), this, SLOT(onPreferencesClosed())); if(settings->always_on_top) preferencesDialog->setWindowFlags(preferencesDialog->windowFlags() | Qt::WindowStaysOnTopHint); preferencesDialog->show(); diff --git a/src/qalculatewindow.h b/src/qalculatewindow.h index 85c928a..fdb8d5e 100644 --- a/src/qalculatewindow.h +++ b/src/qalculatewindow.h @@ -189,6 +189,8 @@ class QalculateWindow : public QMainWindow { void onRPNVisibilityChanged(bool); void onRPNClosed(); void onExpressionChanged(); + void onHistoryReloaded(); + void onStatusChanged(QString, bool, bool); void onToConversionRequested(std::string); void onInsertTextRequested(std::string); void onInsertValueRequested(int); @@ -247,7 +249,7 @@ class QalculateWindow : public QMainWindow { void registerChanged(int); void calculateRPN(int); void approximateResult(); - void onExpressionStatusModeChanged(); + void onExpressionStatusModeChanged(bool = true); void functionActivated(); void unitActivated(); void prefixActivated();