Add variables list and units list and conversion dialogs (files was missing in last commit); Add missing itemproxymodel source files; Add calendar conversion dialog; Add unknown edit dialog; Chain mode; Various improvements and fixes

This commit is contained in:
Hanna K 2021-06-26 12:40:57 +02:00
parent b1aeea8a19
commit 0e7096c70c
24 changed files with 2443 additions and 116 deletions

View File

@ -44,8 +44,8 @@ DEFINES += TRANSLATIONS_DIR=\\\"$$TRANSLATIONS_DIR\\\"
DEFINES += ICON_DIR=\\\"$$ICON_DIR\\\"
DEFINES += VERSION=\\\"$$VERSION\\\"
HEADERS += src/expressionedit.h src/fpconversiondialog.h src/functioneditdialog.h src/functionsdialog.h src/historyview.h src/itemproxymodel.h src/keypadwidget.h src/preferencesdialog.h src/qalculateqtsettings.h src/qalculatewindow.h src/unitsdialog.h src/variableeditdialog.h src/variablesdialog.h
SOURCES += src/expressionedit.cpp src/fpconversiondialog.cpp src/functioneditdialog.cpp src/functionsdialog.cpp src/historyview.cpp src/itemproxymodel.cpp src/keypadwidget.cpp src/main.cpp src/preferencesdialog.cpp src/qalculateqtsettings.cpp src/qalculatewindow.cpp src/unitsdialog.cpp src/variableeditdialog.cpp src/variablesdialog.cpp
HEADERS += src/calendarconversiondialog.h src/expressionedit.h src/fpconversiondialog.h src/functioneditdialog.h src/functionsdialog.h src/historyview.h src/itemproxymodel.h src/keypadwidget.h src/preferencesdialog.h src/qalculateqtsettings.h src/qalculatewindow.h src/unitsdialog.h src/unknowneditdialog.h src/variableeditdialog.h src/variablesdialog.h
SOURCES += src/calendarconversiondialog.cpp src/expressionedit.cpp src/fpconversiondialog.cpp src/functioneditdialog.cpp src/functionsdialog.cpp src/historyview.cpp src/itemproxymodel.cpp src/keypadwidget.cpp src/main.cpp src/preferencesdialog.cpp src/qalculateqtsettings.cpp src/qalculatewindow.cpp src/unitsdialog.cpp src/unknowneditdialog.cpp src/variableeditdialog.cpp src/variablesdialog.cpp
unix:!equals(COMPILE_RESOURCES,"yes"):!android:!macx {

View File

@ -0,0 +1,153 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QComboBox>
#include <QLabel>
#include <QSpinBox>
#include <QPushButton>
#include <QMessageBox>
#include <QDebug>
#include "qalculateqtsettings.h"
#include "calendarconversiondialog.h"
CalendarConversionDialog::CalendarConversionDialog(QWidget *parent) : QDialog(parent), block_calendar_conversion(false) {
setWindowTitle(tr("Calendar Conversion"));
QVBoxLayout *box = new QVBoxLayout(this);
QGridLayout *grid = new QGridLayout();
CalendarSystem cs; QString str;
for(int i = 0; i < 10; i++) {
switch(i) {
case 0: {str = tr("Gregorian"); cs = CALENDAR_GREGORIAN; break;}
case 1: {str = tr("Hebrew"); cs = CALENDAR_HEBREW; break;}
case 2: {str = tr("Islamic (Hijri)"); cs = CALENDAR_ISLAMIC; break;}
case 3: {str = tr("Persian (Solar Hijri)"); cs = CALENDAR_PERSIAN; break;}
case 4: {str = tr("Indian (National)"); cs = CALENDAR_INDIAN; break;}
case 5: {str = tr("Chinese"); cs = CALENDAR_CHINESE; break;}
case 6: {str = tr("Julian"); cs = CALENDAR_JULIAN; break;}
case 7: {str = tr("Revised Julian (Milanković)"); cs = CALENDAR_MILANKOVIC; break;}
case 8: {str = tr("Coptic"); cs = CALENDAR_COPTIC; break;}
case 9: {str = tr("Ethiopian"); cs = CALENDAR_ETHIOPIAN; break;}
}
grid->addWidget(new QLabel(str, this), i, 0);
if(cs == CALENDAR_CHINESE) {
chineseStemCombo = new QComboBox(this); chineseBranchCombo = new QComboBox(this);
chineseStemCombo->setProperty("QALCULATE INDEX", i); chineseStemCombo->setProperty("QALCULATE CALENDAR", cs);
chineseBranchCombo->setProperty("QALCULATE INDEX", i); chineseBranchCombo->setProperty("QALCULATE CALENDAR", cs);
for(int i2 = 1; i2 <= 5; i2++) chineseStemCombo->addItem(QString::fromStdString(chineseStemName(i2 * 2)));
for(int i2 = 1; i2 <= 12; i2++) chineseBranchCombo->addItem(QString::fromStdString(chineseBranchName(i2)));
grid->addWidget(chineseStemCombo, i, 1); grid->addWidget(chineseBranchCombo, i, 2);
connect(chineseStemCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateCalendars()));
connect(chineseBranchCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateCalendars()));
} else {
yearEdit[i] = new QSpinBox(this); yearEdit[i]->setRange(INT_MIN, INT_MAX);
yearEdit[i]->setProperty("QALCULATE INDEX", i); yearEdit[i]->setProperty("QALCULATE CALENDAR", cs);
yearEdit[i]->setAlignment(Qt::AlignRight);
grid->addWidget(yearEdit[i], i, 1, 1, 2);
connect(yearEdit[i], SIGNAL(valueChanged(int)), this, SLOT(updateCalendars()));
}
monthCombo[i] = new QComboBox(this); dayCombo[i] = new QComboBox(this);
for(int i2 = 1; i2 <= numberOfMonths(cs); i2++) monthCombo[i]->addItem(QString::fromStdString(monthName(i2, cs)));
for(int i2 = 1; i2 <= 31; i2++) dayCombo[i]->addItem(QString::number(i2));
monthCombo[i]->setProperty("QALCULATE INDEX", i); monthCombo[i]->setProperty("QALCULATE CALENDAR", cs);
dayCombo[i]->setProperty("QALCULATE INDEX", i); dayCombo[i]->setProperty("QALCULATE CALENDAR", cs);
grid->addWidget(monthCombo[i], i, 3); grid->addWidget(dayCombo[i], i, 4);
connect(monthCombo[i], SIGNAL(currentIndexChanged(int)), this, SLOT(updateCalendars()));
connect(dayCombo[i], SIGNAL(currentIndexChanged(int)), this, SLOT(updateCalendars()));
}
box->addLayout(grid);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, this);
box->addWidget(buttonBox);
connect(buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(reject()));
}
CalendarConversionDialog::~CalendarConversionDialog() {}
void CalendarConversionDialog::updateCalendars() {
if(block_calendar_conversion) return;
block_calendar_conversion = true;
int index = sender()->property("QALCULATE INDEX").toInt();
CalendarSystem cs = (CalendarSystem) sender()->property("QALCULATE CALENDAR").toInt();
long int y;
if(cs == CALENDAR_CHINESE) {
long int cy = chineseStemBranchToCycleYear((chineseStemCombo->currentIndex() * 2) + 1, chineseBranchCombo->currentIndex() + 1);
if(cy <= 0) {
QMessageBox::critical(this, tr("Error"), tr("The selected Chinese year does not exist."));
block_calendar_conversion = false;
return;
}
y = chineseCycleYearToYear(79, cy);
} else {
y = yearEdit[index]->value();
}
long int m = monthCombo[index]->currentIndex() + 1;
long int d = dayCombo[index]->currentIndex() + 1;
QalculateDateTime date;
if(!calendarToDate(date, y, m, d, cs)) {
QMessageBox::critical(this, tr("Error"), tr("Conversion to Gregorian calendar failed."));
block_calendar_conversion = false;
return;
}
QString failed_str;
for(int i = 0; i < 10; i++) {
switch(i) {
case 0: {cs = CALENDAR_GREGORIAN; break;}
case 1: {cs = CALENDAR_HEBREW; break;}
case 2: {cs = CALENDAR_ISLAMIC; break;}
case 3: {cs = CALENDAR_PERSIAN; break;}
case 4: {cs = CALENDAR_INDIAN; break;}
case 5: {cs = CALENDAR_CHINESE; break;}
case 6: {cs = CALENDAR_JULIAN; break;}
case 7: {cs = CALENDAR_MILANKOVIC; break;}
case 8: {cs = CALENDAR_COPTIC; break;}
case 9: {cs = CALENDAR_ETHIOPIAN; break;}
}
if(dateToCalendar(date, y, m, d, cs) && y <= INT_MAX && y >= INT_MIN && m <= numberOfMonths(cs) && d <= 31) {
if(cs == CALENDAR_CHINESE) {
long int cy, yc, st, br;
chineseYearInfo(y, cy, yc, st, br);
chineseStemCombo->setCurrentIndex((st - 1) / 2);
chineseBranchCombo->setCurrentIndex(br - 1);
} else {
yearEdit[i]->setValue(y);
}
monthCombo[i]->setCurrentIndex(m - 1);
dayCombo[i]->setCurrentIndex(d - 1);
} else {
if(!failed_str.isEmpty()) failed_str += ", ";
switch(i) {
case 0: {failed_str += tr("Gregorian"); break;}
case 1: {failed_str += tr("Hebrew"); break;}
case 2: {failed_str += tr("Islamic (Hijri)"); break;}
case 3: {failed_str += tr("Persian (Solar Hijri)"); break;}
case 4: {failed_str += tr("Indian (National)"); break;}
case 5: {failed_str += tr("Chinese"); break;}
case 6: {failed_str += tr("Julian"); break;}
case 7: {failed_str += tr("Revised Julian (Milanković)"); break;}
case 8: {failed_str += tr("Coptic"); break;}
case 9: {failed_str += tr("Ethiopian"); break;}
}
}
}
if(!failed_str.isEmpty()) QMessageBox::warning(this, tr("Error"), tr("Calendar conversion failed for: %1.").arg(failed_str));
block_calendar_conversion = false;
}
void CalendarConversionDialog::setDate(QalculateDateTime date) {
block_calendar_conversion = true;
yearEdit[0]->setValue(date.year());
monthCombo[0]->setCurrentIndex(date.month() - 1);
block_calendar_conversion = false;
dayCombo[0]->setCurrentIndex(date.day() - 1);
}

View File

@ -0,0 +1,48 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#ifndef CALENDAR_CONVERSION_DIALOG_H
#define CALENDAR_CONVERSION_DIALOG_H
#include <QDialog>
#include <libqalculate/qalculate.h>
class QComboBox;
class QSpinBox;
class CalendarConversionDialog : public QDialog {
Q_OBJECT
protected:
QSpinBox *yearEdit[10];
QComboBox *monthCombo[10], *dayCombo[10], *chineseStemCombo, *chineseBranchCombo;
bool block_calendar_conversion;
protected slots:
void updateCalendars();
public slots:
void setDate(QalculateDateTime date);
public:
CalendarConversionDialog(QWidget *parent = NULL);
virtual ~CalendarConversionDialog();
};
#endif //CALENDAR_CONVERSION_DIALOG_H

View File

@ -1040,8 +1040,8 @@ void ExpressionEdit::updateCompletion() {
COMPLETION_APPEND(str1, tr("Bijective base-26"), 290, NULL)
COMPLETION_CONVERT_STRING("binary") str1 += " <i>"; str1 += "bin"; str1 += "</i>";
COMPLETION_APPEND(str1, tr("Binary number"), 202, NULL)
/*COMPLETION_CONVERT_STRING("calendars")
COMPLETION_APPEND(str1, tr("Calendars"), 500, NULL)*/
COMPLETION_CONVERT_STRING("calendars")
COMPLETION_APPEND(str1, tr("Calendars"), 500, NULL)
COMPLETION_CONVERT_STRING("cis")
COMPLETION_APPEND(str1, tr("Complex cis form"), 401, NULL)
COMPLETION_CONVERT_STRING("decimal") str1 += " <i>"; str1 += "dec"; str1 += "</i>";
@ -1095,7 +1095,15 @@ void ExpressionEdit::updateCompletion() {
}
void ExpressionEdit::setExpression(std::string str) {
setPlainText(QString::fromStdString(str));
setExpression(QString::fromStdString(str));
}
void ExpressionEdit::setExpression(const QString &str) {
block_add_to_undo++;
setCursorWidth(0);
clear();
block_add_to_undo--;
insertPlainText(str);
setCursorWidth(1);
}
std::string ExpressionEdit::expression() const {
return toPlainText().toStdString();
@ -1130,14 +1138,17 @@ void ExpressionEdit::keyPressEvent(QKeyEvent *event) {
if(event->modifiers() == Qt::NoModifier || event->modifiers() == Qt::GroupSwitchModifier || event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::KeypadModifier) {
switch(event->key()) {
case Qt::Key_Asterisk: {
if(doChainMode(SIGN_MULTIPLICATION)) return;
wrapSelection(SIGN_MULTIPLICATION);
return;
}
case Qt::Key_Minus: {
if(doChainMode(SIGN_MINUS)) return;
wrapSelection(SIGN_MINUS);
return;
}
case Qt::Key_Dead_Circumflex: {
if(doChainMode(settings->caret_as_xor ? " xor " : "^")) return;
wrapSelection(settings->caret_as_xor ? " xor " : "^");
return;
}
@ -1146,14 +1157,17 @@ void ExpressionEdit::keyPressEvent(QKeyEvent *event) {
return;
}
case Qt::Key_AsciiCircum: {
if(doChainMode(settings->caret_as_xor ? " xor " : "^")) return;
wrapSelection(settings->caret_as_xor ? " xor " : "^");
return;
}
case Qt::Key_Plus: {
if(doChainMode("+")) return;
wrapSelection("+");
return;
}
case Qt::Key_Slash: {
if(doChainMode("/")) return;
wrapSelection("/");
return;
}
@ -1168,6 +1182,14 @@ void ExpressionEdit::keyPressEvent(QKeyEvent *event) {
}
if(event->modifiers() == Qt::ControlModifier || (event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier))) {
switch(event->key()) {
case Qt::Key_A: {
if(event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) {
if(doChainMode("")) return;
insertPlainText("");
return;
}
break;
}
case Qt::Key_ParenLeft: {}
case Qt::Key_ParenRight: {
smartParentheses();
@ -1235,8 +1257,8 @@ void ExpressionEdit::keyPressEvent(QKeyEvent *event) {
blockParseStatus(true);
setCursorWidth(0);
if(history_index == -1 && current_history == toPlainText()) history_index = 0;
if(history_index == -1) setPlainText(current_history);
else setPlainText(QString::fromStdString(settings->expression_history[history_index]));
if(history_index == -1) setExpression(current_history);
else setExpression(QString::fromStdString(settings->expression_history[history_index]));
blockParseStatus(false);
blockCompletion(false);
QTextCursor c = textCursor();
@ -1276,10 +1298,10 @@ void ExpressionEdit::keyPressEvent(QKeyEvent *event) {
blockParseStatus(true);
setCursorWidth(0);
if(history_index < 0) {
if(history_index == -1 && current_history != toPlainText()) setPlainText(current_history);
if(history_index == -1 && current_history != toPlainText()) setExpression(current_history);
else clear();
} else {
setPlainText(QString::fromStdString(settings->expression_history[history_index]));
setExpression(QString::fromStdString(settings->expression_history[history_index]));
}
QTextCursor c = textCursor();
c.movePosition(QTextCursor::End);
@ -1444,7 +1466,7 @@ void ExpressionEdit::blockUndo(bool b) {
void ExpressionEdit::setStatusText(QString text) {
if(completionView->isVisible() || text.isEmpty()) {
QToolTip::hideText();
} else {
} else if(settings->display_expression_status) {
if(text.length() >= 30) {
text.replace("\n", "<br>");
QToolTip::showText(mapToGlobal(cursorRect().bottomRight()), text.length() >= 60 ? ("<font size=\"-1\">" + text + "</font>") : ("<font size=\"+0\">" + text + "</font>"));
@ -1455,6 +1477,7 @@ void ExpressionEdit::setStatusText(QString text) {
}
bool ExpressionEdit::displayFunctionHint(MathFunction *f, int arg_index) {
if(!settings->display_expression_status) return false;
if(!f) return false;
int iargs = f->maxargs();
Argument *arg;
@ -1540,7 +1563,6 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) {
if(update) expression_has_changed2 = true;
bool prev_func = cdata->current_function;
cdata->current_function = NULL;
if(!settings->display_expression_status) return;
if(block_display_parse) return;
QString qtext = toPlainText();
std::string text = qtext.toStdString(), str_f;
@ -1930,11 +1952,11 @@ void ExpressionEdit::displayParseStatus(bool update, bool show_tooltip) {
if(had_errors || had_warnings) prev_parsed_expression = QString::fromStdString(parsed_expression_tooltip);
else prev_parsed_expression = QString::fromStdString(parsed_expression);
}
if(!b_func && show_tooltip) setStatusText(prev_parsed_expression);
if(!b_func && show_tooltip) setStatusText(settings->chain_mode ? "" : prev_parsed_expression);
expression_has_changed2 = false;
} else if(!b_func) {
CALCULATOR->clearMessages();
if(prev_func && show_tooltip) setStatusText(prev_parsed_expression);
if(prev_func && show_tooltip) setStatusText(settings->chain_mode ? "" : prev_parsed_expression);
}
settings->evalops.parse_options.preserve_format = false;
}
@ -1985,6 +2007,7 @@ void ExpressionEdit::onTextChanged() {
cdata->current_function = settings->f_answer;
displayParseStatus();
}
if(document()->isEmpty()) expression_has_changed = false;
}
bool ExpressionEdit::expressionHasChanged() {
return expression_has_changed && !document()->isEmpty() && !toPlainText().trimmed().isEmpty();
@ -2813,7 +2836,7 @@ void ExpressionEdit::setCurrentObject() {
l_to = current_object_text.length();
pos = current_object_text.length();
pos2 = pos;
//if(current_object_text[0] == '/') return;
if(l_to > 0 && current_object_text[0] == '/') return;
for(size_t i = 0; i < l_to; i++) {
if(current_object_text[i] == '#') {
current_object_start = -1;
@ -2895,3 +2918,59 @@ void ExpressionEdit::setCurrentObject() {
}
}
}
bool ExpressionEdit::doChainMode(const QString &op) {
if(expression_has_changed && !settings->rpn_mode && settings->chain_mode && !cdata->current_function && settings->evalops.parse_options.base != BASE_UNICODE && (settings->evalops.parse_options.base != BASE_CUSTOM || (CALCULATOR->customInputBase() <= 62 && CALCULATOR->customInputBase() >= -62))) {
QTextCursor cur = textCursor();
if(cur.hasSelection()) {
if(cur.selectionStart() != 0 || cur.selectionEnd() != toPlainText().length()) return false;
} else if(!cur.atEnd()) {
return false;
}
std::string str = toPlainText().toStdString();
remove_blanks(str);
if(str.empty() || str[0] == '/' || CALCULATOR->hasToExpression(str, true, settings->evalops) || CALCULATOR->hasWhereExpression(str, settings->evalops) || last_is_operator(str)) return false;
size_t par_n = 0, vec_n = 0;
for(size_t i = 0; i < str.length(); i++) {
if(str[i] == LEFT_PARENTHESIS_CH) par_n++;
else if(par_n > 0 && str[i] == RIGHT_PARENTHESIS_CH) par_n--;
else if(str[i] == LEFT_VECTOR_WRAP_CH) vec_n++;
else if(vec_n > 0 && str[i] == RIGHT_VECTOR_WRAP_CH) vec_n--;
}
if(par_n > 0 || vec_n > 0) return false;
MathStructure m;
CALCULATOR->clearMessages();
CALCULATOR->calculate(&m, CALCULATOR->unlocalizeExpression(str, settings->evalops.parse_options), 1000, settings->evalops, NULL, NULL, false);
if(m.isAborted()) return false;
PrintOptions po = settings->printops;
po.allow_non_usable = false;
po.is_approximate = NULL;
po.can_display_unicode_string_arg = (void*) this;
str = CALCULATOR->print(m, 1000, settings->printops);
if(str == CALCULATOR->abortedMessage() || str.length() > 100) return false;
std::string warnings;
int message_n = 0;
while(CALCULATOR->message()) {
MessageType mtype = CALCULATOR->message()->type();
if(mtype == MESSAGE_ERROR || mtype == MESSAGE_WARNING) {
if(mtype == MESSAGE_ERROR) return false;
if(message_n > 0) {
if(message_n == 1) warnings.insert(0, "");
warnings += "\n";
}
warnings += CALCULATOR->message()->message();
}
message_n++;
CALCULATOR->nextMessage();
}
if(m.size() > 0 && !m.isFunction() && !m.isVector() && (((!m.isMultiplication() || op != SIGN_MULTIPLICATION) && (!m.isAddition() || (op != "+" && op != SIGN_MINUS)) && (!m.isBitwiseOr() || op != BITWISE_OR) && (!m.isBitwiseAnd() || op != BITWISE_AND)))) {
str.insert(0, "(");
str += ")";
}
setExpression(QString::fromStdString(str) + op);
setStatusText(QString::fromStdString(warnings));
return true;
}
return false;
}

View File

@ -101,6 +101,7 @@ class ExpressionEdit : public QPlainTextEdit {
QSize sizeHint() const;
void wrapSelection(const QString &text = QString(), bool insert_before = false, bool add_parentheses = false);
bool doChainMode(const QString &op);
bool expressionHasChanged();
void setExpressionHasChanged(bool);
void displayParseStatus(bool = false, bool = true);
@ -120,6 +121,7 @@ class ExpressionEdit : public QPlainTextEdit {
void updateCompletion();
void setExpression(std::string);
void setExpression(const QString &str);
void blockCompletion(bool = true);
void blockParseStatus(bool = true);
void blockUndo(bool = true);

View File

@ -25,7 +25,7 @@
#include "fpconversiondialog.h"
FPConversionDialog::FPConversionDialog(QWidget *parent) : QDialog(parent) {
setWindowTitle(tr("Floating point conversion (IEEE 754)"));
setWindowTitle(tr("Floating Point Conversion (IEEE 754)"));
QVBoxLayout *box = new QVBoxLayout(this);
QGridLayout *grid = new QGridLayout();
grid->addWidget(new QLabel(tr("Format"), this), 0, 0, Qt::AlignRight);

View File

@ -18,18 +18,66 @@
#include <QLabel>
#include <QRadioButton>
#include <QButtonGroup>
#include <QKeyEvent>
#include <QDebug>
#include "qalculateqtsettings.h"
#include "functioneditdialog.h"
FunctionEditDialog::FunctionEditDialog(QWidget *parent) : QDialog(parent), read_only(false) {
class MathTextEdit : public QPlainTextEdit {
public:
MathTextEdit(QWidget *parent) : QPlainTextEdit(parent) {
setAttribute(Qt::WA_InputMethodEnabled, settings->enable_input_method);
}
~MathTextEdit() {}
protected:
void keyPressEvent(QKeyEvent *event) override {
if(event->modifiers() == Qt::NoModifier || event->modifiers() == Qt::GroupSwitchModifier || event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::KeypadModifier) {
switch(event->key()) {
case Qt::Key_Asterisk: {
insertPlainText(SIGN_MULTIPLICATION);
return;
}
case Qt::Key_Minus: {
insertPlainText(SIGN_MINUS);
return;
}
case Qt::Key_Dead_Circumflex: {
insertPlainText(settings->caret_as_xor ? " xor " : "^");
return;
}
case Qt::Key_Dead_Tilde: {
insertPlainText("~");
return;
}
case Qt::Key_AsciiCircum: {
if(settings->caret_as_xor) {
insertPlainText(" xor ");
return;
}
break;
}
}
}
if(event->key() == Qt::Key_Asterisk && (event->modifiers() == Qt::ControlModifier || event->modifiers() == (Qt::ControlModifier | Qt::KeypadModifier) || event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier))) {
insertPlainText("^");
return;
}
QPlainTextEdit::keyPressEvent(event);
}
};
FunctionEditDialog::FunctionEditDialog(QWidget *parent) : QDialog(parent) {
QGridLayout *grid = new QGridLayout(this);
grid->addWidget(new QLabel(tr("Name:"), this), 0, 0);
nameEdit = new QLineEdit(this);
grid->addWidget(nameEdit, 0, 1);
grid->addWidget(new QLabel(tr("Expression:"), this), 1, 0, 1, 2);
expressionEdit = new QPlainTextEdit(this);
expressionEdit = new MathTextEdit(this);
grid->addWidget(expressionEdit, 2, 0, 1, 2);
QHBoxLayout *box = new QHBoxLayout();
QButtonGroup *group = new QButtonGroup(this); group->setExclusive(true);
@ -50,17 +98,24 @@ FunctionEditDialog::FunctionEditDialog(QWidget *parent) : QDialog(parent), read_
}
FunctionEditDialog::~FunctionEditDialog() {}
UserFunction *FunctionEditDialog::createFunction() {
UserFunction *FunctionEditDialog::createFunction(MathFunction **replaced_item) {
if(replaced_item) *replaced_item = NULL;
MathFunction *func = NULL;
if(CALCULATOR->functionNameTaken(nameEdit->text().trimmed().toStdString())) {
if(QMessageBox::question(this, tr("Question"), tr("An function with the same name already exists.\nDo you want to overwrite it?")) != QMessageBox::Yes) {
nameEdit->setFocus();
return NULL;
}
if(replaced_item) {
func = CALCULATOR->getActiveFunction(nameEdit->text().trimmed().toStdString());
*replaced_item = func;
}
}
UserFunction *f;
MathFunction *func = CALCULATOR->getActiveFunction(nameEdit->text().trimmed().toStdString());
if(func && func->isLocal() && func->subtype() == SUBTYPE_USER_FUNCTION) {
f = (UserFunction*) func;
if(f->countNames() > 1) f->clearNames();
f->setHidden(false); f->setApproximate(false); f->setDescription(""); f->setCondition(""); f->setExample(""); f->clearArgumentDefinitions(); f->setTitle("");
if(!modifyFunction(f)) return NULL;
return f;
}
@ -70,19 +125,26 @@ UserFunction *FunctionEditDialog::createFunction() {
gsub("y", "\\y", str);
gsub("z", "\\z", str);
}
gsub(SIGN_MULTIPLICATION, "*", str);
gsub(SIGN_MINUS, "-", str);
f = new UserFunction("", nameEdit->text().trimmed().toStdString(), str);
CALCULATOR->addFunction(f);
return f;
}
bool FunctionEditDialog::modifyFunction(MathFunction *f) {
bool FunctionEditDialog::modifyFunction(MathFunction *f, MathFunction **replaced_item) {
if(replaced_item) *replaced_item = NULL;
if(CALCULATOR->functionNameTaken(nameEdit->text().trimmed().toStdString(), f)) {
if(QMessageBox::question(this, tr("Question"), tr("An function with the same name already exists.\nDo you want to overwrite it?")) != QMessageBox::Yes) {
nameEdit->setFocus();
return false;
}
if(replaced_item) {
MathFunction *func = CALCULATOR->getActiveFunction(nameEdit->text().trimmed().toStdString());
if(func != f) *replaced_item = func;
}
}
f->setLocal(true);
if(f->countNames() > 1) f->clearNames();
if(f->countNames() > 1 && f->getName(1).name != nameEdit->text().trimmed().toStdString()) f->clearNames();
f->setName(nameEdit->text().trimmed().toStdString());
if(f->subtype() == SUBTYPE_USER_FUNCTION) {
std::string str = CALCULATOR->unlocalizeExpression(expressionEdit->toPlainText().trimmed().toStdString(), settings->evalops.parse_options);
@ -91,16 +153,21 @@ bool FunctionEditDialog::modifyFunction(MathFunction *f) {
gsub("y", "\\y", str);
gsub("z", "\\z", str);
}
gsub(SIGN_MULTIPLICATION, "*", str);
gsub(SIGN_MINUS, "-", str);
((UserFunction*) f)->setFormula(str);
}
return true;
}
void FunctionEditDialog::setFunction(MathFunction *f) {
read_only = !f->isLocal();
bool read_only = !f->isLocal();
nameEdit->setText(QString::fromStdString(f->getName(1).name));
if(f->subtype() == SUBTYPE_USER_FUNCTION) {
expressionEdit->setEnabled(true);
expressionEdit->setPlainText(QString::fromStdString(CALCULATOR->localizeExpression(((UserFunction*) f)->formula(), settings->evalops.parse_options)));
std::string str = CALCULATOR->localizeExpression(((UserFunction*) f)->formula(), settings->evalops.parse_options);
gsub("*", SIGN_MULTIPLICATION, str);
gsub("-", SIGN_MINUS, str);
expressionEdit->setPlainText(QString::fromStdString(str));
} else {
read_only = true;
expressionEdit->setEnabled(false);
@ -108,16 +175,18 @@ void FunctionEditDialog::setFunction(MathFunction *f) {
}
okButton->setEnabled(!read_only);
nameEdit->setReadOnly(read_only);
ref1Button->setEnabled(!read_only);
ref2Button->setEnabled(!read_only);
expressionEdit->setReadOnly(read_only);
}
void FunctionEditDialog::onNameEdited(const QString &str) {
if(!read_only) okButton->setEnabled(!str.trimmed().isEmpty() && (!expressionEdit->isEnabled() || !expressionEdit->document()->isEmpty()));
okButton->setEnabled(!str.trimmed().isEmpty() && (!expressionEdit->isEnabled() || !expressionEdit->document()->isEmpty()));
if(!str.trimmed().isEmpty() && !CALCULATOR->functionNameIsValid(str.trimmed().toStdString())) {
nameEdit->setText(QString::fromStdString(CALCULATOR->convertToValidFunctionName(str.trimmed().toStdString())));
}
}
void FunctionEditDialog::onExpressionChanged() {
if(!read_only) okButton->setEnabled((!expressionEdit->document()->isEmpty() || !expressionEdit->isEnabled()) && !nameEdit->text().trimmed().isEmpty());
okButton->setEnabled((!expressionEdit->document()->isEmpty() || !expressionEdit->isEnabled()) && !nameEdit->text().trimmed().isEmpty());
}
void FunctionEditDialog::setExpression(const QString &str) {
expressionEdit->setPlainText(str);
@ -134,13 +203,13 @@ void FunctionEditDialog::setRefType(int i) {
if(i == 1) ref1Button->setChecked(true);
else if(i == 2) ref2Button->setChecked(true);
}
bool FunctionEditDialog::editFunction(QWidget *parent, MathFunction *f) {
bool FunctionEditDialog::editFunction(QWidget *parent, MathFunction *f, MathFunction **replaced_item) {
FunctionEditDialog *d = new FunctionEditDialog(parent);
d->setRefType(2);
d->setWindowTitle(tr("Edit Function"));
d->setFunction(f);
while(d->exec() == QDialog::Accepted) {
if(d->modifyFunction(f)) {
if(d->modifyFunction(f, replaced_item)) {
d->deleteLater();
return true;
}
@ -148,7 +217,7 @@ bool FunctionEditDialog::editFunction(QWidget *parent, MathFunction *f) {
d->deleteLater();
return false;
}
UserFunction* FunctionEditDialog::newFunction(QWidget *parent) {
UserFunction* FunctionEditDialog::newFunction(QWidget *parent, MathFunction **replaced_item) {
FunctionEditDialog *d = new FunctionEditDialog(parent);
d->setWindowTitle(tr("New Function"));
d->setRefType(1);
@ -161,7 +230,7 @@ UserFunction* FunctionEditDialog::newFunction(QWidget *parent) {
d->setName(QString::fromStdString(f_name));
UserFunction *f = NULL;
while(d->exec() == QDialog::Accepted) {
f = d->createFunction();
f = d->createFunction(replaced_item);
if(f) break;
}
d->deleteLater();

View File

@ -32,8 +32,6 @@ class FunctionEditDialog : public QDialog {
QPushButton *okButton;
QRadioButton *ref1Button, *ref2Button;
bool read_only;
protected slots:
void onNameEdited(const QString&);
@ -44,16 +42,16 @@ class FunctionEditDialog : public QDialog {
FunctionEditDialog(QWidget *parent = NULL);
virtual ~FunctionEditDialog();
UserFunction *createFunction();
bool modifyFunction(MathFunction *f);
UserFunction *createFunction(MathFunction **replaced_item = NULL);
bool modifyFunction(MathFunction *f, MathFunction **replaced_item = NULL);
void setFunction(MathFunction *f);
void setExpression(const QString&);
QString expression() const;
void setName(const QString&);
void setRefType(int);
static bool editFunction(QWidget *parent, MathFunction *f);
static UserFunction* newFunction(QWidget *parent);
static bool editFunction(QWidget *parent, MathFunction *f, MathFunction **replaced_item = NULL);
static UserFunction* newFunction(QWidget *parent, MathFunction **replaced_item = NULL);
};

View File

@ -136,14 +136,27 @@ void FunctionsDialog::searchChanged(const QString &str) {
functionsView->selectionModel()->setCurrentIndex(functionsModel->index(0, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
void FunctionsDialog::newClicked() {
UserFunction *f = FunctionEditDialog::newFunction(this);
MathFunction *replaced_item = NULL;
UserFunction *f = FunctionEditDialog::newFunction(this, &replaced_item);
if(f) {
if(replaced_item && (replaced_item == f || !CALCULATOR->hasFunction(replaced_item))) {
QModelIndexList list = sourceModel->match(sourceModel->index(0, 0), Qt::UserRole, QVariant::fromValue((void*) replaced_item), 1, Qt::MatchExactly);
if(!list.isEmpty()) sourceModel->removeRow(list[0].row());
}
selected_item = f;
QStandardItem *item = new QStandardItem(QString::fromStdString(f->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) f), Qt::UserRole);
sourceModel->appendRow(item);
functionsModel->invalidate();
if(selected_category != "All" && selected_category != "User items" && selected_category != std::string("/") + f->category()) {
QList<QTreeWidgetItem*> list = categoriesView->findItems("User items", Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
} else {
functionsModel->invalidate();
}
sourceModel->sort(0);
QModelIndex index = functionsModel->mapFromSource(item->index());
if(index.isValid()) {
functionsView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
@ -155,15 +168,28 @@ void FunctionsDialog::newClicked() {
void FunctionsDialog::editClicked() {
QModelIndex index = functionsView->selectionModel()->currentIndex();
if(!index.isValid()) return;
MathFunction *replaced_item = NULL;
MathFunction *f = (MathFunction*) index.data(Qt::UserRole).value<void*>();
if(f && FunctionEditDialog::editFunction(this, f)) {
if(f && FunctionEditDialog::editFunction(this, f, &replaced_item)) {
sourceModel->removeRow(functionsModel->mapToSource(functionsView->selectionModel()->currentIndex()).row());
if(replaced_item && !CALCULATOR->hasFunction(replaced_item)) {
QModelIndexList list = sourceModel->match(sourceModel->index(0, 0), Qt::UserRole, QVariant::fromValue((void*) replaced_item), 1, Qt::MatchExactly);
if(!list.isEmpty()) sourceModel->removeRow(list[0].row());
}
QStandardItem *item = new QStandardItem(QString::fromStdString(f->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) f), Qt::UserRole);
sourceModel->appendRow(item);
selected_item = f;
functionsModel->invalidate();
if(selected_category != "All" && selected_category != "User items" && selected_category != std::string("/") + f->category()) {
QList<QTreeWidgetItem*> list = categoriesView->findItems("User items", Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
} else {
functionsModel->invalidate();
}
sourceModel->sort(0);
QModelIndex index = functionsModel->mapFromSource(item->index());
if(index.isValid()) {
functionsView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
@ -563,7 +589,7 @@ void FunctionsDialog::setSearch(const QString &str) {
searchChanged(str);
}
void FunctionsDialog::selectCategory(std::string str) {
QList<QTreeWidgetItem*> list = categoriesView->findItems((str.empty() || str == "All") ? "All" : "/" + QString::fromStdString(str), Qt::MatchExactly, 1);
QList<QTreeWidgetItem*> list = categoriesView->findItems((str.empty() || str == "All") ? "All" : "/" + QString::fromStdString(str), Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}

92
src/itemproxymodel.cpp Normal file
View File

@ -0,0 +1,92 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include <QDebug>
#include "itemproxymodel.h"
#include <libqalculate/qalculate.h>
ItemProxyModel::ItemProxyModel(QObject *parent) : QSortFilterProxyModel(parent) {
setSortCaseSensitivity(Qt::CaseInsensitive);
setSortLocaleAware(true);
setDynamicSortFilter(false);
}
ItemProxyModel::~ItemProxyModel() {}
bool ItemProxyModel::filterAcceptsRow(int source_row, const QModelIndex&) const {
QModelIndex index = sourceModel()->index(source_row, 0);
if(!index.isValid()) return false;
ExpressionItem *item = (ExpressionItem*) index.data(Qt::UserRole).value<void*>();
if(cat.empty()) return false;
if(cat == "All") {
if(!item->isActive()) return false;
} else if(cat == "Inactive") {
if(item->isActive()) return false;
} else if(cat == "Uncategorized") {
if(!item->isActive() || !item->category().empty() || item->isLocal()) return false;
} else if(cat == "User items") {
if(!item->isActive() || !item->isLocal()) return false;
} else {
if(!item->isActive()) return false;
if(!subcat.empty()) {
size_t l1 = subcat.length(), l2;
l2 = item->category().length();
if((l2 != l1 && (l2 <= l1 || item->category()[l1] != '/')) || item->category().substr(0, l1) != subcat) return false;
} else {
if(item->category() != cat) return false;
}
}
if(filter.empty()) return true;
std::string title = item->title(true);
remove_blank_ends(title);
while(title.length() >= filter.length()) {
if(equalsIgnoreCase(filter, title.substr(0, filter.length()))) {
return true;
}
size_t i = title.find(' ');
if(i == std::string::npos) break;
title = title.substr(i + 1);
remove_blank_ends(title);
}
for(size_t i2 = 1; i2 <= item->countNames(); i2++) {
if(item->getName(i2).case_sensitive) {
if(filter == item->getName(i2).name.substr(0, filter.length())) return true;
} else {
if(equalsIgnoreCase(filter, item->getName(i2).name.substr(0, filter.length()))) return true;
}
}
return false;
}
void ItemProxyModel::setFilter(std::string scat, std::string sfilter) {
remove_blank_ends(sfilter);
if(cat != scat || filter != sfilter) {
cat = scat;
if(cat[0] == '/') subcat = cat.substr(1, cat.length() - 1);
else subcat = "";
filter = sfilter;
invalidateFilter();
}
}
void ItemProxyModel::setSecondaryFilter(std::string sfilter) {
remove_blank_ends(sfilter);
if(filter != sfilter) {
filter = sfilter;
invalidateFilter();
}
}
std::string ItemProxyModel::currentFilter() const {
return cat;
}
std::string ItemProxyModel::currentSecondaryFilter() const {
return filter;
}

39
src/itemproxymodel.h Normal file
View File

@ -0,0 +1,39 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#ifndef ITEM_PROXY_MODEL_H
#define ITEM_PROXY_MODEL_H
#include <QSortFilterProxyModel>
class ItemProxyModel : public QSortFilterProxyModel {
Q_OBJECT
public:
ItemProxyModel(QObject *parent = NULL);
~ItemProxyModel();
void setFilter(std::string, std::string = "");
void setSecondaryFilter(std::string);
std::string currentFilter() const;
std::string currentSecondaryFilter() const;
protected:
std::string cat, subcat, filter;
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
};
#endif //ITEM_PROXY_MODEL_H

View File

@ -124,6 +124,8 @@ int main(int argc, char **argv) {
return 1;
}
settings->f_answer->setCategory(CALCULATOR->getFunctionById(FUNCTION_ID_WARNING)->category());
CALCULATOR->loadLocalDefinitions();
QalculateWindow *win = new QalculateWindow();

View File

@ -39,8 +39,15 @@ bool string_is_less(std::string str1, std::string str2) {
if(b_uni) return QString::fromStdString(str1).compare(QString::fromStdString(str2)) < 0;
return str1 < str2;
}
bool item_in_calculator(ExpressionItem *item) {
if(!CALCULATOR->stillHasVariable((Variable*) item) || !CALCULATOR->stillHasFunction((MathFunction*) item) || !CALCULATOR->stillHasUnit((Unit*) item)) return false;
if(item->type() == STRUCT_VARIABLE) return CALCULATOR->hasVariable((Variable*) item);
if(item->type() == STRUCT_UNIT) return CALCULATOR->hasUnit((Unit*) item);
if(item->type() == STRUCT_FUNCTION) return CALCULATOR->hasFunction((MathFunction*) item);
return false;
}
AnswerFunction::AnswerFunction() : MathFunction(QApplication::tr("answer").toStdString(), 1, 1, CALCULATOR->f_warning->category(), QApplication::tr("History Answer Value").toStdString()) {
AnswerFunction::AnswerFunction() : MathFunction(QApplication::tr("answer").toStdString(), 1, 1, "", QApplication::tr("History Answer Value").toStdString()) {
if(QApplication::tr("answer") != "answer") addName("answer");
VectorArgument *arg = new VectorArgument(QApplication::tr("History Index(es)").toStdString());
arg->addArgument(new IntegerArgument("", ARGUMENT_MIN_MAX_NONZERO, true, true, INTEGER_TYPE_SINT));
@ -179,11 +186,12 @@ void QalculateQtSettings::loadPreferences() {
dual_approximation = -1;
auto_update_exchange_rates = 7;
rpn_mode = false;
rpn_keys = true;
caret_as_xor = false;
do_imaginary_j = false;
color = 1;
colorize_result = true;
rpn_mode = false;
chain_mode = false;
enable_input_method = false;
enable_completion = true;
enable_completion2 = true;
@ -235,8 +243,7 @@ void QalculateQtSettings::loadPreferences() {
v = s2i(svalue);
if(svar == "version") {
parse_qalculate_version(svalue, version_numbers);
/*} else if(svar == "allow_multiple_instances") {
if(v == 0 && version_numbers[0] < 3) v = -1;
} else if(svar == "allow_multiple_instances") {
allow_multiple_instances = v;
} else if(svar == "always_on_top") {
always_on_top = v;
@ -246,9 +253,9 @@ void QalculateQtSettings::loadPreferences() {
save_mode_on_exit = v;
} else if(svar == "save_definitions_on_exit") {
save_defs_on_exit = v;
} else if(svar == "clear_history_on_exit") {
clear_history_on_exit = v;*/
} else if(svar == "window_state") {
}/* else if(svar == "clear_history_on_exit") {
clear_history_on_exit = v;
}*/ else if(svar == "window_state") {
window_state = QByteArray::fromBase64(svalue.c_str());
} else if(svar == "replace_expression") {
replace_expression = v;
@ -262,10 +269,6 @@ void QalculateQtSettings::loadPreferences() {
functions_vsplitter_state = QByteArray::fromBase64(svalue.c_str());
} else if(svar == "functions_hsplitter_state") {
functions_hsplitter_state = QByteArray::fromBase64(svalue.c_str());
} else if(svar == "keep_function_dialog_open") {
keep_function_dialog_open = v;
} else if(svar == "always_on_top") {
always_on_top = v;
} else if(svar == "style") {
style = v;
} else if(svar == "palette") {
@ -490,16 +493,12 @@ void QalculateQtSettings::loadPreferences() {
if(v >= INTERVAL_CALCULATION_NONE && v <= INTERVAL_CALCULATION_SIMPLE_INTERVAL_ARITHMETIC) {
evalops.interval_calculation = (IntervalCalculation) v;
}
/*} else if(svar == "chain_mode") {
chain_mode = v;*/
} else if(svar == "in_rpn_mode") {
} else if(svar == "chain_mode") {
chain_mode = v;
} else if(svar == "rpn_mode") {
rpn_mode = v;
/*} else if(svar == "rpn_keys") {
rpn_keys = v;*/
} else if(svar == "rpn_syntax") {
if(v) {
evalops.parse_options.parsing_mode = PARSING_MODE_RPN;
}
} else if(svar == "rpn_keys") {
rpn_keys = v;
} else if(svar == "limit_implicit_multiplication") {
evalops.parse_options.limit_implicit_multiplication = v;
printops.limit_implicit_multiplication = v;
@ -570,14 +569,14 @@ void QalculateQtSettings::loadPreferences() {
updateMessagePrintOptions();
std::string ans_str = "ans";
vans[0] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str, m_undefined, QApplication::tr("Last Answer").toStdString(), false));
vans[0] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str, m_undefined, QApplication::tr("Last Answer").toStdString(), false, true));
vans[0]->addName(QApplication::tr("answer").toStdString());
vans[0]->addName(ans_str + "1");
vans[1] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "2", m_undefined, QApplication::tr("Answer 2").toStdString(), false));
vans[2] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "3", m_undefined, QApplication::tr("Answer 3").toStdString(), false));
vans[3] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "4", m_undefined, QApplication::tr("Answer 4").toStdString(), false));
vans[4] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "5", m_undefined, QApplication::tr("Answer 5").toStdString(), false));
v_memory = new KnownVariable(CALCULATOR->temporaryCategory(), "", m_zero, QApplication::tr("Memory").toStdString(), true, true);
vans[1] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "2", m_undefined, QApplication::tr("Answer 2").toStdString(), false, true));
vans[2] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "3", m_undefined, QApplication::tr("Answer 3").toStdString(), false, true));
vans[3] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "4", m_undefined, QApplication::tr("Answer 4").toStdString(), false, true));
vans[4] = (KnownVariable*) CALCULATOR->addVariable(new KnownVariable(CALCULATOR->temporaryCategory(), ans_str + "5", m_undefined, QApplication::tr("Answer 5").toStdString(), false, true));
v_memory = new KnownVariable(CALCULATOR->temporaryCategory(), "", m_zero, QApplication::tr("Memory").toStdString(), false, true);
ExpressionName ename;
ename.name = "MR";
ename.case_sensitive = true;
@ -656,6 +655,12 @@ void QalculateQtSettings::savePreferences(bool save_mode) {
}
fprintf(file, "\n[General]\n");
fprintf(file, "version=%s\n", VERSION);
fprintf(file, "ignore_locale=%i\n", ignore_locale);
/*fprintf(file, "check_version=%i\n", check_version);
if(check_version) {
fprintf(file, "last_version_check=%s\n", last_version_check_date.toISOString().c_str());
if(!last_found_version.empty()) fprintf(file, "last_found_version=%s\n", last_found_version.c_str());
}*/
fprintf(file, "window_state=%s\n", window_state.toBase64().data());
fprintf(file, "window_geometry=%s\n", window_geometry.toBase64().data());
fprintf(file, "splitter_state=%s\n", splitter_state.toBase64().data());
@ -671,7 +676,9 @@ void QalculateQtSettings::savePreferences(bool save_mode) {
if(!variables_hsplitter_state.isEmpty()) fprintf(file, "variables_hsplitter_state=%s\n", variables_hsplitter_state.toBase64().data());
fprintf(file, "always_on_top=%i\n", always_on_top);
if(title_type != TITLE_APP) fprintf(file, "window_title_mode=%i\n", title_type);
fprintf(file, "ignore_locale=%i\n", ignore_locale);
fprintf(file, "save_mode_on_exit=%i\n", save_mode_on_exit);
fprintf(file, "save_definitions_on_exit=%i\n", save_defs_on_exit);
//fprintf(file, "clear_history_on_exit=%i\n", clear_history_on_exit);
fprintf(file, "enable_input_method=%i\n", enable_input_method);
fprintf(file, "display_expression_status=%i\n", display_expression_status);
fprintf(file, "enable_completion=%i\n", enable_completion);
@ -695,6 +702,9 @@ void QalculateQtSettings::savePreferences(bool save_mode) {
if(use_custom_keypad_font || save_custom_keypad_font) fprintf(file, "custom_keypad_font=%s\n", custom_keypad_font.c_str());
if(use_custom_app_font || save_custom_app_font) fprintf(file, "custom_application_font=%s\n", custom_app_font.c_str());
fprintf(file, "replace_expression=%i\n", replace_expression);
fprintf(file, "rpn_keys=%i\n", rpn_keys);
/*if(default_bits >= 0) fprintf(file, "bit_width=%i\n", default_bits);
if(default_signed >= 0) fprintf(file, "signed_integer=%i\n", default_signed);*/
fprintf(file, "spell_out_logical_operators=%i\n", printops.spell_out_logical_operators);
fprintf(file, "caret_as_xor=%i\n", caret_as_xor);
fprintf(file, "digit_grouping=%i\n", printops.digit_grouping);
@ -703,8 +713,13 @@ void QalculateQtSettings::savePreferences(bool save_mode) {
fprintf(file, "comma_as_separator=%i\n", evalops.parse_options.comma_as_separator);
fprintf(file, "twos_complement=%i\n", printops.twos_complement);
fprintf(file, "hexadecimal_twos_complement=%i\n", printops.hexadecimal_twos_complement);
/*fprintf(file, "twos_complement_input=%i\n", twos_complement_in);
fprintf(file, "hexadecimal_twos_complement_input=%i\n", hexadecimal_twos_complement_in);*/
fprintf(file, "use_unicode_signs=%i\n", printops.use_unicode_signs);
fprintf(file, "lower_case_numbers=%i\n", printops.lower_case_numbers);
fprintf(file, "e_notation=%i\n", printops.lower_case_e);
fprintf(file, "imaginary_j=%i\n", CALCULATOR->v_i->hasName("j") > 0);
fprintf(file, "base_display=%i\n", printops.base_display);
if(tc_set) fprintf(file, "temperature_calculation=%i\n", CALCULATOR->getTemperatureCalculationMode());
fprintf(file, "auto_update_exchange_rates=%i\n", auto_update_exchange_rates);
fprintf(file, "local_currency_conversion=%i\n", evalops.local_currency_conversion);
@ -736,7 +751,6 @@ void QalculateQtSettings::savePreferences(bool save_mode) {
fprintf(file, "place_units_separately=%i\n", printops.place_units_separately);
fprintf(file, "auto_post_conversion=%i\n", evalops.auto_post_conversion);
fprintf(file, "mixed_units_conversion=%i\n", evalops.mixed_units_conversion);
fprintf(file, "local_currency_conversion=%i\n", evalops.local_currency_conversion);
fprintf(file, "number_base=%i\n", printops.base);
if(!CALCULATOR->customOutputBase().isZero()) fprintf(file, "custom_number_base=%s\n", CALCULATOR->customOutputBase().print(CALCULATOR->save_printoptions).c_str());
fprintf(file, "number_base_expression=%i\n", evalops.parse_options.base);
@ -763,6 +777,8 @@ void QalculateQtSettings::savePreferences(bool save_mode) {
else if(dual_approximation > 0) fprintf(file, "approximation=%i\n", APPROXIMATION_APPROXIMATE + 1);
else fprintf(file, "approximation=%i\n", evalops.approximation);
fprintf(file, "interval_calculation=%i\n", evalops.interval_calculation);
fprintf(file, "rpn_mode=%i\n", rpn_mode);
fprintf(file, "chain_mode=%i\n", chain_mode);
fprintf(file, "limit_implicit_multiplication=%i\n", evalops.parse_options.limit_implicit_multiplication);
fprintf(file, "parsing_mode=%i\n", evalops.parse_options.parsing_mode);
fprintf(file, "spacious=%i\n", printops.spacious);

View File

@ -32,6 +32,7 @@ std::string unhtmlize(std::string str);
QIcon load_icon(const QString &str, QWidget*);
bool last_is_operator(std::string str, bool allow_exp = false);
bool string_is_less(std::string str1, std::string str2);
bool item_in_calculator(ExpressionItem *item);
enum {
TITLE_APP,
@ -69,7 +70,7 @@ class QalculateQtSettings : QObject {
EvaluationOptions evalops;
PrintOptions printops;
bool complex_angle_form, dot_question_asked, adaptive_interval_display, tc_set, rpn_mode, caret_as_xor, ignore_locale, do_imaginary_j, fetch_exchange_rates_at_startup, always_on_top, display_expression_status, prefixes_default;
bool complex_angle_form, dot_question_asked, adaptive_interval_display, tc_set, rpn_mode, chain_mode, caret_as_xor, ignore_locale, do_imaginary_j, fetch_exchange_rates_at_startup, always_on_top, display_expression_status, prefixes_default, rpn_keys, allow_multiple_instances;
int decimal_comma, dual_fraction, dual_approximation, auto_update_exchange_rates, title_type;
int completion_delay;
int completion_min, completion_min2;

View File

@ -48,6 +48,7 @@
#include "expressionedit.h"
#include "historyview.h"
#include "keypadwidget.h"
#include "unknowneditdialog.h"
#include "variableeditdialog.h"
#include "functioneditdialog.h"
#include "preferencesdialog.h"
@ -55,6 +56,7 @@
#include "variablesdialog.h"
#include "unitsdialog.h"
#include "fpconversiondialog.h"
#include "calendarconversiondialog.h"
class ViewThread : public Thread {
protected:
@ -216,6 +218,7 @@ QalculateWindow::QalculateWindow() : QMainWindow() {
variablesDialog = NULL;
unitsDialog = NULL;
fpConversionDialog = NULL;
calendarConversionDialog = NULL;
QVBoxLayout *topLayout = new QVBoxLayout(w_top);
QHBoxLayout *hLayout = new QHBoxLayout();
@ -237,16 +240,23 @@ QalculateWindow::QalculateWindow() : QMainWindow() {
menu2 = menu;
menu = menu2->addMenu(tr("New"));
menu->addAction(tr("Function"), this, SLOT(newFunction()));
menu->addAction(tr("Variable"), this, SLOT(newVariable()));
menu->addAction(tr("Variable/Constant"), this, SLOT(newVariable()));
menu->addAction(tr("Unknown Variable"), this, SLOT(newUnknown()));
menu = menu2;
menu->addSeparator();
menu->addAction(tr("Functions list"), this, SLOT(openFunctions()));
menu->addAction(tr("Units list"), this, SLOT(openUnits()));
menu->addAction(tr("Variables list"), this, SLOT(openVariables()));
menu->addAction(tr("Functions List"), this, SLOT(openFunctions()), Qt::CTRL | Qt::Key_F);
menu->addAction(tr("Variables and Constants List"), this, SLOT(openVariables()), Qt::CTRL | Qt::Key_M);
menu->addAction(tr("Units List and Conversion"), this, SLOT(openUnits()), Qt::CTRL | Qt::Key_U);
menu->addSeparator();
menu->addAction(tr("Floating point conversion (IEEE 754)"), this, SLOT(openFPConversion()));
menu->addAction(tr("Floating Point Conversion (IEEE 754)"), this, SLOT(openFPConversion()));
menu->addAction(tr("Calendar Conversion"), this, SLOT(openCalendarConversion()));
menu->addSeparator();
menu->addAction(tr("Update exchange rates"), this, SLOT(fetchExchangeRates()));
menu->addAction(tr("Update Exchange Rates"), this, SLOT(fetchExchangeRates()));
menu->addSeparator();
group = new QActionGroup(this); group->setExclusionPolicy(QActionGroup::ExclusionPolicy::Exclusive);
action = menu->addAction(tr("Normal Mode"), this, SLOT(normalModeActivated())); action->setCheckable(true); group->addAction(action); action->setObjectName("action_normalmode"); if(!settings->rpn_mode && !settings->chain_mode) action->setChecked(true);
action = menu->addAction(tr("RPN Mode"), this, SLOT(rpnModeActivated()), Qt::CTRL | Qt::Key_R); action->setCheckable(true); group->addAction(action); action->setObjectName("action_rpnmode"); if(settings->rpn_mode) action->setChecked(true);
action = menu->addAction(tr("Chain Mode"), this, SLOT(chainModeActivated())); action->setCheckable(true); group->addAction(action); action->setObjectName("action_chainmode"); if(settings->chain_mode) action->setChecked(true);
menu->addSeparator();
menu->addAction(tr("Preferences"), this, SLOT(editPreferences()));
menu->addSeparator();
@ -459,7 +469,7 @@ QalculateWindow::QalculateWindow() : QMainWindow() {
toAction = new QAction(LOAD_ICON("convert"), tr("Convert"));
connect(toAction, SIGNAL(triggered(bool)), this, SLOT(onToActivated()));
tb->addAction(toAction);
storeAction = new QAction(LOAD_ICON("document-save"), tr("Store"));
storeAction = new QAction(LOAD_ICON("document-save"), tr("Store")); storeAction->setShortcut(QKeySequence::Save);
connect(storeAction, SIGNAL(triggered(bool)), this, SLOT(onStoreActivated()));
tb->addAction(storeAction);
functionsAction = new QAction(LOAD_ICON("function"), tr("Functions"));
@ -660,7 +670,7 @@ void QalculateWindow::onOperatorClicked(const QString &str) {
if(expressionEdit->textCursor().hasSelection()) expressionEdit->wrapSelection(SIGN_MULTIPLICATION "10^");
else expressionEdit->insertPlainText(settings->printops.lower_case_e ? "e" : str);
} else {
expressionEdit->wrapSelection(str);
if(!expressionEdit->doChainMode(str)) expressionEdit->wrapSelection(str);
}
if(!expressionEdit->hasFocus()) expressionEdit->setFocus();
if(do_exec) calculate();
@ -679,11 +689,14 @@ void QalculateWindow::onFunctionClicked(MathFunction *f) {
expressionEdit->blockParseStatus();
QTextCursor cur = expressionEdit->textCursor();
bool do_exec = false;
if(cur.hasSelection()) {
if(settings->chain_mode) {
if(!expressionEdit->document()->isEmpty()) {
expressionEdit->selectAll();
do_exec = true;
}
} else if(cur.hasSelection()) {
do_exec = cur.selectionStart() == 0 && cur.selectionEnd() == expressionEdit->toPlainText().length();
} else if(last_is_number(expressionEdit->toPlainText().toStdString())) {
expressionEdit->selectAll();
do_exec = true;
}
expressionEdit->wrapSelection(f->referenceName() == "neg" ? SIGN_MINUS : QString::fromStdString(f->preferredInputName(settings->printops.abbreviate_names, settings->printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) expressionEdit).name), true, true);
if(!expressionEdit->hasFocus()) expressionEdit->setFocus();
@ -846,7 +859,7 @@ void QalculateWindow::socketReadyRead() {
}
}
command = command.mid(1).trimmed();
if(!command.isEmpty()) {expressionEdit->setPlainText(command); calculate();}
if(!command.isEmpty()) {expressionEdit->setExpression(command); calculate();}
}
void QalculateWindow::onActivateRequested(const QStringList &arguments, const QString&) {
if(!arguments.isEmpty()) {
@ -858,7 +871,7 @@ void QalculateWindow::onActivateRequested(const QStringList &arguments, const QS
command += args.at(i);
}
command = command.trimmed();
if(!command.isEmpty()) {expressionEdit->setPlainText(command); calculate();}
if(!command.isEmpty()) {expressionEdit->setExpression(command); calculate();}
args.clear();
}
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
@ -872,7 +885,7 @@ void QalculateWindow::calculate() {
calculateExpression();
}
void QalculateWindow::calculate(const QString &expression) {
expressionEdit->setPlainText(expression);
expressionEdit->setExpression(expression);
calculate();
}
@ -882,7 +895,7 @@ void QalculateWindow::setPreviousExpression() {
} else {
expressionEdit->blockCompletion();
expressionEdit->blockParseStatus();
expressionEdit->setPlainText(QString::fromStdString(previous_expression));
expressionEdit->setExpression(QString::fromStdString(previous_expression));
expressionEdit->selectAll();
expressionEdit->setExpressionHasChanged(false);
expressionEdit->blockCompletion(false);
@ -1240,7 +1253,20 @@ void QalculateWindow::setOption(std::string str) {
expressionFormatUpdated(false);
}
} else if(equalsIgnoreCase(svar, "rpn") && svalue.find(" ") == std::string::npos) {
//SET_BOOL_MENU("menu_item_rpn_mode")
bool b = settings->rpn_mode;
SET_BOOL(b)
if(b != settings->rpn_mode) {
QAction *w = NULL;
if(b) w = findChild<QAction*>("action_rpnmode");
else w = findChild<QAction*>("action_normalmode");
if(w) {
w->blockSignals(true);
w->setChecked(true);
w->blockSignals(false);
}
if(b) rpnModeActivated();
else normalModeActivated();
}
} else if(equalsIgnoreCase(svar, "short multiplication") || svar == "shortmul") SET_BOOL_D(settings->printops.short_multiplication)
else if(equalsIgnoreCase(svar, "lowercase e") || svar == "lowe") SET_BOOL_D(settings->printops.lower_case_e)
else if(equalsIgnoreCase(svar, "lowercase numbers") || svar == "lownum") SET_BOOL_D(settings->printops.lower_case_numbers)
@ -1847,9 +1873,12 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
remove_blank_ends(str);
if(equalsIgnoreCase(str, "mode")) {
settings->savePreferences();
if(current_expr) expressionEdit->clear();
} else if(equalsIgnoreCase(str, "definitions")) {
if(!CALCULATOR->saveDefinitions()) {
QMessageBox::critical(this, tr("Error"), tr("Couldn't write definitions"), QMessageBox::Ok);
} else {
if(current_expr) expressionEdit->clear();
}
} else {
std::string name = str, cat, title;
@ -1920,6 +1949,8 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
}
expressionEdit->updateCompletion();
if(variablesDialog) variablesDialog->updateVariables();
if(unitsDialog) unitsDialog->updateUnits();
if(current_expr) expressionEdit->clear();
}
}
}
@ -1974,6 +2005,8 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
}
expressionEdit->updateCompletion();
if(variablesDialog) variablesDialog->updateVariables();
if(unitsDialog) unitsDialog->updateUnits();
if(current_expr) expressionEdit->clear();
}
} else if(equalsIgnoreCase(scom, "function")) {
str = str.substr(ispace + 1, slen - (ispace + 1));
@ -2030,6 +2063,7 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
}
expressionEdit->updateCompletion();
if(functionsDialog) functionsDialog->updateFunctions();
if(current_expr) expressionEdit->clear();
}
} else if(equalsIgnoreCase(scom, "delete")) {
str = str.substr(ispace + 1, slen - (ispace + 1));
@ -2039,12 +2073,15 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
v->destroy();
expressionEdit->updateCompletion();
if(variablesDialog) variablesDialog->updateVariables();
if(unitsDialog) unitsDialog->updateUnits();
if(current_expr) expressionEdit->clear();
} else {
MathFunction *f = CALCULATOR->getActiveFunction(str);
if(f && f->isLocal()) {
f->destroy();
expressionEdit->updateCompletion();
if(functionsDialog) functionsDialog->updateFunctions();
if(current_expr) expressionEdit->clear();
} else {
CALCULATOR->error(true, "No user-defined variable or function with the specified name (%s) exist.", str.c_str(), NULL);
}
@ -2264,12 +2301,16 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
variablesDialog->selectCategory("All");
variablesDialog->setSearch(QString::fromStdString(str));
}
if(current_expr) expressionEdit->clear();
} else {
CALCULATOR->error(true, "No function, variable, or unit with the specified name (%s) was found.", str.c_str(), NULL);
}
} else {
if(current_expr) expressionEdit->clear();
}
} else if(equalsIgnoreCase(str, "quit") || equalsIgnoreCase(str, "exit")) {
qApp->closeAllWindows();
return;
} else {
CALCULATOR->error(true, "Unknown command: %s.", str.c_str(), NULL);
}
@ -2473,7 +2514,7 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
if(from_str.empty()) {
b_busy--;
if(current_expr) setPreviousExpression();
//on_popup_menu_item_calendarconversion_activate(NULL, NULL);
openCalendarConversion();
return;
}
do_calendars = true;
@ -2986,7 +3027,7 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
setResult(NULL, true, stack_index == 0, true, "", stack_index);
if(do_bases) basesDock->show();
//if(do_calendars) on_popup_menu_item_calendarconversion_activate(NULL, NULL);
if(do_calendars) openCalendarConversion();
settings->evalops.complex_number_form = cnf_bak;
settings->evalops.auto_post_conversion = save_auto_post_conversion;
@ -3004,19 +3045,22 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
}
expressionEdit->blockCompletion();
expressionEdit->blockParseStatus();
if(settings->replace_expression == CLEAR_EXPRESSION) {
if(settings->chain_mode) {
if(exact_text == "0" || result_text == "0") expressionEdit->clear();
expressionEdit->setExpression(QString::fromStdString(unhtmlize(result_text)));
} else if(settings->replace_expression == CLEAR_EXPRESSION) {
expressionEdit->clear();
} else if(settings->replace_expression == REPLACE_EXPRESSION_WITH_RESULT || settings->replace_expression == REPLACE_EXPRESSION_WITH_RESULT_IF_SHORTER) {
if(settings->replace_expression == REPLACE_EXPRESSION_WITH_RESULT || (!exact_text.empty() && unicode_length(exact_text) < unicode_length(from_str))) {
if(exact_text == "0") expressionEdit->clear();
else if(exact_text.empty()) expressionEdit->setPlainText(QString::fromStdString(unhtmlize(result_text)));
else expressionEdit->setPlainText(QString::fromStdString(exact_text));
if(exact_text == "0" || result_text == "0") expressionEdit->clear();
else if(exact_text.empty()) expressionEdit->setExpression(QString::fromStdString(unhtmlize(result_text)));
else expressionEdit->setExpression(QString::fromStdString(exact_text));
} else {
if(!execute_str.empty()) {
from_str = execute_str;
CALCULATOR->separateToExpression(from_str, str, settings->evalops, true, true);
}
expressionEdit->setPlainText(QString::fromStdString(from_str));
expressionEdit->setExpression(QString::fromStdString(from_str));
}
}
if(!expressionEdit->hasFocus()) expressionEdit->setFocus();
@ -3025,6 +3069,14 @@ void QalculateWindow::calculateExpression(bool force, bool do_mathoperation, Mat
expressionEdit->blockParseStatus(false);
expressionEdit->setExpressionHasChanged(false);
}
if(CALCULATOR->checkSaveFunctionCalled()) {
expressionEdit->updateCompletion();
if(variablesDialog) variablesDialog->updateVariables();
if(unitsDialog) unitsDialog->updateUnits();
if(functionsDialog) functionsDialog->updateFunctions();
}
}
void CommandThread::run() {
@ -3907,6 +3959,7 @@ void QalculateWindow::onStoreActivated() {
if(v) {
expressionEdit->updateCompletion();
if(variablesDialog) variablesDialog->updateVariables();
if(unitsDialog) unitsDialog->updateUnits();
}
}
void QalculateWindow::newVariable() {
@ -3914,6 +3967,15 @@ void QalculateWindow::newVariable() {
if(v) {
expressionEdit->updateCompletion();
if(variablesDialog) variablesDialog->updateVariables();
if(unitsDialog) unitsDialog->updateUnits();
}
}
void QalculateWindow::newUnknown() {
UnknownVariable *v = UnknownEditDialog::newVariable(this);
if(v) {
expressionEdit->updateCompletion();
if(variablesDialog) variablesDialog->updateVariables();
if(unitsDialog) unitsDialog->updateUnits();
}
}
void QalculateWindow::newFunction() {
@ -4113,8 +4175,23 @@ void QalculateWindow::assumptionsSignActivated() {
expressionCalculationUpdated();
}
void QalculateWindow::onAlwaysOnTopChanged() {
if(settings->always_on_top) setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
else setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
if(settings->always_on_top) {
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
if(functionsDialog) functionsDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
if(variablesDialog) variablesDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
if(unitsDialog) unitsDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
if(fpConversionDialog) fpConversionDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
if(calendarConversionDialog) calendarConversionDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
if(preferencesDialog) preferencesDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
} else {
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
if(functionsDialog) functionsDialog->setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
if(variablesDialog) variablesDialog->setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
if(unitsDialog) unitsDialog->setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
if(fpConversionDialog) fpConversionDialog->setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
if(calendarConversionDialog) calendarConversionDialog->setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
if(preferencesDialog) preferencesDialog->setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
}
show();
}
void QalculateWindow::onTitleTypeChanged() {
@ -4193,10 +4270,7 @@ void QalculateWindow::applyFunction(MathFunction *f) {
str += ")";
}
expressionEdit->blockParseStatus();
expressionEdit->blockUndo();
expressionEdit->clear();
expressionEdit->blockUndo(false);
expressionEdit->setPlainText(str);
expressionEdit->setExpression(str);
expressionEdit->blockParseStatus(false);
calculate();
}
@ -4213,8 +4287,13 @@ void QalculateWindow::openFunctions() {
connect(functionsDialog, SIGNAL(applyFunctionRequest(MathFunction*)), this, SLOT(applyFunction(MathFunction*)));
connect(functionsDialog, SIGNAL(insertFunctionRequest(MathFunction*)), this, SLOT(onInsertFunctionRequested(MathFunction*)));
connect(functionsDialog, SIGNAL(calculateFunctionRequest(MathFunction*)), this, SLOT(onCalculateFunctionRequested(MathFunction*)));
if(settings->always_on_top) functionsDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
functionsDialog->show();
}
void QalculateWindow::onUnitRemoved(Unit *u) {
if(unitsDialog) unitsDialog->unitRemoved(u);
expressionEdit->updateCompletion();
}
void QalculateWindow::openVariables() {
if(variablesDialog) {
variablesDialog->setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
@ -4225,9 +4304,15 @@ void QalculateWindow::openVariables() {
}
variablesDialog = new VariablesDialog();
connect(variablesDialog, SIGNAL(itemsChanged()), expressionEdit, SLOT(updateCompletion()));
connect(variablesDialog, SIGNAL(unitRemoved(Unit*)), this, SLOT(onUnitRemoved(Unit*)));
connect(variablesDialog, SIGNAL(insertVariableRequest(Variable*)), this, SLOT(onVariableClicked(Variable*)));
if(settings->always_on_top) variablesDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
variablesDialog->show();
}
void QalculateWindow::onVariableRemoved(Variable *v) {
if(variablesDialog) variablesDialog->variableRemoved(v);
expressionEdit->updateCompletion();
}
void QalculateWindow::openUnits() {
Unit *u = NULL;
if(!expressionEdit->expressionHasChanged() && !settings->history_answer.empty()) {
@ -4245,9 +4330,11 @@ void QalculateWindow::openUnits() {
unitsDialog = new UnitsDialog();
if(u && !u->category().empty()) unitsDialog->selectCategory(u->category());
connect(unitsDialog, SIGNAL(itemsChanged()), expressionEdit, SLOT(updateCompletion()));
connect(unitsDialog, SIGNAL(variableRemoved(Variable*)), this, SLOT(onVariableRemoved(Variable*)));
connect(unitsDialog, SIGNAL(insertUnitRequest(Unit*)), this, SLOT(onUnitClicked(Unit*)));
connect(unitsDialog, SIGNAL(convertToUnitRequest(Unit*)), this, SLOT(convertToUnit(Unit*)));
connect(unitsDialog, SIGNAL(unitActivated(Unit*)), this, SLOT(onUnitActivated(Unit*)));
if(settings->always_on_top) unitsDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
unitsDialog->show();
}
void QalculateWindow::onUnitActivated(Unit *u) {
@ -4291,6 +4378,24 @@ void QalculateWindow::openFPConversion() {
}
fpConversionDialog->show();
}
void QalculateWindow::openCalendarConversion() {
if(calendarConversionDialog) {
if(settings->always_on_top) calendarConversionDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
if(mstruct && mstruct->isDateTime()) calendarConversionDialog->setDate(*mstruct->datetime());
calendarConversionDialog->setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
calendarConversionDialog->show();
calendarConversionDialog->raise();
calendarConversionDialog->activateWindow();
return;
}
calendarConversionDialog = new CalendarConversionDialog(this);
if(settings->always_on_top) calendarConversionDialog->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
QalculateDateTime dt;
if(mstruct && mstruct->isDateTime()) dt.set(*mstruct->datetime());
else dt.setToCurrentDate();
calendarConversionDialog->setDate(dt);
calendarConversionDialog->show();
}
struct FunctionDialog {
MathFunction *f;
@ -5008,5 +5113,16 @@ void QalculateWindow::executeFromFile(const QString &file) {
void QalculateWindow::convertToUnit(Unit *u) {
executeCommand(COMMAND_CONVERT_UNIT, true, "", u);
}
void QalculateWindow::normalModeActivated() {
settings->rpn_mode = false;
settings->chain_mode = false;
}
void QalculateWindow::rpnModeActivated() {
settings->rpn_mode = true;
settings->chain_mode = false;
}
void QalculateWindow::chainModeActivated() {
settings->rpn_mode = false;
settings->chain_mode = true;
}

View File

@ -37,6 +37,7 @@ class FunctionsDialog;
class VariablesDialog;
class UnitsDialog;
class FPConversionDialog;
class CalendarConversionDialog;
struct FunctionDialog;
class QalculateWindow : public QMainWindow {
@ -68,6 +69,7 @@ class QalculateWindow : public QMainWindow {
VariablesDialog *variablesDialog;
UnitsDialog *unitsDialog;
FPConversionDialog *fpConversionDialog;
CalendarConversionDialog *calendarConversionDialog;
KeypadWidget *keypad;
QDockWidget *keypadDock, *basesDock;
@ -157,6 +159,7 @@ class QalculateWindow : public QMainWindow {
void approximationActivated();
void applyFunction(MathFunction*);
void openFPConversion();
void openCalendarConversion();
void onInsertFunctionExec();
void onInsertFunctionRPN();
void onInsertFunctionInsert();
@ -169,7 +172,11 @@ class QalculateWindow : public QMainWindow {
void onCalculateFunctionRequested(MathFunction*);
void onInsertFunctionRequested(MathFunction*);
void onUnitActivated(Unit *u);
void onUnitRemoved(Unit*);
void onVariableRemoved(Variable*);
void normalModeActivated();
void rpnModeActivated();
void chainModeActivated();
public slots:
@ -188,6 +195,7 @@ class QalculateWindow : public QMainWindow {
void expressionFormatUpdated(bool);
void insertFunction(MathFunction*, QWidget* = NULL);
void newVariable();
void newUnknown();
void newFunction();
void convertToUnit(Unit*);

660
src/unitsdialog.cpp Normal file
View File

@ -0,0 +1,660 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSplitter>
#include <QTextEdit>
#include <QStandardItemModel>
#include <QTreeView>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QPushButton>
#include <QLineEdit>
#include <QKeyEvent>
#include <QLabel>
#include <QComboBox>
#include <QDebug>
#include "qalculateqtsettings.h"
#include "unitsdialog.h"
#include "itemproxymodel.h"
//#include "uniteditdialog.h"
UnitsDialog::UnitsDialog(QWidget *parent) : QDialog(parent) {
last_from = true;
QVBoxLayout *topbox = new QVBoxLayout(this);
setWindowTitle(tr("Units"));
QHBoxLayout *hbox = new QHBoxLayout();
topbox->addLayout(hbox);
vsplitter = new QSplitter(Qt::Vertical, this);
hbox->addWidget(vsplitter, 1);
hsplitter = new QSplitter(Qt::Horizontal, this);
categoriesView = new QTreeWidget(this);
categoriesView->setSelectionMode(QAbstractItemView::SingleSelection);
categoriesView->setRootIsDecorated(false);
categoriesView->headerItem()->setText(0, tr("Category"));
categoriesView->setColumnCount(2);
categoriesView->setColumnHidden(1, true);
hsplitter->addWidget(categoriesView);
QWidget *w = new QWidget(this);
QVBoxLayout *vbox = new QVBoxLayout(w);
vbox->setSpacing(0);
vbox->setContentsMargins(0, 0, 0, 0);
unitsView = new QTreeView(this);
unitsView->setSelectionMode(QAbstractItemView::SingleSelection);
unitsView->setRootIsDecorated(false);
unitsModel = new ItemProxyModel(this);
sourceModel = new QStandardItemModel(this);
unitsModel->setSourceModel(sourceModel);
sourceModel->setColumnCount(1);
sourceModel->setHorizontalHeaderItem(0, new QStandardItem(tr("Unit")));
unitsView->setModel(unitsModel);
selected_item = NULL;
vbox->addWidget(unitsView, 1);
searchEdit = new QLineEdit(this);
searchEdit->addAction(LOAD_ICON("edit-find"), QLineEdit::LeadingPosition);
vbox->addWidget(searchEdit, 0);
hsplitter->addWidget(w);
vsplitter->addWidget(hsplitter);
descriptionView = new QTextEdit(this);
descriptionView->setReadOnly(true);
vsplitter->addWidget(descriptionView);
vsplitter->setStretchFactor(0, 3);
vsplitter->setStretchFactor(1, 1);
hsplitter->setStretchFactor(0, 2);
hsplitter->setStretchFactor(1, 3);
QVBoxLayout *box = new QVBoxLayout();
newButton = new QPushButton(tr("New"), this); box->addWidget(newButton); connect(newButton, SIGNAL(clicked()), this, SLOT(newClicked()));
newButton->setEnabled(false);
editButton = new QPushButton(tr("Edit"), this); box->addWidget(editButton); connect(editButton, SIGNAL(clicked()), this, SLOT(editClicked()));
delButton = new QPushButton(tr("Delete"), this); box->addWidget(delButton); connect(delButton, SIGNAL(clicked()), this, SLOT(delClicked()));
deactivateButton = new QPushButton(tr("Deactivate"), this); box->addWidget(deactivateButton); connect(deactivateButton, SIGNAL(clicked()), this, SLOT(deactivateClicked()));
box->addSpacing(24);
convertButton = new QPushButton(tr("Convert"), this); box->addWidget(convertButton); connect(convertButton, SIGNAL(clicked()), this, SLOT(convertClicked()));
insertButton = new QPushButton(tr("Insert"), this); box->addWidget(insertButton); connect(insertButton, SIGNAL(clicked()), this, SLOT(insertClicked()));
insertButton->setDefault(true);
box->addStretch(1);
hbox->addLayout(box, 0);
QGridLayout *grid = new QGridLayout();
equalsLabel = new QLabel("=", this);
equalsLabel->setAlignment(Qt::AlignRight);
QFontMetrics fm(equalsLabel->font());
equalsLabel->setMinimumWidth(fm.boundingRect(SIGN_ALMOST_EQUAL).width());
equalsLabel->setMinimumHeight(fm.boundingRect(SIGN_ALMOST_EQUAL).height());
grid->addWidget(equalsLabel, 1, 0);
fromEdit = new MathLineEdit(this);
fromEdit->setText("1");
fromEdit->setAlignment(Qt::AlignRight);
grid->addWidget(fromEdit, 0, 1);
toEdit = new MathLineEdit(this);
toEdit->setText("1");
toEdit->setAlignment(Qt::AlignRight);
grid->addWidget(toEdit, 1, 1);
fromLabel = new QLabel(this);
grid->addWidget(fromLabel, 0, 2);
toCombo = new QComboBox(this);
toModel = new ItemProxyModel(this);
toSourceModel = new QStandardItemModel(this);
toSourceModel->setColumnCount(1);
toModel->setSourceModel(toSourceModel);
toCombo->setModel(toModel);
grid->addWidget(toCombo, 1, 2);
topbox->addLayout(grid);
topbox->addSpacing(topbox->spacing() * 2);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, this);
topbox->addWidget(buttonBox);
selected_category = "All";
updateUnits();
unitsModel->setFilter("All");
toModel->setFilter("All");
connect(searchEdit, SIGNAL(textEdited(const QString&)), this, SLOT(searchChanged(const QString&)));
connect(fromEdit, SIGNAL(textEdited(const QString&)), this, SLOT(fromChanged()));
connect(toEdit, SIGNAL(textEdited(const QString&)), this, SLOT(toChanged()));
connect(toCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(toUnitChanged()));
connect(buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(reject()));
connect(categoriesView, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(selectedCategoryChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
connect(unitsView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(selectedUnitChanged(const QModelIndex&, const QModelIndex&)));
connect(unitsView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onUnitActivated(const QModelIndex&)));
selectedUnitChanged(QModelIndex(), QModelIndex());
if(!settings->units_geometry.isEmpty()) restoreGeometry(settings->units_geometry);
else resize(900, 800);
if(!settings->units_vsplitter_state.isEmpty()) vsplitter->restoreState(settings->units_vsplitter_state);
if(!settings->units_hsplitter_state.isEmpty()) hsplitter->restoreState(settings->units_hsplitter_state);
}
UnitsDialog::~UnitsDialog() {}
void UnitsDialog::convert(bool from) {
QModelIndex index = unitsView->selectionModel()->currentIndex();
if(!index.isValid() || toCombo->currentIndex() < 0) {toEdit->clear(); return;}
last_from = from;
Unit *from_u, *to_u;
std::string str;
if(from) {
str = fromEdit->text().trimmed().toStdString();
if(str == CALCULATOR->timedOutString()) return;
from_u = (Unit*) index.data(Qt::UserRole).value<void*>();
to_u = (Unit*) toCombo->currentData(Qt::UserRole).value<void*>();
if(from_u == to_u || str.empty()) {toEdit->setText(fromEdit->text().trimmed()); return;}
} else {
str = toEdit->text().trimmed().toStdString();
if(str == CALCULATOR->timedOutString()) return;
to_u = (Unit*) index.data(Qt::UserRole).value<void*>();
from_u = (Unit*) toCombo->currentData(Qt::UserRole).value<void*>();
if(from_u == to_u || str.empty()) {fromEdit->setText(toEdit->text().trimmed()); return;}
}
bool b = false;
EvaluationOptions eo;
eo.approximation = APPROXIMATION_APPROXIMATE;
eo.parse_options = settings->evalops.parse_options;
eo.parse_options.base = 10;
if(eo.parse_options.parsing_mode == PARSING_MODE_RPN || eo.parse_options.parsing_mode == PARSING_MODE_CHAIN) eo.parse_options.parsing_mode = PARSING_MODE_ADAPTIVE;
eo.parse_options.read_precision = DONT_READ_PRECISION;
PrintOptions po;
po.is_approximate = &b;
po.number_fraction_format = FRACTION_DECIMAL;
po.interval_display = INTERVAL_DISPLAY_SIGNIFICANT_DIGITS;
CALCULATOR->resetExchangeRatesUsed();
CALCULATOR->beginTemporaryStopMessages();
MathStructure v_mstruct = CALCULATOR->convert(CALCULATOR->unlocalizeExpression(str, eo.parse_options), from_u, to_u, 1500, eo);
if(!v_mstruct.isAborted() && settings->checkExchangeRates(this)) v_mstruct = CALCULATOR->convert(CALCULATOR->unlocalizeExpression(str, eo.parse_options), from_u, to_u, 1500, eo);
if(v_mstruct.isAborted()) {
str = CALCULATOR->timedOutString();
} else {
str = CALCULATOR->print(v_mstruct, 300, po);
}
if(from) toEdit->setText(QString::fromStdString(str));
else fromEdit->setText(QString::fromStdString(str));
b = b || v_mstruct.isApproximate();
if(b) equalsLabel->setText(SIGN_ALMOST_EQUAL);
else equalsLabel->setText("=");
CALCULATOR->endTemporaryStopMessages();
}
void UnitsDialog::fromChanged() {
convert(true);
}
void UnitsDialog::toChanged() {
convert(false);
}
void UnitsDialog::toUnitChanged() {
convert(last_from);
}
void UnitsDialog::fromUnitChanged() {
convert(last_from);
}
void UnitsDialog::keyPressEvent(QKeyEvent *event) {
if(event->matches(QKeySequence::Find)) {
searchEdit->setFocus();
return;
}
if(event->key() == Qt::Key_Escape && searchEdit->hasFocus()) {
searchEdit->clear();
unitsView->setFocus();
return;
}
if(event->key() == Qt::Key_Escape && (fromEdit->hasFocus() || toEdit->hasFocus())) {
fromEdit->clear();
toEdit->clear();
return;
}
if(event->key() == Qt::Key_Return && unitsView->hasFocus()) {
QModelIndex index = unitsView->selectionModel()->currentIndex();
if(index.isValid()) {
onUnitActivated(index);
return;
}
}
QDialog::keyPressEvent(event);
}
void UnitsDialog::onUnitActivated(const QModelIndex &index) {
if(!index.isValid()) return;
Unit *u = (Unit*) index.data(Qt::UserRole).value<void*>();
if(u) emit unitActivated(u);
}
void UnitsDialog::closeEvent(QCloseEvent *e) {
settings->units_geometry = saveGeometry();
settings->units_vsplitter_state = vsplitter->saveState();
settings->units_hsplitter_state = hsplitter->saveState();
QDialog::closeEvent(e);
}
void UnitsDialog::reject() {
settings->units_geometry = saveGeometry();
settings->units_vsplitter_state = vsplitter->saveState();
settings->units_hsplitter_state = hsplitter->saveState();
QDialog::reject();
}
void UnitsDialog::searchChanged(const QString &str) {
unitsModel->setSecondaryFilter(str.toStdString());
unitsView->selectionModel()->setCurrentIndex(unitsModel->index(0, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
void UnitsDialog::newClicked() {
/*Unit *u = UnitEditDialog::newUnit(this);
if(u) {
selected_item = u;
QStandardItem *item = new QStandardItem(QString::fromStdString(f->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) u), Qt::UserRole);
sourceModel->appendRow(item);
unitsModel->invalidate();
QModelIndex index = unitsModel->mapFromSource(item->index());
if(index.isValid()) {
unitsView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
unitsView->scrollTo(index);
}
emit itemsChanged();
}*/
}
void UnitsDialog::unitRemoved(Unit *u) {
QModelIndexList list = sourceModel->match(sourceModel->index(0, 0), Qt::UserRole, QVariant::fromValue((void*) u), 1, Qt::MatchExactly);
if(!list.isEmpty()) sourceModel->removeRow(list[0].row());
}
void UnitsDialog::editClicked() {
/*QModelIndex index = unitsView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Unit *u = (Unit*) index.data(Qt::UserRole).value<void*>();
if(u && UnitEditDialog::editUnit(this, u)) {
sourceModel->removeRow(unitsModel->mapToSource(unitsView->selectionModel()->currentIndex()).row());
QStandardItem *item = new QStandardItem(QString::fromStdString(f->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) u), Qt::UserRole);
sourceModel->appendRow(item);
selected_item = u;
unitsModel->invalidate();
QModelIndex index = unitsModel->mapFromSource(item->index());
if(index.isValid()) {
unitsView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
unitsView->scrollTo(index);
}
emit itemsChanged();
}*/
}
void UnitsDialog::delClicked() {
QModelIndex index = unitsView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Unit *u = (Unit*) index.data(Qt::UserRole).value<void*>();
if(u && u->isLocal()) {
sourceModel->removeRow(unitsModel->mapToSource(unitsView->selectionModel()->currentIndex()).row());
selected_item = NULL;
u->destroy();
emit itemsChanged();
}
}
void UnitsDialog::convertClicked() {
QModelIndex index = unitsView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Unit *u = (Unit*) index.data(Qt::UserRole).value<void*>();
if(u) {
emit convertToUnitRequest(u);
}
}
void UnitsDialog::insertClicked() {
QModelIndex index = unitsView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Unit *u = (Unit*) index.data(Qt::UserRole).value<void*>();
if(u) {
emit insertUnitRequest(u);
}
}
void UnitsDialog::deactivateClicked() {
QModelIndex index = unitsView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Unit *u = (Unit*) index.data(Qt::UserRole).value<void*>();
if(u) {
u->setActive(!u->isActive());
unitsModel->invalidate();
emit itemsChanged();
}
}
void UnitsDialog::selectedUnitChanged(const QModelIndex &index, const QModelIndex&) {
if(index.isValid()) {
Unit *u = (Unit*) index.data(Qt::UserRole).value<void*>();
if(CALCULATOR->stillHasUnit(u)) {
selected_item = u;
std::string str;
const ExpressionName *ename = &u->preferredName(false, settings->printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) descriptionView);
str = "<b>";
str += ename->name;
str += "</b>";
for(size_t i2 = 1; i2 <= u->countNames(); i2++) {
if(&u->getName(i2) != ename) {
str += ", ";
str += u->getName(i2).name;
}
}
str += "<br><br>";
bool is_approximate = false;
PrintOptions po = settings->printops;
po.can_display_unicode_string_arg = (void*) descriptionView;
po.is_approximate = &is_approximate;
po.allow_non_usable = true;
po.interval_display = INTERVAL_DISPLAY_PLUSMINUS;
po.base = 10;
po.number_fraction_format = FRACTION_DECIMAL_EXACT;
po.use_unit_prefixes = false;
if(u->subtype() == SUBTYPE_ALIAS_UNIT) {
AliasUnit *au = (AliasUnit*) u;
MathStructure m(1, 1, 0), mexp(1, 1, 0);
if(au->hasNonlinearExpression()) {
m.set("x");
if(au->expression().find("\\y") != std::string::npos) mexp.set("y");
str += "<i>x</i> ";
str += u->preferredDisplayName(settings->printops.abbreviate_names, settings->printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) descriptionView).name;
str += " ";
if(au->expression().find("\\y") != std::string::npos) str += "<sup><i>y</i></sup>";
}
au->convertToFirstBaseUnit(m, mexp);
m.multiply(au->firstBaseUnit());
if(!mexp.isOne()) m.last() ^= mexp;
if(m.isApproximate() || is_approximate) str += SIGN_ALMOST_EQUAL " ";
else str += "= ";
m.format(po);
str += m.print(po, true, false, TAG_TYPE_HTML);
if(au->hasNonlinearExpression() && !au->inverseExpression().empty()) {
str += "<br>";
m.set("x");
if(au->inverseExpression().find("\\y") != std::string::npos) mexp.set("y");
else mexp.set(1, 1, 0);
str += "<i>x</i> ";
str += au->firstBaseUnit()->preferredDisplayName(settings->printops.abbreviate_names, settings->printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) descriptionView).name;
str += " ";
if(au->inverseExpression().find("\\y") != std::string::npos) str += "<sup><i>y</i></sup>";
au->convertFromFirstBaseUnit(m, mexp);
m.multiply(au);
if(!mexp.isOne()) m.last() ^= mexp;
if(m.isApproximate() || is_approximate) str += SIGN_ALMOST_EQUAL " ";
else str += "= ";
m.format(po);
str += m.print(po, true, false, TAG_TYPE_HTML);
}
} else if(u->subtype() == SUBTYPE_COMPOSITE_UNIT) {
str += "= ";
MathStructure m(((CompositeUnit*) u)->generateMathStructure());
m.format(po);
str += m.print(po, true, false, TAG_TYPE_HTML);
}
if(!u->description().empty()) {
if(u->subtype() != SUBTYPE_BASE_UNIT) str += "<br>";
str += "<br>";
str += u->description();
}
if(u->isActive() != (deactivateButton->text() == tr("Deactivate"))) {
deactivateButton->setMinimumWidth(deactivateButton->width());
if(u->isActive()) {
deactivateButton->setText(tr("Deactivate"));
} else {
deactivateButton->setText(tr("Activate"));
}
}
//editButton->setEnabled(!u->isBuiltin());
insertButton->setEnabled(u->isActive());
convertButton->setEnabled(u->isActive());
delButton->setEnabled(u->isLocal());
deactivateButton->setEnabled(true);
descriptionView->setHtml(QString::fromStdString(str));
std::string to_filter;
if(u->category().empty()) {
if(u->isLocal()) to_filter = "Uncategorized";
else to_filter = "User items";
} else {
to_filter = "/"; to_filter += u->category();
}
bool change_index = false;
if(u->baseUnit()->referenceName() == "m" && u->baseExponent() == 3) {
size_t i = to_filter.find("/", 1);
if(i != std::string::npos) to_filter = to_filter.substr(0, i);
}
if(to_filter != toModel->currentFilter()) {
toModel->setFilter(to_filter);
toModel->sort(0);
change_index = toCombo->count() > 0;
} else if(toCombo->currentIndex() >= 0 && toCombo->currentData(Qt::UserRole).value<void*>() == u) {
change_index = true;
}
if(change_index) {
if(toCombo->itemData(0, Qt::UserRole).value<void*>() == u && toCombo->count() >= 2) toCombo->setCurrentIndex(1);
else toCombo->setCurrentIndex(0);
}
fromEdit->setEnabled(u->isActive());
toEdit->setEnabled(u->isActive());
toCombo->setEnabled(u->isActive());
QString qstr;
if(u->isCurrency()) qstr = QString::fromStdString(u->referenceName());
else qstr = QString::fromStdString(u->print(true, true, settings->printops.use_unicode_signs, &can_display_unicode_string_function, (void*) fromLabel));
if(u->subtype() == SUBTYPE_COMPOSITE_UNIT) {
qstr.replace("^-1", "" SIGN_POWER_1);
qstr.replace("^-2", "" SIGN_POWER_2);
qstr.replace("^-3", "" SIGN_POWER_3);
qstr.replace("^-4", "" SIGN_POWER_4);
qstr.replace("^4", SIGN_POWER_4);
qstr.remove("_unit");
}
fromLabel->setText(qstr);
fromUnitChanged();
return;
}
}
fromUnitChanged();
fromEdit->setEnabled(false);
toEdit->setEnabled(false);
toCombo->setEnabled(false);
fromLabel->setText(QString());
editButton->setEnabled(false);
insertButton->setEnabled(false);
convertButton->setEnabled(false);
delButton->setEnabled(false);
deactivateButton->setEnabled(false);
descriptionView->clear();
selected_item = NULL;
}
void UnitsDialog::selectedCategoryChanged(QTreeWidgetItem *iter, QTreeWidgetItem*) {
if(!iter) selected_category = "";
else selected_category = iter->text(1).toStdString();
searchEdit->clear();
unitsModel->setFilter(selected_category);
QModelIndex index = unitsView->selectionModel()->currentIndex();
if(index.isValid()) {
selectedUnitChanged(index, QModelIndex());
unitsView->scrollTo(index);
} else if(selected_category != "All" && selected_category != "Inactive" && selected_category != toModel->currentFilter()) {
toModel->setFilter(selected_category);
toModel->sort(0);
if(toCombo->count()) toCombo->setCurrentIndex(0);
}
}
struct tree_struct {
std::string item;
std::list<tree_struct> items;
std::list<tree_struct>::iterator it;
std::list<tree_struct>::reverse_iterator rit;
tree_struct *parent;
void sort() {
items.sort();
for(std::list<tree_struct>::iterator it = items.begin(); it != items.end(); ++it) {
it->sort();
}
}
bool operator < (const tree_struct &s1) const {
return string_is_less(item, s1.item);
}
};
void UnitsDialog::updateUnits() {
size_t cat_i, cat_i_prev;
bool b;
std::string str, cat, cat_sub;
QString qstr;
tree_struct unit_cats;
unit_cats.parent = NULL;
bool has_inactive = false, has_uncat = false;
std::list<tree_struct>::iterator it;
sourceModel->clear();
sourceModel->setColumnCount(1);
sourceModel->setHorizontalHeaderItem(0, new QStandardItem(tr("Unit")));
toSourceModel->clear();
toSourceModel->setColumnCount(1);
for(size_t i = 0; i < CALCULATOR->units.size(); i++) {
Unit *u = CALCULATOR->units[i];
if(!u->isActive()) {
has_inactive = true;
} else {
tree_struct *item = &unit_cats;
if(!u->category().empty()) {
cat = u->category();
cat_i = cat.find("/"); cat_i_prev = 0;
b = false;
while(true) {
if(cat_i == std::string::npos) {
cat_sub = cat.substr(cat_i_prev, cat.length() - cat_i_prev);
} else {
cat_sub = cat.substr(cat_i_prev, cat_i - cat_i_prev);
}
b = false;
for(it = item->items.begin(); it != item->items.end(); ++it) {
if(cat_sub == it->item) {
item = &*it;
b = true;
break;
}
}
if(!b) {
tree_struct cat;
item->items.push_back(cat);
it = item->items.end();
--it;
it->parent = item;
item = &*it;
item->item = cat_sub;
}
if(cat_i == std::string::npos) {
break;
}
cat_i_prev = cat_i + 1;
cat_i = cat.find("/", cat_i_prev);
}
} else if(!u->isLocal()) {
has_uncat = true;
}
}
QStandardItem *item = new QStandardItem(QString::fromStdString(u->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) u), Qt::UserRole);
sourceModel->appendRow(item);
if(u == selected_item) unitsView->selectionModel()->setCurrentIndex(unitsModel->mapFromSource(item->index()), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
if(u->isCurrency()) qstr = QString::fromStdString(u->referenceName());
else qstr = QString::fromStdString(u->print(true, true, settings->printops.use_unicode_signs, &can_display_unicode_string_function, (void*) toCombo));
if(u->subtype() == SUBTYPE_COMPOSITE_UNIT) {
qstr.replace("^-1", "" SIGN_POWER_1);
qstr.replace("^-2", "" SIGN_POWER_2);
qstr.replace("^-3", "" SIGN_POWER_3);
qstr.replace("^-4", "" SIGN_POWER_4);
qstr.replace("^4", SIGN_POWER_4);
qstr.remove("_unit");
}
item = new QStandardItem(qstr);
item->setEditable(false);
item->setData(QVariant::fromValue((void*) u), Qt::UserRole);
toSourceModel->appendRow(item);
}
sourceModel->sort(0);
toSourceModel->sort(0);
unit_cats.sort();
categoriesView->clear();
QTreeWidgetItem *iter, *iter2, *iter3;
QStringList l;
l << tr("All", "All units"); l << "All";
iter3 = new QTreeWidgetItem(categoriesView, l);
tree_struct *item, *item2;
unit_cats.it = unit_cats.items.begin();
if(unit_cats.it != unit_cats.items.end()) {
item = &*unit_cats.it;
++unit_cats.it;
item->it = item->items.begin();
} else {
item = NULL;
}
str = "";
iter2 = iter3;
while(item) {
str += "/";
str += item->item;
l.clear(); l << QString::fromStdString(item->item); l << QString::fromStdString(str);
iter = new QTreeWidgetItem(iter2, l);
if(str == selected_category) {
iter->setExpanded(true);
iter->setSelected(true);
}
while(item && item->it == item->items.end()) {
size_t str_i = str.rfind("/");
if(str_i == std::string::npos) {
str = "";
} else {
str = str.substr(0, str_i);
}
item = item->parent;
iter2 = iter->parent();
iter = iter2;
}
if(item) {
item2 = &*item->it;
if(item->it == item->items.begin()) iter2 = iter;
++item->it;
item = item2;
item->it = item->items.begin();
}
}
if(has_uncat) {
//add "Uncategorized" category if there are units without category
l.clear(); l << tr("Uncategorized"); l << "Uncategorized";
iter = new QTreeWidgetItem(iter3, l);
if(selected_category == "Uncategorized") {
iter->setSelected(true);
}
}
l.clear(); l << tr("User units"); l << "User items";
iter = new QTreeWidgetItem(iter3, l);
if(selected_category == "User items") {
iter->setSelected(true);
}
if(has_inactive) {
//add "Inactive" category if there are inactive units
l.clear(); l << tr("Inactive"); l << "Inactive";
iter = new QTreeWidgetItem(categoriesView, l);
if(selected_category == "Inactive") {
iter->setSelected(true);
}
}
if(categoriesView->selectedItems().isEmpty()) {
//if no category has been selected (previously selected has been renamed/deleted), select "All"
selected_category = "All";
iter3->setExpanded(true);
iter3->setSelected(true);
}
}
void UnitsDialog::setSearch(const QString &str) {
searchEdit->setText(str);
searchChanged(str);
}
void UnitsDialog::selectCategory(std::string str) {
QList<QTreeWidgetItem*> list = categoriesView->findItems((str.empty() || str == "All") ? "All" : "/" + QString::fromStdString(str), Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
}

99
src/unitsdialog.h Normal file
View File

@ -0,0 +1,99 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#ifndef UNITS_DIALOG_H
#define UNITS_DIALOG_H
#include <QDialog>
#include <libqalculate/qalculate.h>
class QTreeView;
class QTreeWidget;
class QTextEdit;
class QTreeWidgetItem;
class QStandardItemModel;
class QPushButton;
class QLineEdit;
class QSplitter;
class ItemProxyModel;
class QComboBox;
class QLabel;
class UnitsDialog : public QDialog {
Q_OBJECT
protected:
QTreeView *unitsView;
QTreeWidget *categoriesView;
QTextEdit *descriptionView;
ItemProxyModel *unitsModel, *toModel;
QStandardItemModel *sourceModel, *toSourceModel;
QPushButton *deactivateButton, *insertButton, *delButton, *editButton, *newButton, *convertButton;
QLineEdit *searchEdit, *fromEdit, *toEdit;
QSplitter *vsplitter, *hsplitter;
QLabel *fromLabel, *equalsLabel;
QComboBox *toCombo;
bool last_from;
std::string selected_category;
ExpressionItem *selected_item;
void keyPressEvent(QKeyEvent *event) override;
void closeEvent(QCloseEvent*) override;
void convert(bool from);
protected slots:
void selectedCategoryChanged(QTreeWidgetItem*, QTreeWidgetItem*);
void selectedUnitChanged(const QModelIndex&, const QModelIndex&);
void newClicked();
void editClicked();
void delClicked();
void insertClicked();
void convertClicked();
void deactivateClicked();
void searchChanged(const QString&);
void fromChanged();
void toChanged();
void toUnitChanged();
void fromUnitChanged();
void onUnitActivated(const QModelIndex&);
public:
UnitsDialog(QWidget *parent = NULL);
virtual ~UnitsDialog();
void updateUnits();
void setSearch(const QString&);
void selectCategory(std::string);
void unitRemoved(Unit*);
public slots:
void reject() override;
signals:
void itemsChanged();
void variableRemoved(Variable*);
void insertUnitRequest(Unit*);
void convertToUnitRequest(Unit*);
void unitActivated(Unit*);
};
#endif //UNITS_DIALOG_H

198
src/unknowneditdialog.cpp Normal file
View File

@ -0,0 +1,198 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include <QLineEdit>
#include <QCheckBox>
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QGridLayout>
#include <QLabel>
#include <QComboBox>
#include <QVBoxLayout>
#include <QDebug>
#include "qalculateqtsettings.h"
#include "unknowneditdialog.h"
UnknownEditDialog::UnknownEditDialog(QWidget *parent) : QDialog(parent) {
QVBoxLayout *box = new QVBoxLayout(this);
QGridLayout *grid = new QGridLayout();
box->addLayout(grid);
grid->addWidget(new QLabel(tr("Name:"), this), 0, 0);
nameEdit = new QLineEdit(this);
grid->addWidget(nameEdit, 0, 1);
customBox = new QCheckBox(tr("Custom assumptions"), this);
customBox->setChecked(true);
grid->addWidget(customBox, 1, 0, 1, 2);
grid->addWidget(new QLabel(tr("Type:"), this), 2, 0);
typeCombo = new QComboBox(this);
typeCombo->addItem("Number", ASSUMPTION_TYPE_NUMBER);
typeCombo->addItem("Real number", ASSUMPTION_TYPE_REAL);
typeCombo->addItem("Rational number", ASSUMPTION_TYPE_RATIONAL);
typeCombo->addItem("Integer", ASSUMPTION_TYPE_INTEGER);
typeCombo->addItem("Boolean", ASSUMPTION_TYPE_BOOLEAN);
grid->addWidget(typeCombo, 2, 1);
grid->addWidget(new QLabel(tr("Sign:"), this), 3, 0);
signCombo = new QComboBox(this);
signCombo->addItem("Unknown", ASSUMPTION_SIGN_UNKNOWN);
signCombo->addItem("Non-zero", ASSUMPTION_SIGN_NONZERO);
signCombo->addItem("Positive", ASSUMPTION_SIGN_POSITIVE);
signCombo->addItem("Non-negative", ASSUMPTION_SIGN_NONNEGATIVE);
signCombo->addItem("Negative", ASSUMPTION_SIGN_NEGATIVE);
signCombo->addItem("Non-positive", ASSUMPTION_SIGN_NONPOSITIVE);
grid->addWidget(signCombo, 3, 1);
typeCombo->setCurrentIndex(typeCombo->findData(CALCULATOR->defaultAssumptions()->type()));
signCombo->setCurrentIndex(signCombo->findData(CALCULATOR->defaultAssumptions()->sign()));
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
okButton = buttonBox->button(QDialogButtonBox::Ok);
box->addWidget(buttonBox);
connect(nameEdit, SIGNAL(textEdited(const QString&)), this, SLOT(onNameEdited(const QString&)));
connect(typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onTypeChanged(int)));
connect(signCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onSignChanged(int)));
connect(customBox, SIGNAL(toggled(bool)), this, SLOT(onCustomToggled(bool)));
connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject()));
connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
okButton->setEnabled(false);
if(settings->always_on_top) setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
}
UnknownEditDialog::~UnknownEditDialog() {}
void UnknownEditDialog::onTypeChanged(int i) {
int t = typeCombo->itemData(i).toInt();
int s = signCombo->currentData().toInt();
if((t == ASSUMPTION_TYPE_NUMBER && s != ASSUMPTION_SIGN_NONZERO && s != ASSUMPTION_SIGN_UNKNOWN) || (t == ASSUMPTION_TYPE_BOOLEAN && s != ASSUMPTION_SIGN_UNKNOWN)) {
signCombo->blockSignals(true);
signCombo->setCurrentIndex(signCombo->findData(ASSUMPTION_SIGN_UNKNOWN));
signCombo->blockSignals(false);
}
}
void UnknownEditDialog::onSignChanged(int i) {
int t = typeCombo->currentData().toInt();
int s = signCombo->itemData(i).toInt();
if((t == ASSUMPTION_TYPE_NUMBER && s != ASSUMPTION_SIGN_NONZERO && s != ASSUMPTION_SIGN_UNKNOWN) || (t == ASSUMPTION_TYPE_BOOLEAN && s != ASSUMPTION_SIGN_UNKNOWN)) {
typeCombo->blockSignals(true);
typeCombo->setCurrentIndex(typeCombo->findData(ASSUMPTION_TYPE_REAL));
typeCombo->blockSignals(false);
}
}
void UnknownEditDialog::onCustomToggled(bool b) {
typeCombo->setEnabled(b);
signCombo->setEnabled(b);
}
UnknownVariable *UnknownEditDialog::createVariable(ExpressionItem **replaced_item) {
if(replaced_item) *replaced_item = NULL;
Variable *var = NULL;
if(CALCULATOR->variableNameTaken(nameEdit->text().trimmed().toStdString())) {
if(QMessageBox::question(this, tr("Question"), tr("An unit or variable with the same name already exists.\nDo you want to overwrite it?")) != QMessageBox::Yes) {
nameEdit->setFocus();
return NULL;
}
if(replaced_item) {
var = CALCULATOR->getActiveVariable(nameEdit->text().trimmed().toStdString());
if(!var) *replaced_item = CALCULATOR->getActiveUnit(nameEdit->text().trimmed().toStdString());
else *replaced_item = var;
}
}
UnknownVariable *v;
if(var && var->isLocal() && !var->isKnown()) {
v = (UnknownVariable*) var;
if(v->countNames() > 1) v->clearNames();
v->setHidden(false); v->setApproximate(false); v->setDescription(""); v->setTitle("");
if(!modifyVariable(v)) return NULL;
return v;
}
v = new UnknownVariable("", nameEdit->text().trimmed().toStdString());
if(customBox->isChecked()) {
v->setAssumptions(new Assumptions());
v->assumptions()->setType((AssumptionType) typeCombo->currentData().toInt());
v->assumptions()->setSign((AssumptionSign) signCombo->currentData().toInt());
}
v->setCategory(CALCULATOR->getVariableById(VARIABLE_ID_X)->category());
v->setChanged(false);
CALCULATOR->addVariable(v);
return v;
}
bool UnknownEditDialog::modifyVariable(UnknownVariable *v, ExpressionItem **replaced_item) {
if(replaced_item) *replaced_item = NULL;
if(CALCULATOR->variableNameTaken(nameEdit->text().trimmed().toStdString(), v)) {
if(QMessageBox::question(this, tr("Question"), tr("An unit or variable with the same name already exists.\nDo you want to overwrite it?")) != QMessageBox::Yes) {
nameEdit->setFocus();
return false;
}
if(replaced_item) {
Variable *var = CALCULATOR->getActiveVariable(nameEdit->text().trimmed().toStdString());
if(!var) *replaced_item = CALCULATOR->getActiveUnit(nameEdit->text().trimmed().toStdString());
else if(var != v) *replaced_item = var;
}
}
if(v->countNames() > 1 && v->getName(1).name != nameEdit->text().trimmed().toStdString()) v->clearNames();
v->setName(nameEdit->text().trimmed().toStdString());
if(!customBox->isChecked()) {
v->setAssumptions(NULL);
} else {
if(!v->assumptions()) v->setAssumptions(new Assumptions());
v->assumptions()->setType((AssumptionType) typeCombo->currentData().toInt());
v->assumptions()->setSign((AssumptionSign) signCombo->currentData().toInt());
}
return true;
}
void UnknownEditDialog::setVariable(UnknownVariable *v) {
nameEdit->setText(QString::fromStdString(v->getName(1).name));
Assumptions *ass = v->assumptions();
customBox->setChecked(ass);
if(!ass) ass = CALCULATOR->defaultAssumptions();
typeCombo->setCurrentIndex(typeCombo->findData(ass->type()));
signCombo->setCurrentIndex(signCombo->findData(ass->sign()));
okButton->setEnabled(true);
}
void UnknownEditDialog::onNameEdited(const QString &str) {
okButton->setEnabled(!str.trimmed().isEmpty());
if(!str.trimmed().isEmpty() && !CALCULATOR->variableNameIsValid(str.trimmed().toStdString())) {
nameEdit->setText(QString::fromStdString(CALCULATOR->convertToValidVariableName(str.trimmed().toStdString())));
}
}
void UnknownEditDialog::setName(const QString &str) {
nameEdit->setText(str);
onNameEdited(str);
}
bool UnknownEditDialog::editVariable(QWidget *parent, UnknownVariable *v, ExpressionItem **replaced_item) {
UnknownEditDialog *d = new UnknownEditDialog(parent);
d->setWindowTitle(tr("Edit Unknown Variable"));
d->setVariable(v);
while(d->exec() == QDialog::Accepted) {
if(d->modifyVariable(v, replaced_item)) {
d->deleteLater();
return true;
}
}
d->deleteLater();
return false;
}
UnknownVariable* UnknownEditDialog::newVariable(QWidget *parent, ExpressionItem **replaced_item) {
UnknownEditDialog *d = new UnknownEditDialog(parent);
d->setWindowTitle(tr("New Unknown Variable"));
std::string v_name;
int i = 1;
do {
v_name = "v"; v_name += i2s(i);
i++;
} while(CALCULATOR->nameTaken(v_name));
d->setName(QString::fromStdString(v_name));
UnknownVariable *v = NULL;
while(d->exec() == QDialog::Accepted) {
v = d->createVariable(replaced_item);
if(v) break;
}
d->deleteLater();
return v;
}

57
src/unknowneditdialog.h Normal file
View File

@ -0,0 +1,57 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#ifndef UNKNOWN_EDIT_DIALOG_H
#define UNKNOWN_EDIT_DIALOG_H
#include <QDialog>
#include <libqalculate/qalculate.h>
class QLineEdit;
class QCheckBox;
class QPushButton;
class QComboBox;
class UnknownEditDialog : public QDialog {
Q_OBJECT
protected:
QLineEdit *nameEdit;
QCheckBox *customBox;
QComboBox *typeCombo, *signCombo;
QPushButton *okButton;
protected slots:
void onNameEdited(const QString&);
void onTypeChanged(int);
void onSignChanged(int);
void onCustomToggled(bool);
public:
UnknownEditDialog(QWidget *parent = NULL);
virtual ~UnknownEditDialog();
UnknownVariable *createVariable(ExpressionItem **replaced_item = NULL);
bool modifyVariable(UnknownVariable *v, ExpressionItem **replaced_item = NULL);
void setVariable(UnknownVariable *v);
void setName(const QString&);
static bool editVariable(QWidget *parent, UnknownVariable *, ExpressionItem **replaced_item = NULL);
static UnknownVariable* newVariable(QWidget *parent, ExpressionItem **replaced_item = NULL);
};
#endif //UNKNOWN_EDIT_DIALOG_H

View File

@ -16,18 +16,21 @@
#include <QPushButton>
#include <QGridLayout>
#include <QLabel>
#include <QVBoxLayout>
#include <QDebug>
#include "qalculateqtsettings.h"
#include "variableeditdialog.h"
VariableEditDialog::VariableEditDialog(QWidget *parent, bool allow_empty_value) : QDialog(parent), b_empty(allow_empty_value) {
QGridLayout *grid = new QGridLayout(this);
QVBoxLayout *box = new QVBoxLayout(this);
QGridLayout *grid = new QGridLayout();
box->addLayout(grid);
grid->addWidget(new QLabel(tr("Name:"), this), 0, 0);
nameEdit = new QLineEdit(this);
grid->addWidget(nameEdit, 0, 1);
grid->addWidget(new QLabel(tr("Value:"), this), 1, 0);
valueEdit = new QLineEdit(this);
valueEdit = new MathLineEdit(this);
valueEdit->setAlignment(Qt::AlignRight);
if(b_empty) valueEdit->setPlaceholderText(tr("current result"));
grid->addWidget(valueEdit, 1, 1);
@ -36,7 +39,7 @@ VariableEditDialog::VariableEditDialog(QWidget *parent, bool allow_empty_value)
grid->addWidget(temporaryBox, 2, 0, 1, 2, Qt::AlignRight);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
okButton = buttonBox->button(QDialogButtonBox::Ok);
grid->addWidget(buttonBox, 3, 0, 1, 2);
box->addWidget(buttonBox);
connect(nameEdit, SIGNAL(textEdited(const QString&)), this, SLOT(onNameEdited(const QString&)));
if(!b_empty) connect(valueEdit, SIGNAL(textEdited(const QString&)), this, SLOT(onValueEdited(const QString&)));
connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject()));
@ -46,17 +49,25 @@ VariableEditDialog::VariableEditDialog(QWidget *parent, bool allow_empty_value)
}
VariableEditDialog::~VariableEditDialog() {}
KnownVariable *VariableEditDialog::createVariable(MathStructure *default_value) {
KnownVariable *VariableEditDialog::createVariable(MathStructure *default_value, ExpressionItem **replaced_item) {
if(replaced_item) *replaced_item = NULL;
Variable *var = NULL;
if(CALCULATOR->variableNameTaken(nameEdit->text().trimmed().toStdString())) {
if(QMessageBox::question(this, tr("Question"), tr("An unit or variable with the same name already exists.\nDo you want to overwrite it?")) != QMessageBox::Yes) {
nameEdit->setFocus();
return NULL;
}
if(replaced_item) {
var = CALCULATOR->getActiveVariable(nameEdit->text().trimmed().toStdString());
if(!var) *replaced_item = CALCULATOR->getActiveUnit(nameEdit->text().trimmed().toStdString());
else *replaced_item = var;
}
}
KnownVariable *v;
Variable *var = CALCULATOR->getActiveVariable(nameEdit->text().trimmed().toStdString());
if(var && var->isLocal() && var->isKnown()) {
v = (KnownVariable*) var;
if(v->countNames() > 1) v->clearNames();
v->setHidden(false); v->setDescription(""); v->setTitle("");
if(!modifyVariable(v, default_value)) return NULL;
return v;
}
@ -71,15 +82,22 @@ KnownVariable *VariableEditDialog::createVariable(MathStructure *default_value)
CALCULATOR->addVariable(v);
return v;
}
bool VariableEditDialog::modifyVariable(KnownVariable *v, MathStructure *default_value) {
bool VariableEditDialog::modifyVariable(KnownVariable *v, MathStructure *default_value, ExpressionItem **replaced_item) {
if(replaced_item) *replaced_item = NULL;
if(CALCULATOR->variableNameTaken(nameEdit->text().trimmed().toStdString(), v)) {
if(QMessageBox::question(this, tr("Question"), tr("An unit or variable with the same name already exists.\nDo you want to overwrite it?")) != QMessageBox::Yes) {
nameEdit->setFocus();
return false;
}
if(replaced_item) {
Variable *var = CALCULATOR->getActiveVariable(nameEdit->text().trimmed().toStdString());
if(!var) *replaced_item = CALCULATOR->getActiveUnit(nameEdit->text().trimmed().toStdString());
else if(var != v) *replaced_item = var;
}
}
if(v->countNames() > 1) v->clearNames();
if(v->countNames() > 1 && v->getName(1).name != nameEdit->text().trimmed().toStdString()) v->clearNames();
v->setName(nameEdit->text().trimmed().toStdString());
v->setApproximate(false); v->setUncertainty(""); v->setUnit("");
if(default_value && valueEdit->text().isEmpty()) {
v->set(*default_value);
} else {
@ -102,7 +120,10 @@ void VariableEditDialog::setVariable(KnownVariable *v) {
po.base = 10;
valueEdit->setText(QString::fromStdString(CALCULATOR->print(v->get(), 1000, po)));
}
okButton->setEnabled(true);
nameEdit->setReadOnly(!v->isLocal());
valueEdit->setReadOnly(!v->isLocal());
temporaryBox->setChecked(v->category() == CALCULATOR->temporaryCategory());
okButton->setEnabled(v->isLocal());
}
void VariableEditDialog::onNameEdited(const QString &str) {
okButton->setEnabled(!str.trimmed().isEmpty() && (b_empty || !valueEdit->text().trimmed().isEmpty()));
@ -124,12 +145,12 @@ void VariableEditDialog::setName(const QString &str) {
QString VariableEditDialog::value() const {
return valueEdit->text();
}
bool VariableEditDialog::editVariable(QWidget *parent, KnownVariable *v, MathStructure *default_value) {
bool VariableEditDialog::editVariable(QWidget *parent, KnownVariable *v, MathStructure *default_value, ExpressionItem **replaced_item) {
VariableEditDialog *d = new VariableEditDialog(parent, default_value != NULL);
d->setWindowTitle(tr("Edit Variable"));
d->setVariable(v);
while(d->exec() == QDialog::Accepted) {
if(d->modifyVariable(v, default_value)) {
if(d->modifyVariable(v, default_value, replaced_item)) {
d->deleteLater();
return true;
}
@ -137,7 +158,7 @@ bool VariableEditDialog::editVariable(QWidget *parent, KnownVariable *v, MathStr
d->deleteLater();
return false;
}
KnownVariable* VariableEditDialog::newVariable(QWidget *parent, MathStructure *default_value, const QString &value_str) {
KnownVariable* VariableEditDialog::newVariable(QWidget *parent, MathStructure *default_value, const QString &value_str, ExpressionItem **replaced_item) {
VariableEditDialog *d = new VariableEditDialog(parent, default_value != NULL && value_str.isEmpty());
d->setWindowTitle(tr("New Variable"));
std::string v_name;
@ -152,7 +173,7 @@ KnownVariable* VariableEditDialog::newVariable(QWidget *parent, MathStructure *d
while(d->exec() == QDialog::Accepted) {
QString str = d->value().trimmed();
if(default_value && str == value_str) d->setValue("");
v = d->createVariable(default_value);
v = d->createVariable(default_value, replaced_item);
if(v) break;
d->setValue(str);
}

View File

@ -41,15 +41,15 @@ class VariableEditDialog : public QDialog {
VariableEditDialog(QWidget *parent = NULL, bool allow_empty_value = true);
virtual ~VariableEditDialog();
KnownVariable *createVariable(MathStructure *default_value = NULL);
bool modifyVariable(KnownVariable *v, MathStructure *default_value = NULL);
KnownVariable *createVariable(MathStructure *default_value = NULL, ExpressionItem **replaced_item = NULL);
bool modifyVariable(KnownVariable *v, MathStructure *default_value = NULL, ExpressionItem **replaced_item = NULL);
void setVariable(KnownVariable *v);
void setValue(const QString&);
QString value() const;
void setName(const QString&);
static bool editVariable(QWidget *parent, KnownVariable *v, MathStructure *default_value = NULL);
static KnownVariable* newVariable(QWidget *parent, MathStructure *default_value = NULL, const QString &value_str = QString());
static bool editVariable(QWidget *parent, KnownVariable *v, MathStructure *default_value = NULL, ExpressionItem **replaced_item = NULL);
static KnownVariable* newVariable(QWidget *parent, MathStructure *default_value = NULL, const QString &value_str = QString(), ExpressionItem **replaced_item = NULL);
};

556
src/variablesdialog.cpp Normal file
View File

@ -0,0 +1,556 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSplitter>
#include <QTextEdit>
#include <QStandardItemModel>
#include <QTreeView>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QPushButton>
#include <QLineEdit>
#include <QKeyEvent>
#include <QMenu>
#include <QDebug>
#include "qalculateqtsettings.h"
#include "variablesdialog.h"
#include "itemproxymodel.h"
#include "unknowneditdialog.h"
#include "variableeditdialog.h"
VariablesDialog::VariablesDialog(QWidget *parent) : QDialog(parent) {
QVBoxLayout *topbox = new QVBoxLayout(this);
setWindowTitle(tr("Variables"));
QHBoxLayout *hbox = new QHBoxLayout();
topbox->addLayout(hbox);
vsplitter = new QSplitter(Qt::Vertical, this);
hbox->addWidget(vsplitter, 1);
hsplitter = new QSplitter(Qt::Horizontal, this);
categoriesView = new QTreeWidget(this);
categoriesView->setSelectionMode(QAbstractItemView::SingleSelection);
categoriesView->setRootIsDecorated(false);
categoriesView->headerItem()->setText(0, tr("Category"));
categoriesView->setColumnCount(2);
categoriesView->setColumnHidden(1, true);
hsplitter->addWidget(categoriesView);
QWidget *w = new QWidget(this);
QVBoxLayout *vbox = new QVBoxLayout(w);
vbox->setSpacing(0);
vbox->setContentsMargins(0, 0, 0, 0);
variablesView = new QTreeView(this);
variablesView->setSelectionMode(QAbstractItemView::SingleSelection);
variablesView->setRootIsDecorated(false);
variablesModel = new ItemProxyModel(this);
sourceModel = new QStandardItemModel(this);
variablesModel->setSourceModel(sourceModel);
sourceModel->setColumnCount(1);
sourceModel->setHorizontalHeaderItem(0, new QStandardItem(tr("Variable")));
variablesView->setModel(variablesModel);
selected_item = NULL;
vbox->addWidget(variablesView, 1);
searchEdit = new QLineEdit(this);
searchEdit->addAction(LOAD_ICON("edit-find"), QLineEdit::LeadingPosition);
vbox->addWidget(searchEdit, 0);
hsplitter->addWidget(w);
vsplitter->addWidget(hsplitter);
descriptionView = new QTextEdit(this);
descriptionView->setReadOnly(true);
vsplitter->addWidget(descriptionView);
vsplitter->setStretchFactor(0, 3);
vsplitter->setStretchFactor(1, 1);
hsplitter->setStretchFactor(0, 2);
hsplitter->setStretchFactor(1, 3);
QVBoxLayout *box = new QVBoxLayout();
newButton = new QPushButton(tr("New"), this); box->addWidget(newButton);
QMenu *menu = new QMenu(this);
menu->addAction(tr("Variable/constant"), this, SLOT(newVariable()));
menu->addAction(tr("Unknown variable"), this, SLOT(newUnknown()));
newButton->setMenu(menu);
editButton = new QPushButton(tr("Edit"), this); box->addWidget(editButton); connect(editButton, SIGNAL(clicked()), this, SLOT(editClicked()));
delButton = new QPushButton(tr("Delete"), this); box->addWidget(delButton); connect(delButton, SIGNAL(clicked()), this, SLOT(delClicked()));
deactivateButton = new QPushButton(tr("Deactivate"), this); box->addWidget(deactivateButton); connect(deactivateButton, SIGNAL(clicked()), this, SLOT(deactivateClicked()));
box->addSpacing(24);
insertButton = new QPushButton(tr("Insert"), this); box->addWidget(insertButton); connect(insertButton, SIGNAL(clicked()), this, SLOT(insertClicked()));
insertButton->setDefault(true);
box->addStretch(1);
hbox->addLayout(box, 0);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, this);
topbox->addWidget(buttonBox);
selected_category = "All";
updateVariables();
variablesModel->setFilter("All");
connect(searchEdit, SIGNAL(textEdited(const QString&)), this, SLOT(searchChanged(const QString&)));
connect(buttonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(reject()));
connect(categoriesView, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(selectedCategoryChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
connect(variablesView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(selectedVariableChanged(const QModelIndex&, const QModelIndex&)));
connect(variablesView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(insertClicked()));
selectedVariableChanged(QModelIndex(), QModelIndex());
if(!settings->variables_geometry.isEmpty()) restoreGeometry(settings->variables_geometry);
else resize(900, 700);
if(!settings->variables_vsplitter_state.isEmpty()) vsplitter->restoreState(settings->variables_vsplitter_state);
if(!settings->variables_hsplitter_state.isEmpty()) hsplitter->restoreState(settings->variables_hsplitter_state);
}
VariablesDialog::~VariablesDialog() {}
void VariablesDialog::keyPressEvent(QKeyEvent *event) {
if(event->matches(QKeySequence::Find)) {
searchEdit->setFocus();
return;
}
if(event->key() == Qt::Key_Escape && searchEdit->hasFocus()) {
searchEdit->clear();
variablesView->setFocus();
return;
}
if(event->key() == Qt::Key_Return && variablesView->hasFocus()) {
QModelIndex index = variablesView->selectionModel()->currentIndex();
if(index.isValid()) {
insertClicked();
return;
}
}
QDialog::keyPressEvent(event);
}
void VariablesDialog::closeEvent(QCloseEvent *e) {
settings->variables_geometry = saveGeometry();
settings->variables_vsplitter_state = vsplitter->saveState();
settings->variables_hsplitter_state = hsplitter->saveState();
QDialog::closeEvent(e);
}
void VariablesDialog::reject() {
settings->variables_geometry = saveGeometry();
settings->variables_vsplitter_state = vsplitter->saveState();
settings->variables_hsplitter_state = hsplitter->saveState();
QDialog::reject();
}
void VariablesDialog::searchChanged(const QString &str) {
variablesModel->setSecondaryFilter(str.toStdString());
variablesView->selectionModel()->setCurrentIndex(variablesModel->index(0, 0), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
void VariablesDialog::newVariable() {
ExpressionItem *replaced_item = NULL;
KnownVariable *v = VariableEditDialog::newVariable(this, NULL, QString(), &replaced_item);
if(v) {
if(replaced_item && (replaced_item == v || !item_in_calculator(replaced_item))) {
QModelIndexList list = sourceModel->match(sourceModel->index(0, 0), Qt::UserRole, QVariant::fromValue((void*) replaced_item), 1, Qt::MatchExactly);
if(!list.isEmpty()) sourceModel->removeRow(list[0].row());
}
selected_item = v;
QStandardItem *item = new QStandardItem(QString::fromStdString(v->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) v), Qt::UserRole);
sourceModel->appendRow(item);
if(selected_category != "All" && selected_category != "User items" && selected_category != std::string("/") + v->category()) {
QList<QTreeWidgetItem*> list = categoriesView->findItems("User items", Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
} else {
variablesModel->invalidate();
}
sourceModel->sort(0);
QModelIndex index = variablesModel->mapFromSource(item->index());
if(index.isValid()) {
variablesView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
variablesView->scrollTo(index);
}
emit itemsChanged();
}
}
void VariablesDialog::newUnknown() {
ExpressionItem *replaced_item = NULL;
UnknownVariable *v = UnknownEditDialog::newVariable(this, &replaced_item);
if(v) {
if(replaced_item && (replaced_item == v || !item_in_calculator(replaced_item))) {
if(!CALCULATOR->stillHasUnit((Unit*) replaced_item)) {
emit unitRemoved((Unit*) replaced_item);
} else if(replaced_item == v || !CALCULATOR->stillHasVariable((Variable*) replaced_item) || (replaced_item->type() == TYPE_VARIABLE && !CALCULATOR->hasVariable((Variable*) replaced_item))) {
QModelIndexList list = sourceModel->match(sourceModel->index(0, 0), Qt::UserRole, QVariant::fromValue((void*) replaced_item), 1, Qt::MatchExactly);
if(!list.isEmpty()) sourceModel->removeRow(list[0].row());
} else if(replaced_item->type() == TYPE_UNIT && !CALCULATOR->hasUnit((Unit*) replaced_item)) {
emit unitRemoved((Unit*) replaced_item);
}
}
selected_item = v;
QStandardItem *item = new QStandardItem(QString::fromStdString(v->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) v), Qt::UserRole);
sourceModel->appendRow(item);
if(selected_category != "All" && selected_category != "User items" && selected_category != std::string("/") + v->category()) {
QList<QTreeWidgetItem*> list = categoriesView->findItems("User items", Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
} else {
variablesModel->invalidate();
}
sourceModel->sort(0);
QModelIndex index = variablesModel->mapFromSource(item->index());
if(index.isValid()) {
variablesView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
variablesView->scrollTo(index);
}
emit itemsChanged();
}
}
void VariablesDialog::variableRemoved(Variable *v) {
QModelIndexList list = sourceModel->match(sourceModel->index(0, 0), Qt::UserRole, QVariant::fromValue((void*) v), 1, Qt::MatchExactly);
if(!list.isEmpty()) sourceModel->removeRow(list[0].row());
}
void VariablesDialog::editClicked() {
QModelIndex index = variablesView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Variable *v = (Variable*) index.data(Qt::UserRole).value<void*>();
if(!v) return;
bool b = false;
ExpressionItem *replaced_item = NULL;
if(v->isKnown()) {
b = VariableEditDialog::editVariable(this, (KnownVariable*) v, NULL, &replaced_item);
} else {
b = UnknownEditDialog::editVariable(this, (UnknownVariable*) v, &replaced_item);
}
if(b) {
sourceModel->removeRow(variablesModel->mapToSource(variablesView->selectionModel()->currentIndex()).row());
if(replaced_item) {
if(!CALCULATOR->stillHasUnit((Unit*) replaced_item)) {
emit unitRemoved((Unit*) replaced_item);
} else if(!CALCULATOR->stillHasVariable((Variable*) replaced_item) || (replaced_item->type() == TYPE_VARIABLE && !CALCULATOR->hasVariable((Variable*) replaced_item))) {
QModelIndexList list = sourceModel->match(sourceModel->index(0, 0), Qt::UserRole, QVariant::fromValue((void*) replaced_item), 1, Qt::MatchExactly);
if(!list.isEmpty()) sourceModel->removeRow(list[0].row());
} else if(replaced_item->type() == TYPE_UNIT && !CALCULATOR->hasUnit((Unit*) replaced_item)) {
emit unitRemoved((Unit*) replaced_item);
}
}
QStandardItem *item = new QStandardItem(QString::fromStdString(v->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) v), Qt::UserRole);
sourceModel->appendRow(item);
selected_item = v;
if(selected_category != "All" && selected_category != "User items" && selected_category != std::string("/") + v->category()) {
QList<QTreeWidgetItem*> list = categoriesView->findItems("User items", Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
} else {
variablesModel->invalidate();
}
sourceModel->sort(0);
QModelIndex index = variablesModel->mapFromSource(item->index());
if(index.isValid()) {
variablesView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
variablesView->scrollTo(index);
}
emit itemsChanged();
}
}
void VariablesDialog::delClicked() {
QModelIndex index = variablesView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Variable *v = (Variable*) index.data(Qt::UserRole).value<void*>();
if(v && v->isLocal()) {
sourceModel->removeRow(variablesModel->mapToSource(variablesView->selectionModel()->currentIndex()).row());
selected_item = NULL;
v->destroy();
emit itemsChanged();
}
}
void VariablesDialog::insertClicked() {
QModelIndex index = variablesView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Variable *v = (Variable*) index.data(Qt::UserRole).value<void*>();
if(v) {
emit insertVariableRequest(v);
}
}
void VariablesDialog::deactivateClicked() {
QModelIndex index = variablesView->selectionModel()->currentIndex();
if(!index.isValid()) return;
Variable *v = (Variable*) index.data(Qt::UserRole).value<void*>();
if(v) {
v->setActive(!v->isActive());
variablesModel->invalidate();
emit itemsChanged();
}
}
void VariablesDialog::selectedVariableChanged(const QModelIndex &index, const QModelIndex&) {
if(index.isValid()) {
Variable *v = (Variable*) index.data(Qt::UserRole).value<void*>();
if(CALCULATOR->stillHasVariable(v)) {
selected_item = v;
std::string str;
const ExpressionName *ename = &v->preferredName(false, settings->printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) descriptionView);
str = "<b>";
str += ename->name;
str += "</b>";
for(size_t i2 = 1; i2 <= v->countNames(); i2++) {
if(&v->getName(i2) != ename) {
str += ", ";
str += v->getName(i2).name;
}
}
str += "<br><br>";
if(v->isKnown()) {
bool is_approximate = false;
if(((KnownVariable*) v)->get().isMatrix() && ((KnownVariable*) v)->get().columns() * ((KnownVariable*) v)->get().rows() > 16) {
str += tr("a matrix").toStdString();
} else if(((KnownVariable*) v)->get().isVector() && ((KnownVariable*) v)->get().size() > 10) {
str += tr("a vector").toStdString();
} else {
PrintOptions po = settings->printops;
po.can_display_unicode_string_arg = (void*) descriptionView;
po.interval_display = INTERVAL_DISPLAY_PLUSMINUS;
po.base = 10;
po.number_fraction_format = FRACTION_DECIMAL_EXACT;
po.allow_non_usable = true;
po.is_approximate = &is_approximate;
if(v->isApproximate() || is_approximate) str += SIGN_ALMOST_EQUAL " ";
else str += "= ";
str += CALCULATOR->print(((KnownVariable*) v)->get(), 1000, po, true, settings->colorize_result ? settings->color : 0, TAG_TYPE_HTML);
}
} else {
if(((UnknownVariable*) v)->assumptions()) {
QString value;
if(((UnknownVariable*) v)->assumptions()->type() != ASSUMPTION_TYPE_BOOLEAN) {
switch(((UnknownVariable*) v)->assumptions()->sign()) {
case ASSUMPTION_SIGN_POSITIVE: {value = tr("positive"); break;}
case ASSUMPTION_SIGN_NONPOSITIVE: {value = tr("non-positive"); break;}
case ASSUMPTION_SIGN_NEGATIVE: {value = tr("negative"); break;}
case ASSUMPTION_SIGN_NONNEGATIVE: {value = tr("non-negative"); break;}
case ASSUMPTION_SIGN_NONZERO: {value = tr("non-zero"); break;}
default: {}
}
}
if(!value.isEmpty() && ((UnknownVariable*) v)->assumptions()->type() != ASSUMPTION_TYPE_NONE) value += " ";
switch(((UnknownVariable*) v)->assumptions()->type()) {
case ASSUMPTION_TYPE_INTEGER: {value += tr("integer"); break;}
case ASSUMPTION_TYPE_BOOLEAN: {value += tr("boolean"); break;}
case ASSUMPTION_TYPE_RATIONAL: {value += tr("rational"); break;}
case ASSUMPTION_TYPE_REAL: {value += tr("real"); break;}
case ASSUMPTION_TYPE_COMPLEX: {value += tr("complex"); break;}
case ASSUMPTION_TYPE_NUMBER: {value += tr("number"); break;}
case ASSUMPTION_TYPE_NONMATRIX: {value += tr("non-matrix"); break;}
default: {}
}
if(value.isEmpty()) value = tr("unknown");
str += value.toStdString();
} else {
str += tr("Default assumptions").toStdString();
}
}
if(!v->description().empty()) {
str += "<br><br>";
str += v->description();
}
if(v->isActive() != (deactivateButton->text() == tr("Deactivate"))) {
deactivateButton->setMinimumWidth(deactivateButton->width());
if(v->isActive()) {
deactivateButton->setText(tr("Deactivate"));
} else {
deactivateButton->setText(tr("Activate"));
}
}
editButton->setEnabled(!v->isBuiltin());
insertButton->setEnabled(v->isActive());
delButton->setEnabled(v->isLocal());
deactivateButton->setEnabled(!settings->isAnswerVariable(v) && v != settings->v_memory);
descriptionView->setHtml(QString::fromStdString(str));
return;
}
}
editButton->setEnabled(false);
insertButton->setEnabled(false);
delButton->setEnabled(false);
deactivateButton->setEnabled(false);
descriptionView->clear();
selected_item = NULL;
}
void VariablesDialog::selectedCategoryChanged(QTreeWidgetItem *iter, QTreeWidgetItem*) {
if(!iter) selected_category = "";
else selected_category = iter->text(1).toStdString();
searchEdit->clear();
variablesModel->setFilter(selected_category);
QModelIndex index = variablesView->selectionModel()->currentIndex();
if(index.isValid()) {
selectedVariableChanged(index, QModelIndex());
variablesView->scrollTo(index);
}
}
struct tree_struct {
std::string item;
std::list<tree_struct> items;
std::list<tree_struct>::iterator it;
std::list<tree_struct>::reverse_iterator rit;
tree_struct *parent;
void sort() {
items.sort();
for(std::list<tree_struct>::iterator it = items.begin(); it != items.end(); ++it) {
it->sort();
}
}
bool operator < (const tree_struct &s1) const {
return string_is_less(item, s1.item);
}
};
void VariablesDialog::updateVariables() {
size_t cat_i, cat_i_prev;
bool b;
std::string str, cat, cat_sub;
tree_struct variable_cats;
variable_cats.parent = NULL;
bool has_inactive = false, has_uncat = false;
std::list<tree_struct>::iterator it;
sourceModel->clear();
sourceModel->setColumnCount(1);
sourceModel->setHorizontalHeaderItem(0, new QStandardItem(tr("Variable")));
for(size_t i = 0; i < CALCULATOR->variables.size(); i++) {
Variable *v = CALCULATOR->variables[i];
if(!v->isActive()) {
has_inactive = true;
} else {
tree_struct *item = &variable_cats;
if(!v->category().empty()) {
cat = v->category();
cat_i = cat.find("/"); cat_i_prev = 0;
b = false;
while(true) {
if(cat_i == std::string::npos) {
cat_sub = cat.substr(cat_i_prev, cat.length() - cat_i_prev);
} else {
cat_sub = cat.substr(cat_i_prev, cat_i - cat_i_prev);
}
b = false;
for(it = item->items.begin(); it != item->items.end(); ++it) {
if(cat_sub == it->item) {
item = &*it;
b = true;
break;
}
}
if(!b) {
tree_struct cat;
item->items.push_back(cat);
it = item->items.end();
--it;
it->parent = item;
item = &*it;
item->item = cat_sub;
}
if(cat_i == std::string::npos) {
break;
}
cat_i_prev = cat_i + 1;
cat_i = cat.find("/", cat_i_prev);
}
} else if(!v->isLocal()) {
has_uncat = true;
}
}
QStandardItem *item = new QStandardItem(QString::fromStdString(v->title(true)));
item->setEditable(false);
item->setData(QVariant::fromValue((void*) v), Qt::UserRole);
sourceModel->appendRow(item);
if(v == selected_item) variablesView->selectionModel()->setCurrentIndex(variablesModel->mapFromSource(item->index()), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
sourceModel->sort(0);
variable_cats.sort();
categoriesView->clear();
QTreeWidgetItem *iter, *iter2, *iter3;
QStringList l;
l << tr("All", "All variables"); l << "All";
iter3 = new QTreeWidgetItem(categoriesView, l);
tree_struct *item, *item2;
variable_cats.it = variable_cats.items.begin();
if(variable_cats.it != variable_cats.items.end()) {
item = &*variable_cats.it;
++variable_cats.it;
item->it = item->items.begin();
} else {
item = NULL;
}
str = "";
iter2 = iter3;
while(item) {
str += "/";
str += item->item;
l.clear(); l << QString::fromStdString(item->item); l << QString::fromStdString(str);
iter = new QTreeWidgetItem(iter2, l);
if(str == selected_category) {
iter->setExpanded(true);
iter->setSelected(true);
}
while(item && item->it == item->items.end()) {
size_t str_i = str.rfind("/");
if(str_i == std::string::npos) {
str = "";
} else {
str = str.substr(0, str_i);
}
item = item->parent;
iter2 = iter->parent();
iter = iter2;
}
if(item) {
item2 = &*item->it;
if(item->it == item->items.begin()) iter2 = iter;
++item->it;
item = item2;
item->it = item->items.begin();
}
}
if(has_uncat) {
//add "Uncategorized" category if there are variables without category
l.clear(); l << tr("Uncategorized"); l << "Uncategorized";
iter = new QTreeWidgetItem(iter3, l);
if(selected_category == "Uncategorized") {
iter->setSelected(true);
}
}
l.clear(); l << tr("User variables"); l << "User items";
iter = new QTreeWidgetItem(iter3, l);
if(selected_category == "User items") {
iter->setSelected(true);
}
if(has_inactive) {
//add "Inactive" category if there are inactive variables
l.clear(); l << tr("Inactive"); l << "Inactive";
iter = new QTreeWidgetItem(categoriesView, l);
if(selected_category == "Inactive") {
iter->setSelected(true);
}
}
if(categoriesView->selectedItems().isEmpty()) {
//if no category has been selected (previously selected has been renamed/deleted), select "All"
selected_category = "All";
iter3->setExpanded(true);
iter3->setSelected(true);
}
}
void VariablesDialog::setSearch(const QString &str) {
searchEdit->setText(str);
searchChanged(str);
}
void VariablesDialog::selectCategory(std::string str) {
QList<QTreeWidgetItem*> list = categoriesView->findItems((str.empty() || str == "All") ? "All" : "/" + QString::fromStdString(str), Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap, 1);
if(!list.isEmpty()) {
categoriesView->setCurrentItem(list[0], 0, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear);
}
}

87
src/variablesdialog.h Normal file
View File

@ -0,0 +1,87 @@
/*
Qalculate (QT UI)
Copyright (C) 2021 Hanna Knutsson (hanna.knutsson@protonmail.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#ifndef VARIABLES_DIALOG_H
#define VARIABLES_DIALOG_H
#include <QDialog>
#include <libqalculate/qalculate.h>
class QTreeView;
class QTreeWidget;
class QTextEdit;
class QTreeWidgetItem;
class QStandardItemModel;
class QPushButton;
class QLineEdit;
class QSplitter;
class ItemProxyModel;
class VariablesDialog : public QDialog {
Q_OBJECT
protected:
QTreeView *variablesView;
QTreeWidget *categoriesView;
QTextEdit *descriptionView;
ItemProxyModel *variablesModel;
QStandardItemModel *sourceModel;
QPushButton *deactivateButton, *insertButton, *delButton, *editButton, *newButton;
QLineEdit *searchEdit;
QSplitter *vsplitter, *hsplitter;
std::string selected_category;
ExpressionItem *selected_item;
void keyPressEvent(QKeyEvent *event) override;
void closeEvent(QCloseEvent*) override;
protected slots:
void selectedCategoryChanged(QTreeWidgetItem*, QTreeWidgetItem*);
void selectedVariableChanged(const QModelIndex&, const QModelIndex&);
void newVariable();
void newUnknown();
void editClicked();
void delClicked();
void insertClicked();
void deactivateClicked();
void searchChanged(const QString&);
public:
VariablesDialog(QWidget *parent = NULL);
virtual ~VariablesDialog();
void updateVariables();
void setSearch(const QString&);
void selectCategory(std::string);
void variableRemoved(Variable*);
public slots:
void reject() override;
signals:
void itemsChanged();
void unitRemoved(Unit*);
void applyVariableRequest(Variable*);
void insertVariableRequest(Variable*);
};
#endif //VARIABLES_DIALOG_H