diff options
author | Slávek Banko <slavek.banko@axis.cz> | 2016-04-09 15:59:57 +0200 |
---|---|---|
committer | Slávek Banko <slavek.banko@axis.cz> | 2016-04-09 15:59:57 +0200 |
commit | ee0c9d4bc3e25a409b3127be2876079f69719978 (patch) | |
tree | 04d895b486c04df1fe2e5dedb7cd5705eff06a9b /src/qalculateexpressionedit.cpp | |
download | qalculate-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.cpp | 1018 |
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" |