summaryrefslogtreecommitdiffstats
path: root/src/qalculateexpressionedit.cpp
diff options
context:
space:
mode:
authorSlávek Banko <slavek.banko@axis.cz>2016-04-09 15:59:57 +0200
committerSlávek Banko <slavek.banko@axis.cz>2016-04-09 15:59:57 +0200
commitee0c9d4bc3e25a409b3127be2876079f69719978 (patch)
tree04d895b486c04df1fe2e5dedb7cd5705eff06a9b /src/qalculateexpressionedit.cpp
downloadqalculate-tde-ee0c9d4bc3e25a409b3127be2876079f69719978.tar.gz
qalculate-tde-ee0c9d4bc3e25a409b3127be2876079f69719978.zip
Initial import of qalculate-kde 0.9.7
Diffstat (limited to 'src/qalculateexpressionedit.cpp')
-rw-r--r--src/qalculateexpressionedit.cpp1018
1 files changed, 1018 insertions, 0 deletions
diff --git a/src/qalculateexpressionedit.cpp b/src/qalculateexpressionedit.cpp
new file mode 100644
index 0000000..6ef9328
--- /dev/null
+++ b/src/qalculateexpressionedit.cpp
@@ -0,0 +1,1018 @@
+/***************************************************************************
+ * Copyright (C) 2005. 2007 by Niklas Knutsson *
+ * nq@altern.org *
+ * *
+ * 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. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "qalculateexpressionedit.h"
+
+#include "qalculate_kde_utils.h"
+#include "kqalculate.h"
+#include <kdeversion.h>
+#include <kcursor.h>
+#include <qstyle.h>
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qfontmetrics.h>
+#include <kstringhandler.h>
+#include <kpopupmenu.h>
+#include <kxmlguifactory.h>
+#include <kxmlguiclient.h>
+#include <kaction.h>
+#include <klocale.h>
+#include <qsimplerichtext.h>
+
+extern vector<mode_struct> modes;
+
+extern KQalculate *mainWin;
+extern PrintOptions printops;
+extern EvaluationOptions evalops;
+extern bool rpn_mode, rpn_keypad_only;
+extern tree_struct function_cats, unit_cats, variable_cats;
+extern vector<void*> ia_units, ia_variables, ia_functions;
+
+class QalculateExpressionEditListBoxItem : public QListBoxItem {
+
+public:
+
+ QalculateExpressionEditListBoxItem(ExpressionItem *eitem);
+ virtual ~QalculateExpressionEditListBoxItem();
+
+ int height(const QListBox*) const;
+ int width(const QListBox*) const;
+
+ bool reuse(ExpressionItem *newItem);
+
+ ExpressionItem *item;
+
+protected:
+
+ void paint(QPainter*);
+
+private:
+
+ QString title;
+ int italic_index;
+ bool rich_text;
+
+};
+
+QalculateExpressionEdit::QalculateExpressionEdit(bool connected_to_main_win, QWidget *parent, const char *name) : KLineEdit(parent, name) {
+
+ qalculateCompletionBox = new QalculateExpressionEditCompletionBox(this);
+ qalculateCompletion = new KCompletion();
+ setCompletionObject(qalculateCompletion);
+
+ pos_before_completion = 0;
+
+ b_main = connected_to_main_win;
+
+ dont_change_index = false;
+ expression_history_index = -1;
+
+ setCompletionMode(KGlobalSettings::CompletionPopup);
+
+ setKeyCompression(false);
+
+ //check for position changes regularly
+ prev_position = 0;
+ pos_timer = startTimer(50);
+
+ connect(qalculateCompletionBox, SIGNAL(highlighted(QListBoxItem*)), this, SLOT(insertCompletion(QListBoxItem*)));
+ connect(qalculateCompletionBox, SIGNAL(userCancelled(const QString&)), this, SLOT(cancelCompletion(const QString&)));
+ connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged(const QString&)));
+
+}
+
+QalculateExpressionEdit::~QalculateExpressionEdit() {}
+
+void QalculateExpressionEdit::timerEvent(QTimerEvent *event) {
+ if(event->timerId() == pos_timer) {
+ if(cursorPosition() != prev_position) {
+ prev_position = cursorPosition();
+ emit cursorMoved();
+ }
+ } else {
+ KLineEdit::timerEvent(event);
+ }
+}
+
+void QalculateExpressionEdit::onTextChanged(const QString &str) {
+ if(str.isEmpty()) {
+ if(qalculateCompletionBox) {
+ qalculateCompletionBox->hide();
+ qalculateCompletionBox->clear();
+ }
+ return;
+ }
+ prev_position = cursorPosition();
+}
+
+void QalculateExpressionEdit::cancelCompletion(const QString &str) {
+ int new_pos = pos_before_completion;
+ setText(str);
+ setCursorPosition(new_pos);
+}
+
+void QalculateExpressionEdit::enableCompletion() {
+ setCompletionMode(KGlobalSettings::CompletionPopup);
+}
+void QalculateExpressionEdit::disableCompletion() {
+ setCompletionMode(KGlobalSettings::CompletionNone);
+}
+bool QalculateExpressionEdit::completionEnabled() const {
+ return completionMode() == KGlobalSettings::CompletionPopup;
+}
+
+QPopupMenu *QalculateExpressionEdit::createPopupMenu() {
+
+ setCompletionObject(NULL);
+ QPopupMenu *menu = KLineEdit::createPopupMenu();
+ setCompletionObject(qalculateCompletion);
+ menu->insertSeparator();
+ if(completionMode() == KGlobalSettings::CompletionPopup) menu->insertItem(i18n("Disable Completion"), this, SLOT(disableCompletion()));
+ else menu->insertItem(i18n("Enable Completion"), this, SLOT(enableCompletion()));
+ if(b_main) {
+ menu->insertSeparator();
+ mainWin->ActionReadPrecision->plug(menu);
+ mainWin->ActionLimitImplicitMultiplication->plug(menu);
+ mainWin->ActionRPNMode->plug(menu);
+ QPopupMenu *modes_menu = new QPopupMenu(menu);
+ QObject::connect(modes_menu, SIGNAL(activated(int)), mainWin, SLOT(loadMode(int)));
+ for(size_t i = 0; i < modes.size(); i++) {
+ modes_menu->insertItem(modes[i].name, i, i);
+ }
+ modes_menu->insertSeparator();
+ mainWin->ActionSaveModeAs->plug(modes_menu);
+ mainWin->ActionDeleteMode->plug(modes_menu);
+ menu->insertItem(i18n("Meta Modes"), modes_menu);
+ menu->insertSeparator();
+ mainWin->ActionInsertMatrix->plug(menu);
+ mainWin->ActionInsertVector->plug(menu);
+ }
+ return menu;
+
+}
+
+#if QT_VERSION >= 0x030200
+#define GET_SELECTION int start = selectionStart(), end = -1; if(start >= 0) end = start + selectedText().length();
+#else
+#define GET_SELECTION int start = -1, end = -1; getSelection(&start, &end);
+#endif
+#define RESTORE_SELECTION if(start > 0) setSelection(start, end - start);
+
+void QalculateExpressionEdit::addToHistory(const QString &str) {
+ for(QStringList::Iterator it = expression_history.begin(); it != expression_history.end(); ++it) {
+ if(*it == str) {
+ expression_history.erase(it);
+ break;
+ }
+ }
+ if(expression_history.size() >= 25) {
+ expression_history.pop_back();
+ }
+ expression_history.insert(expression_history.begin(), str);
+ expression_history_index = 0;
+}
+
+
+void QalculateExpressionEdit::setAfterCompletionPosition() {
+ setCursorPosition(cpos_ac);
+}
+
+void QalculateExpressionEdit::insertCompletion(QListBoxItem *li) {
+
+ ExpressionItem *item = ((QalculateExpressionEditListBoxItem*) li)->item;
+
+ setSelection(cstart, cend - cstart + 1);
+
+ QString str = completed_text;
+ const ExpressionName *ename = NULL, *ename_r = NULL;
+ ename_r = &item->preferredInputName(false, printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) this);
+ for(size_t name_i = 0; name_i <= item->countNames() && !ename; name_i++) {
+ if(name_i == 0) {
+ ename = ename_r;
+ } else {
+ ename = &item->getName(name_i);
+ if(!ename || ename == ename_r || ename->plural || (ename->unicode && (!printops.use_unicode_signs || !can_display_unicode_string_function(ename->name.c_str(), (void*) this)))) {
+ ename = NULL;
+ }
+ }
+ if(ename) {
+ if(str.length() <= ename->name.length()) {
+ for(size_t i = 0; i < str.length(); i++) {
+ if(ename->name[i] != str[i]) {
+ ename = NULL;
+ break;
+ }
+ }
+ } else {
+ ename = NULL;
+ }
+ }
+ }
+ for(size_t name_i = 1; name_i <= item->countNames() && !ename; name_i++) {
+ ename = &item->getName(name_i);
+ if(!ename || ename == ename_r || (!ename->plural && !(ename->unicode && (!printops.use_unicode_signs || !can_display_unicode_string_function(ename->name.c_str(), (void*) this))))) {
+ ename = NULL;
+ }
+ if(ename) {
+ if(str.length() <= ename->name.length()) {
+ for(size_t i = 0; i < str.length(); i++) {
+ if(ename->name[i] != str[i]) {
+ ename = NULL;
+ break;
+ }
+ }
+ } else {
+ ename = NULL;
+ }
+ }
+ }
+ if(!ename) ename = ename_r;
+ if(!ename) return;
+ if(item->type() == TYPE_FUNCTION) {
+ if(text()[cend + 1] == '(') {
+ insert(ename->name.c_str());
+ cend = cstart + ename->name.length() - 1;
+ cpos_ac = cend + 2;
+ } else {
+ str = ename->name.c_str();
+ str += "()";
+ insert(str);
+ cend = cstart + str.length() - 1;
+ cpos_ac = cend;
+ }
+ } else {
+ insert(ename->name.c_str());
+ cend = cstart + ename->name.length() - 1;
+ cpos_ac = cend + 1;
+ }
+ setCursorPosition(cpos_ac);
+
+}
+
+bool matchesExpressionItem(const QString &str, ExpressionItem *item) {
+ bool b_match = false;
+ for(size_t name_i = 1; !b_match && name_i <= item->countNames(); name_i++) {
+ const ExpressionName *ename = &item->getName(name_i);
+ if(ename && str.length() <= ename->name.length()) {
+ b_match = true;
+ for(size_t i = 0; i < str.length(); i++) {
+ if(ename->name[i] != str[i]) {
+ b_match = false;
+ break;
+ }
+ }
+ }
+ }
+ return b_match;
+}
+
+void QalculateExpressionEdit::makeCompletion(const QString &str) {
+ if(cursorPosition() <= 0 || str.isEmpty()) {
+ if(qalculateCompletionBox) {
+ qalculateCompletionBox->hide();
+ qalculateCompletionBox->clear();
+ }
+ return;
+ }
+ uint start = (uint) cursorPosition();
+ cend = (int) start - 1;
+ QString str2 = str;
+ str2.truncate(start);
+ const char *cstr = str2.ascii();
+ bool non_number_before = false;
+ size_t cpos = strlen(cstr) - 1;
+ start--;
+ while(true) {
+ while(cpos > 0 && (unsigned char) cstr[cpos] >= 0x80 && (unsigned char) cstr[cpos] <= 0xBF) {
+ cpos--;
+ }
+ if(!CALCULATOR->utf8_pos_is_valid_in_name((char*) &cstr[cpos])) {
+ start++;
+ break;
+ } else if(is_in(NUMBERS, cstr[cpos])) {
+ if(non_number_before) {
+ start++;
+ break;
+ }
+ } else {
+ non_number_before = true;
+ }
+ if(start == 0 || cpos == 0) break;
+ start--;
+ cpos--;
+ }
+ if(start >= (uint) cursorPosition()) {
+ if(qalculateCompletionBox) {
+ qalculateCompletionBox->hide();
+ qalculateCompletionBox->clear();
+ }
+ return;
+ }
+ cstart = (int) start;
+ str2.remove(0, start);
+
+ completed_text = str2;
+ pos_before_completion = cursorPosition();
+ qalculateCompletionBox->setCancelledText(text());
+
+ matched_items.clear();
+ if(evalops.parse_options.functions_enabled) {
+ for(size_t i = 0; i < CALCULATOR->functions.size(); i++) {
+ if(CALCULATOR->functions[i]->isActive()) {
+ if(matchesExpressionItem(str2, CALCULATOR->functions[i])) {
+ matched_items.push_back(CALCULATOR->functions[i]);
+ }
+ }
+ }
+ }
+ if(evalops.parse_options.variables_enabled) {
+ for(size_t i = 0; i < CALCULATOR->variables.size(); i++) {
+ if(CALCULATOR->variables[i]->isActive()) {
+ if(matchesExpressionItem(str2, CALCULATOR->variables[i])) {
+ matched_items.push_back(CALCULATOR->variables[i]);
+ }
+ }
+ }
+ }
+ if(evalops.parse_options.units_enabled) {
+ for(size_t i = 0; i < CALCULATOR->units.size(); i++) {
+ if(CALCULATOR->units[i]->isActive() && CALCULATOR->units[i]->subtype() != SUBTYPE_COMPOSITE_UNIT) {
+ if(matchesExpressionItem(str2, CALCULATOR->units[i])) {
+ matched_items.push_back(CALCULATOR->units[i]);
+ }
+ }
+ }
+ }
+
+ if(matched_items.isEmpty()) {
+ if(qalculateCompletionBox) {
+ qalculateCompletionBox->hide();
+ qalculateCompletionBox->clear();
+ }
+ } else {
+ setCompletedItems();
+ }
+}
+
+void QalculateExpressionEdit::updateCompletion() {
+}
+
+void QalculateExpressionEdit::wrapSelection() {
+ GET_SELECTION
+ if(start >= 0) {
+ deselect();
+ setCursorPosition(start);
+ insert("(");
+ setCursorPosition(end + 1);
+ insert(")");
+ setSelection(start, end - start + 2);
+ }
+}
+
+void QalculateExpressionEdit::keyPressEvent(QKeyEvent *e) {
+ if(e->key() == Key_Enter || e->key() == Key_Return) {
+ if(b_main) {
+ mainWin->execute();
+ e->accept();
+ return;
+ } else {
+ return KLineEdit::keyPressEvent(e);
+ }
+ }
+ if(e->state() & ControlButton && e->key() == Key_Asterisk) {
+ if(rpn_mode && b_main && (!rpn_keypad_only || e->state() & Keypad)) {
+ mainWin->calculateRPN(OPERATION_RAISE);
+ return;
+ }
+ insert("^");
+ e->accept();
+ return;
+ }
+ if(e->state() != 0 && e->state() != ShiftButton && e->state() != Keypad) {
+ KLineEdit::keyPressEvent(e);
+ return;
+ }
+ switch(e->key()) {
+ case Key_Period: {
+ if(e->state() == Keypad) {
+ insert(CALCULATOR->getDecimalPoint());
+ e->accept();
+ return;
+ }
+ break;
+ }
+ case Key_Comma: {
+ if(e->state() == Keypad) {
+ insert(CALCULATOR->getDecimalPoint());
+ e->accept();
+ return;
+ }
+ break;
+ }
+ case Key_AsciiCircum: {
+ if(rpn_mode && b_main && (!rpn_keypad_only || e->state() & Keypad)) {
+ mainWin->calculateRPN(OPERATION_RAISE);
+ return;
+ }
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^");
+ e->accept();
+ return;
+ }
+ case Key_Slash: {
+ if(rpn_mode && b_main && (!rpn_keypad_only || e->state() & Keypad)) {
+ mainWin->calculateRPN(OPERATION_DIVIDE);
+ return;
+ }
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ if(printops.use_unicode_signs && printops.division_sign == DIVISION_SIGN_DIVISION && can_display_unicode_string_function(SIGN_DIVISION, (void*) this)) {
+ insert(SIGN_DIVISION);
+ e->accept();
+ return;
+ }
+ break;
+ }
+ case Key_Asterisk: {
+ if(rpn_mode && b_main && (!rpn_keypad_only || e->state() & Keypad)) {
+ mainWin->calculateRPN(OPERATION_MULTIPLY);
+ return;
+ }
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ if(printops.use_unicode_signs && printops.multiplication_sign == MULTIPLICATION_SIGN_DOT && can_display_unicode_string_function(SIGN_MULTIDOT, (void*) this)) {
+ insert(SIGN_MULTIDOT);
+ } else if(printops.use_unicode_signs && printops.multiplication_sign == MULTIPLICATION_SIGN_DOT && can_display_unicode_string_function(SIGN_SMALLCIRCLE, (void*) this)) {
+ insert(SIGN_SMALLCIRCLE);
+ } else if(printops.use_unicode_signs && printops.multiplication_sign == MULTIPLICATION_SIGN_X && can_display_unicode_string_function(SIGN_MULTIPLICATION, (void*) this)) {
+ insert(SIGN_MULTIPLICATION);
+ } else {
+ insert("*");
+ }
+ e->accept();
+ return;
+ }
+ case Key_Plus: {
+ if(rpn_mode && b_main && (!rpn_keypad_only || e->state() & Keypad)) {
+ mainWin->calculateRPN(OPERATION_ADD);
+ return;
+ }
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ e->accept();
+ break;
+ }
+ case Key_Minus: {
+ if(rpn_mode && b_main && (!rpn_keypad_only || e->state() & Keypad)) {
+ mainWin->calculateRPN(OPERATION_SUBTRACT);
+ return;
+ }
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ if(printops.use_unicode_signs && can_display_unicode_string_function(SIGN_MINUS, (void*) this)) {
+ insert(SIGN_MINUS);
+ e->accept();
+ return;
+ }
+ break;
+ }
+ case Key_Up: {}
+ case Key_PageUp: {
+ if(expression_history_index + 1 < (int) expression_history.size()) {
+ expression_history_index++;
+ dont_change_index = true;
+ setText(expression_history[expression_history_index]);
+ dont_change_index = false;
+ }
+ e->accept();
+ return;
+ }
+ case Key_Down: {}
+ case Key_PageDown: {
+ if(expression_history_index > -1) {
+ expression_history_index--;
+ dont_change_index = true;
+ if(expression_history_index < 0) {
+ clear();
+ } else {
+ setText(expression_history[expression_history_index]);
+ }
+ dont_change_index = false;
+ }
+ e->accept();
+ return;
+ }
+ case Key_BraceLeft: {}
+ case Key_BraceRight: {
+ return;
+ }
+ case 0xffff: {
+ if(e->text().utf8() == "⁰") {
+ insert("°");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "¹") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^1");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "²") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^2");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "³") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^3");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "⁴") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^4");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "⁵") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^5");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "⁶") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^6");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "⁷") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^7");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "⁸") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^8");
+ e->accept();
+ return;
+ }
+ if(e->text().utf8() == "⁹") {
+ if(!evalops.parse_options.rpn) {
+ wrapSelection();
+ deselect();
+ }
+ insert("^9");
+ e->accept();
+ return;
+ }
+ break;
+ }
+ }
+ KLineEdit::keyPressEvent(e);
+}
+
+bool QalculateExpressionEdit::eventFilter(QObject *o, QEvent *ev) {
+ if(o == this && ev->type() == QEvent::KeyPress) {
+ QKeyEvent *e = static_cast<QKeyEvent *>(ev);
+ if((e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) && qalculateCompletionBox && qalculateCompletionBox->isVisible()) {
+ if(qalculateCompletionBox->selectedItem()) {
+ KCursor::autoHideEventFilter(this, ev);
+ e->accept();
+ qalculateCompletionBox->hide();
+ deselect();
+ return true;
+ } else {
+ qalculateCompletionBox->hide();
+ return false;
+ }
+ }
+ }
+ return KLineEdit::eventFilter(o, ev);
+}
+
+
+void set_title(ExpressionItem *item, QString &title) {
+ if(item->type() != TYPE_VARIABLE || !item->title(false).empty()) {
+ title = item->title().c_str();
+ } else {
+ Variable *v = (Variable*) item;
+ if(is_answer_variable(v)) {
+ title = i18n("a previous result");
+ } else if(v->isKnown()) {
+ if(((KnownVariable*) v)->isExpression()) {
+ title = CALCULATOR->localizeExpression(((KnownVariable*) v)->expression()).c_str();
+ } else {
+ if(((KnownVariable*) v)->get().isMatrix()) {
+ title = i18n("matrix");
+ } else if(((KnownVariable*) v)->get().isVector()) {
+ title = i18n("vector");
+ } else {
+ title = CALCULATOR->printMathStructureTimeOut(((KnownVariable*) v)->get(), 30).c_str();
+ }
+ }
+ } else {
+ if(((UnknownVariable*) v)->assumptions()) {
+ switch(((UnknownVariable*) v)->assumptions()->sign()) {
+ case ASSUMPTION_SIGN_POSITIVE: {
+ title = i18n("positive");
+ break;
+ }
+ case ASSUMPTION_SIGN_NONPOSITIVE: {
+ title = i18n("non-positive");
+ break;
+ }
+ case ASSUMPTION_SIGN_NEGATIVE: {
+ title = i18n("negative");
+ break;
+ }
+ case ASSUMPTION_SIGN_NONNEGATIVE: {
+ title = i18n("non-negative");
+ break;
+ }
+ case ASSUMPTION_SIGN_NONZERO: {
+ title = i18n("non-zero");
+ break;
+ }
+ default: {}
+ }
+ if(!title.isEmpty() && !((UnknownVariable*) v)->assumptions()->type() == ASSUMPTION_TYPE_NONE) title += " ";
+ switch(((UnknownVariable*) v)->assumptions()->type()) {
+ case ASSUMPTION_TYPE_INTEGER: {
+ title += i18n("integer");
+ break;
+ }
+ case ASSUMPTION_TYPE_RATIONAL: {
+ title += i18n("rational");
+ break;
+ }
+ case ASSUMPTION_TYPE_REAL: {
+ title += i18n("real");
+ break;
+ }
+ case ASSUMPTION_TYPE_COMPLEX: {
+ title += i18n("complex");
+ break;
+ }
+ case ASSUMPTION_TYPE_NUMBER: {
+ title += i18n("number");
+ break;
+ }
+ case ASSUMPTION_TYPE_NONMATRIX: {
+ title += i18n("(not matrix)");
+ break;
+ }
+ default: {}
+ }
+ if(title.isEmpty()) title = i18n("unknown");
+ } else {
+ title = i18n("default assumptions");
+ }
+ }
+ }
+}
+
+string sub_suffix(const ExpressionName *ename) {
+ size_t i = ename->name.rfind('_');
+ bool b = i == string::npos || i == ename->name.length() - 1 || i == 0;
+ size_t i2 = 1;
+ string str;
+ if(b) {
+ if(is_in(NUMBERS, ename->name[ename->name.length() - 1])) {
+ while(ename->name.length() > i2 + 1 && is_in(NUMBERS, ename->name[ename->name.length() - 1 - i2])) {
+ i2++;
+ }
+ }
+ str += ename->name.substr(0, ename->name.length() - i2);
+ } else {
+ str += ename->name.substr(0, i);
+ }
+ str += "<sub>";
+ if(b) str += ename->name.substr(ename->name.length() - i2, i2);
+ else str += ename->name.substr(i + 1, ename->name.length() - (i + 1));
+ str += "</sub>";
+ return str;
+}
+
+QString makeListName(ExpressionItem *item, QWidget *w, int *italic_index, bool *rich_text) {
+ string str;
+ const ExpressionName *ename, *ename_r;
+ *rich_text = false;
+ bool b = false;
+ ename_r = &item->preferredInputName(false, printops.use_unicode_signs, false, false, &can_display_unicode_string_function, (void*) w);
+ if(ename_r->suffix && ename_r->name.length() > 1) {
+ str = sub_suffix(ename_r);
+ *rich_text = true;
+ } else {
+ str = ename_r->name;
+ }
+ if(item->type() == TYPE_FUNCTION) str += "()";
+ for(size_t name_i = 1; name_i <= item->countNames(); name_i++) {
+ ename = &item->getName(name_i);
+ if(ename && ename != ename_r && !ename->plural && (!ename->unicode || can_display_unicode_string_function(ename->name.c_str(), (void*) w))) {
+ str += " ";
+ if(!b) {
+ *italic_index = str.length();
+ *rich_text = true;
+ str += "<i>";
+ b = true;
+ }
+ if(ename->suffix && ename->name.length() > 1) {
+ str += sub_suffix(ename);
+ } else {
+ str += ename->name;
+ }
+ if(item->type() == TYPE_FUNCTION) str += "()";
+ }
+ }
+ if(b) str += "</i>";
+ return str.c_str();
+}
+
+QalculateExpressionEditListBoxItem::QalculateExpressionEditListBoxItem(ExpressionItem *eitem) : QListBoxItem() {
+ item = eitem;
+ italic_index = -1;
+ setText(makeListName(item, listBox(), &italic_index, &rich_text));
+ set_title(item, title);
+}
+QalculateExpressionEditListBoxItem::~QalculateExpressionEditListBoxItem() {
+}
+
+#if KDE_VERSION_MAJOR < 4 && KDE_VERSION_MINOR < 2
+QString rPixelSqueeze(const QString& name, const QFontMetrics& fontMetrics, uint maxPixels)
+{
+ uint nameWidth = fontMetrics.width(name);
+
+ if (maxPixels < nameWidth)
+ {
+ QString tmp = name;
+ const uint em = fontMetrics.maxWidth();
+ maxPixels -= fontMetrics.width("...");
+
+ while (maxPixels < nameWidth && !tmp.isEmpty())
+ {
+ int length = tmp.length();
+ int delta = em ? (nameWidth - maxPixels) / em : length;
+
+ if(delta < 1) delta = 1;
+ else if(length < delta) delta = length;
+
+ tmp.remove(length - delta, delta);
+ nameWidth = fontMetrics.width(tmp);
+ }
+
+ return (tmp + "...");
+ }
+
+ return name;
+}
+#endif
+
+void QalculateExpressionEditListBoxItem::paint(QPainter *painter) {
+
+ int itemHeight = height(listBox());
+
+ int entryWidth = listBox()->width() - listBox()->style().pixelMetric(QStyle::PM_ScrollBarExtent) - 2 * listBox()->style().pixelMetric(QStyle::PM_DefaultFrameWidth);
+ int titleWidth = (entryWidth / 2) - 1;
+ int nameWidth = entryWidth - titleWidth - 2;
+
+ if(!text().isEmpty()) {
+ QString squeezedText;
+ if(rich_text) {
+ QSimpleRichText rt(text(), painter->font());
+ rt.setWidth(entryWidth);
+ if(rt.widthUsed() > nameWidth - 1) {
+ squeezedText = text();
+ if(italic_index > 0) squeezedText.truncate(italic_index);
+#if KDE_VERSION_MAJOR < 4 && KDE_VERSION_MINOR < 2
+ squeezedText = rPixelSqueeze(squeezedText, listBox()->fontMetrics(), nameWidth);
+#else
+ squeezedText = KStringHandler::rPixelSqueeze(squeezedText, listBox()->fontMetrics(), nameWidth);
+#endif
+ painter->drawText(0, 0, nameWidth, itemHeight, Qt::AlignLeft | Qt::AlignVCenter, squeezedText);
+ } else {
+ QColorGroup cg = listBox()->colorGroup();
+ cg.setColor(QColorGroup::Text, painter->pen().color());
+ rt.draw(painter, 1, 0, QRect(0, 0, nameWidth - 1, itemHeight), cg, &painter->brush());
+ }
+ } else {
+#if KDE_VERSION_MAJOR < 4 && KDE_VERSION_MINOR < 2
+ squeezedText = rPixelSqueeze(text(), listBox()->fontMetrics(), nameWidth);
+#else
+ squeezedText = KStringHandler::rPixelSqueeze(text(), listBox()->fontMetrics(), nameWidth);
+#endif
+ painter->drawText(0, 0, nameWidth, itemHeight, Qt::AlignLeft | Qt::AlignVCenter, squeezedText);
+ }
+#if KDE_VERSION_MAJOR < 4 && KDE_VERSION_MINOR < 2
+ squeezedText = rPixelSqueeze(title, listBox()->fontMetrics(), titleWidth);
+#else
+ squeezedText = KStringHandler::rPixelSqueeze(title, listBox()->fontMetrics(), titleWidth);
+#endif
+ QFont font = painter->font();
+ font.setItalic(true);
+ painter->setFont(font);
+ painter->drawText(entryWidth - titleWidth, 0, titleWidth, itemHeight, Qt::AlignLeft | Qt::AlignVCenter, squeezedText);
+ }
+
+}
+
+int QalculateExpressionEditListBoxItem::height(const QListBox *lb) const {
+ int h;
+ h = lb->fontMetrics().lineSpacing() + 4;
+ return QMAX(h, QApplication::globalStrut().height());
+}
+
+int QalculateExpressionEditListBoxItem::width(const QListBox *lb) const {
+ return QMAX(lb->fontMetrics().width(text()) + 6, QApplication::globalStrut().width());
+}
+bool QalculateExpressionEditListBoxItem::reuse(ExpressionItem *newItem) {
+ if(item == newItem) return false;
+ item = newItem;
+ italic_index = -1;
+ setText(makeListName(item, listBox(), &italic_index, &rich_text));
+ set_title(item, title);
+ return true;
+}
+
+QalculateExpressionEditCompletionBox::QalculateExpressionEditCompletionBox(QWidget *parent, const char *name) : KCompletionBox(parent, name) {
+}
+QalculateExpressionEditCompletionBox::~QalculateExpressionEditCompletionBox() {
+}
+
+void QalculateExpressionEditCompletionBox::setItems(const QValueVector<ExpressionItem*>& items) {
+
+ bool block = signalsBlocked();
+ blockSignals(true);
+
+ QListBoxItem* item = firstItem();
+ if(!item) {
+ insertItemList(items);
+ } else {
+ //Keep track of whether we need to change anything,
+ //so we can avoid a repaint for identical updates,
+ //to reduce flicker
+ bool dirty = false;
+
+#if QT_VERSION >= 0x030200
+ QValueVector<ExpressionItem*>::ConstIterator it = items.constBegin();
+ const QValueVector<ExpressionItem*>::ConstIterator itEnd = items.constEnd();
+#else
+ QValueVector<ExpressionItem*>::ConstIterator it = items.begin();
+ const QValueVector<ExpressionItem*>::ConstIterator itEnd = items.end();
+#endif
+
+ for (; it != itEnd; ++it) {
+ if(item) {
+ const bool changed = ((QalculateExpressionEditListBoxItem*) item)->reuse(*it);
+ dirty = dirty || changed;
+ item = item->next();
+ } else {
+ dirty = true;
+ //Inserting an item is a way of making this dirty
+ insertItem(new QalculateExpressionEditListBoxItem(*it));
+ }
+ }
+
+ //If there is an unused item, mark as dirty -> less items now
+ if(item) dirty = true;
+
+ QListBoxItem* tmp = item;
+ while((item = tmp)) {
+ tmp = item->next();
+ delete item;
+ }
+
+ if(dirty) triggerUpdate(false);
+ }
+ sort();
+
+ if(isVisible() && size().height() != sizeHint().height()) {
+#if KDE_VERSION_MAJOR < 4 && KDE_VERSION_MINOR < 2
+ hide();
+ popup();
+#else
+ sizeAndPosition();
+#endif
+ }
+
+ blockSignals(block);
+
+ // Trigger d->down_workaround = true within KCompletionBox
+ QStringList dummy;
+ KCompletionBox::insertItems(dummy, 1);
+
+}
+
+void QalculateExpressionEditCompletionBox::insertItemList(const QValueVector<ExpressionItem*> & list, int index) {
+ if(index < 0) index = count();
+ for(QValueVector<ExpressionItem*>::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ insertItem(new QalculateExpressionEditListBoxItem(*it), index++);
+ }
+}
+
+void QalculateExpressionEdit::setCompletedItems() {
+
+ QString txt;
+ if(qalculateCompletionBox && qalculateCompletionBox->isVisible()) {
+ // The popup is visible already - do the matching on the initial string,
+ // not on the currently selected one.
+ txt = qalculateCompletionBox->cancelledText();
+ } else {
+ txt = text();
+ }
+
+ if(!matched_items.isEmpty() && !(matched_items.size() == 1 && matched_items[0]->hasName(txt.ascii()))) {
+
+ if(qalculateCompletionBox->isVisible()) {
+ bool wasSelected = qalculateCompletionBox->isSelected(qalculateCompletionBox->currentItem());
+ const QString currentSelection = qalculateCompletionBox->currentText();
+ qalculateCompletionBox->setItems(matched_items);
+ QListBoxItem* item = qalculateCompletionBox->findItem(currentSelection, Qt::ExactMatch);
+ // If no item is selected, that means the listbox hasn't been manipulated by the user yet,
+ // because it's not possible otherwise to have no selected item. In such case make
+ // always the first item current and unselected, so that the current item doesn't jump.
+ if(!item || !wasSelected) {
+ wasSelected = false;
+ item = qalculateCompletionBox->item(0);
+ }
+ if(item) {
+ qalculateCompletionBox->blockSignals(true);
+ qalculateCompletionBox->setCurrentItem(item);
+ qalculateCompletionBox->setSelected(item, wasSelected);
+ qalculateCompletionBox->blockSignals(false);
+ }
+ } else { // completion box not visible yet -> show it
+ if(!txt.isEmpty()) qalculateCompletionBox->setCancelledText(txt);
+ ((QalculateExpressionEditCompletionBox*) qalculateCompletionBox)->setItems(matched_items);
+ qalculateCompletionBox->popup();
+ }
+
+ } else {
+
+ if(qalculateCompletionBox && qalculateCompletionBox->isVisible()) {
+ qalculateCompletionBox->hide();
+ }
+
+ }
+
+}
+
+void QalculateExpressionEditCompletionBox::hideEvent(QHideEvent*) {
+ emit hidden();
+}
+
+#include "qalculateexpressionedit.moc"