You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
16 KiB
560 lines
16 KiB
/***************************************************************************
|
|
kmymoneyedit.cpp
|
|
-------------------
|
|
copyright : (C) 2000 by Michael Edwardes,
|
|
2004 by Thomas Baumgart
|
|
email : mte@users.sourceforge.net
|
|
ipwizard@users.sourceforge.net
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// QT Includes
|
|
|
|
#include <tqapplication.h>
|
|
#include <tqdesktopwidget.h>
|
|
#include <tqwidget.h>
|
|
#include <tqvbox.h>
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// KDE Includes
|
|
|
|
#include <kglobal.h>
|
|
#include <klocale.h>
|
|
#include <kdebug.h>
|
|
#include <klineedit.h>
|
|
#include <kiconloader.h>
|
|
#include <kconfig.h>
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Project Includes
|
|
|
|
#include <kmymoney/kmymoneylineedit.h>
|
|
#include "kmymoneyedit.h"
|
|
#include "kmymoneycalculator.h"
|
|
#include "../mymoney/mymoneymoney.h"
|
|
|
|
kMyMoneyMoneyValidator::kMyMoneyMoneyValidator(TQObject * tqparent, const char * name) :
|
|
TQDoubleValidator(tqparent, name)
|
|
{
|
|
}
|
|
|
|
kMyMoneyMoneyValidator::kMyMoneyMoneyValidator( double bottom, double top, int decimals,
|
|
TQObject * tqparent, const char * name ) :
|
|
TQDoubleValidator(bottom, top, decimals, tqparent, name)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* The code of the following function is taken from kdeui/knumvalidator.cpp
|
|
* and adjusted to always use the monetary symbols defined in the KDE control center
|
|
*/
|
|
TQValidator::State kMyMoneyMoneyValidator::validate( TQString & input, int & _p ) const
|
|
{
|
|
TQString s = input;
|
|
KLocale * l = KGlobal::locale();
|
|
// ok, we have to re-format the number to have:
|
|
// 1. decimalSymbol == '.'
|
|
// 2. negativeSign == '-'
|
|
// 3. positiveSign == <empty>
|
|
// 4. thousandsSeparator() == <empty> (we don't check that there
|
|
// are exactly three decimals between each separator):
|
|
TQString d = l->monetaryDecimalSymbol(),
|
|
n = l->negativeSign(),
|
|
p = l->positiveSign(),
|
|
t = l->monetaryThousandsSeparator();
|
|
// first, delete p's and t's:
|
|
if ( !p.isEmpty() )
|
|
for ( int idx = s.tqfind( p ) ; idx >= 0 ; idx = s.tqfind( p, idx ) )
|
|
s.remove( idx, p.length() );
|
|
|
|
|
|
if ( !t.isEmpty() )
|
|
for ( int idx = s.tqfind( t ) ; idx >= 0 ; idx = s.tqfind( t, idx ) )
|
|
s.remove( idx, t.length() );
|
|
|
|
// then, replace the d's and n's
|
|
if ( ( !n.isEmpty() && n.tqfind('.') != -1 ) ||
|
|
( !d.isEmpty() && d.tqfind('-') != -1 ) ) {
|
|
// make sure we don't replace something twice:
|
|
kdWarning() << "KDoubleValidator: decimal symbol tqcontains '-' or "
|
|
"negative sign tqcontains '.' -> improve algorithm" << endl;
|
|
return Invalid;
|
|
}
|
|
|
|
if ( !d.isEmpty() && d != "." )
|
|
for ( int idx = s.tqfind( d ) ; idx >= 0 ; idx = s.tqfind( d, idx + 1 ) )
|
|
s.tqreplace( idx, d.length(), ".");
|
|
|
|
if ( !n.isEmpty() && n != "-" )
|
|
for ( int idx = s.tqfind( n ) ; idx >= 0 ; idx = s.tqfind( n, idx + 1 ) )
|
|
s.tqreplace( idx, n.length(), "-" );
|
|
|
|
// Take care of monetary parens around the value if selected via
|
|
// the locale settings.
|
|
// If the lead-in or lead-out paren is present, remove it
|
|
// before passing the string to the TQDoubleValidator
|
|
if(l->negativeMonetarySignPosition() == KLocale::ParensAround
|
|
|| l->positiveMonetarySignPosition() == KLocale::ParensAround) {
|
|
TQRegExp regExp("^(\\()?([\\d-\\.]*)(\\))?$");
|
|
if(s.tqfind(regExp) != -1) {
|
|
s = regExp.cap(2);
|
|
}
|
|
}
|
|
|
|
// check for non numeric values (TQDoubleValidator allows an 'e', we don't)
|
|
TQRegExp nonNumeric("[^\\d-\\.]+");
|
|
if(s.tqfind(nonNumeric) != -1)
|
|
return Invalid;
|
|
|
|
// check for minus sign trailing the number
|
|
TQRegExp trailingMinus("^([^-]*)\\w*-$");
|
|
if(s.tqfind(trailingMinus) != -1) {
|
|
s = TQString("-%1").tqarg(trailingMinus.cap(1));
|
|
}
|
|
|
|
// check for the maximum allowed number of decimal places
|
|
int decPos = s.tqfind('.');
|
|
if(decPos != -1) {
|
|
if(decimals() == 0)
|
|
return Invalid;
|
|
if(((int)(s.length()) - decPos) > decimals())
|
|
return Invalid;
|
|
}
|
|
|
|
// If we have just a single minus sign, we are done
|
|
if(s == TQString("-"))
|
|
return Acceptable;
|
|
|
|
TQValidator::State rc = TQDoubleValidator::validate( s, _p );
|
|
|
|
if(rc == Acceptable) {
|
|
// If the numeric value is acceptable, we check if the parens
|
|
// are ok. If only the lead-in is present, the return value
|
|
// is intermediate, if only the lead-out is present then it
|
|
// definitely is invalid. Nevertheless, we check for parens
|
|
// only, if the locale settings have it enabled.
|
|
if(l->negativeMonetarySignPosition() == KLocale::ParensAround
|
|
|| l->positiveMonetarySignPosition() == KLocale::ParensAround) {
|
|
int tmp = input.tqcontains('(') - input.tqcontains(')');
|
|
if(tmp > 0)
|
|
rc = Intermediate;
|
|
else if(tmp < 0)
|
|
rc = Invalid;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
kMyMoneyEdit::kMyMoneyEdit(TQWidget *tqparent, const char *name, const int prec)
|
|
: TQHBox(tqparent, name)
|
|
{
|
|
m_prec = prec;
|
|
if(prec < -1 || prec > 20)
|
|
m_prec = KGlobal::locale()->fracDigits();
|
|
init();
|
|
}
|
|
|
|
kMyMoneyEdit::kMyMoneyEdit(const MyMoneySecurity& sec, TQWidget *tqparent, const char *name)
|
|
: TQHBox(tqparent, name)
|
|
{
|
|
m_prec = MyMoneyMoney::denomToPrec(sec.smallestAccountFraction());
|
|
init();
|
|
}
|
|
|
|
// converted image from kde3.5.1/share/apps/kdevdesignerpart/pics/designer_resetproperty.png
|
|
static const uchar resetButtonImage[] = {
|
|
0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,
|
|
0x00,0x00,0x00,0x0D,0x49,0x48,0x44,0x52,
|
|
0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,
|
|
0x08,0x06,0x00,0x00,0x00,0x0F,0x0E,0x84,
|
|
0x76,0x00,0x00,0x00,0x06,0x62,0x4B,0x47,
|
|
0x44,0x00,0xFF,0x00,0xFF,0x00,0xFF,0xA0,
|
|
0xBD,0xA7,0x93,0x00,0x00,0x00,0x09,0x70,
|
|
0x48,0x59,0x73,0x00,0x00,0x0B,0x13,0x00,
|
|
0x00,0x0B,0x13,0x01,0x00,0x9A,0x9C,0x18,
|
|
0x00,0x00,0x00,0x07,0x74,0x49,0x4D,0x45,
|
|
0x07,0xD6,0x06,0x10,0x09,0x36,0x0C,0x58,
|
|
0x91,0x11,0x7C,0x00,0x00,0x00,0x64,0x49,
|
|
0x44,0x41,0x54,0x78,0xDA,0x65,0xC9,0xA1,
|
|
0x0D,0x02,0x41,0x18,0x84,0xD1,0xF7,0x5F,
|
|
0x13,0x04,0x9A,0x39,0x43,0x68,0x81,0x02,
|
|
0x10,0xB8,0x13,0x74,0x80,0xC1,0x21,0x76,
|
|
0x1D,0xDD,0xD0,0x01,0x65,0x10,0x34,0x9A,
|
|
0x0C,0x66,0x83,0x61,0x92,0x2F,0x23,0x5E,
|
|
0x25,0x01,0xBD,0x6A,0xC6,0x1D,0x9B,0x25,
|
|
0x79,0xC2,0x34,0xE0,0x30,0x00,0x56,0xBD,
|
|
0x6A,0x0D,0xD5,0x38,0xE1,0xEA,0x7F,0xE7,
|
|
0x4A,0xA2,0x57,0x1D,0x71,0xC1,0x07,0xBB,
|
|
0x81,0x8F,0x09,0x96,0xE4,0x86,0x3D,0xDE,
|
|
0x78,0x8D,0x48,0xF2,0xAB,0xB1,0x1D,0x9F,
|
|
0xC6,0xFC,0x05,0x46,0x68,0x28,0x6B,0x58,
|
|
0xEE,0x72,0x0A,0x00,0x00,0x00,0x00,0x49,
|
|
0x45,0x4E,0x44,0xAE,0x42,0x60,0x82
|
|
};
|
|
|
|
void kMyMoneyEdit::init(void)
|
|
{
|
|
allowEmpty = false;
|
|
m_edit = new kMyMoneyLineEdit(this, 0, true);
|
|
m_edit->installEventFilter(this);
|
|
setFocusProxy(m_edit);
|
|
|
|
// Yes, just a simple double validator !
|
|
kMyMoneyMoneyValidator *validator = new kMyMoneyMoneyValidator(TQT_TQOBJECT(this));
|
|
m_edit->setValidator(validator);
|
|
m_edit->tqsetAlignment(AlignRight | AlignVCenter);
|
|
|
|
m_calculatorFrame = new TQVBox(this, 0, WType_Popup);
|
|
|
|
m_calculatorFrame->setFrameStyle(TQFrame::PopupPanel | TQFrame::Raised);
|
|
m_calculatorFrame->setLineWidth(3);
|
|
|
|
m_calculator = new kMyMoneyCalculator(m_calculatorFrame);
|
|
m_calculatorFrame->setFixedSize(m_calculator->width()+3, m_calculator->height()+3);
|
|
m_calculatorFrame->hide();
|
|
|
|
m_calcButton = new KPushButton(TQIconSet(TQPixmap(KGlobal::iconLoader()->iconPath("kcalc", -KIcon::SizeSmall))), TQString(""), this);
|
|
m_calcButton->setFixedWidth( m_calcButton->tqsizeHint().width() );
|
|
m_calcButton->setFixedHeight(m_edit->tqsizeHint().height());
|
|
m_calcButton->setFocusProxy(m_edit);
|
|
|
|
TQPixmap pixmap;
|
|
pixmap.loadFromData(resetButtonImage, sizeof(resetButtonImage), "PNG", 0);
|
|
m_resetButton = new KPushButton(TQIconSet(pixmap), TQString(""), this);
|
|
m_resetButton->setFixedWidth( m_resetButton->tqsizeHint().width() );
|
|
m_resetButton->setFixedHeight(m_edit->tqsizeHint().height());
|
|
m_resetButton->setEnabled(false);
|
|
m_resetButton->setFocusProxy(m_edit);
|
|
|
|
KConfig *kconfig = KGlobal::config();
|
|
kconfig->setGroup("General Options");
|
|
if(kconfig->readBoolEntry("DontShowCalculatorButton", false) == true)
|
|
setCalculatorButtonVisible(false);
|
|
|
|
setSpacing(0);
|
|
|
|
connect(m_edit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(theTextChanged(const TQString&)));
|
|
connect(m_calculator, TQT_SIGNAL(signalResultAvailable()), this, TQT_SLOT(slotCalculatorResult()));
|
|
connect(m_calcButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotCalculatorOpen()));
|
|
connect(m_resetButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(resetText()));
|
|
}
|
|
|
|
void kMyMoneyEdit::setValidator(const TQValidator* v)
|
|
{
|
|
m_edit->setValidator(v);
|
|
}
|
|
|
|
kMyMoneyEdit::~kMyMoneyEdit()
|
|
{
|
|
delete m_calculatorFrame;
|
|
}
|
|
|
|
KLineEdit* kMyMoneyEdit::lineedit(void) const
|
|
{
|
|
return m_edit;
|
|
}
|
|
|
|
void kMyMoneyEdit::setPrecision(const int prec)
|
|
{
|
|
if(prec >= -1 && prec <= 20) {
|
|
if(prec != m_prec) {
|
|
m_prec = prec;
|
|
// update current display
|
|
setValue(value());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool kMyMoneyEdit::isValid(void) const
|
|
{
|
|
return !(m_edit->text().isEmpty());
|
|
}
|
|
|
|
MyMoneyMoney kMyMoneyEdit::value(void) const
|
|
{
|
|
TQString txt = m_edit->text();
|
|
ensureFractionalPart(txt);
|
|
MyMoneyMoney money(txt);
|
|
if(m_prec != -1)
|
|
money = money.convert(MyMoneyMoney::precToDenom(m_prec));
|
|
return money;
|
|
}
|
|
|
|
void kMyMoneyEdit::setValue(const MyMoneyMoney& value)
|
|
{
|
|
// load the value into the widget but don't use thousandsSeparators
|
|
TQString txt = value.formatMoney("", m_prec, false);
|
|
loadText(txt);
|
|
}
|
|
|
|
void kMyMoneyEdit::loadText(const TQString& txt)
|
|
{
|
|
m_edit->setText(txt);
|
|
if(isEnabled() && !txt.isEmpty())
|
|
ensureFractionalPart();
|
|
m_text = m_edit->text();
|
|
m_resetButton->setEnabled(false);
|
|
}
|
|
|
|
void kMyMoneyEdit::clearText(void)
|
|
{
|
|
m_text = TQString();
|
|
m_edit->setText(m_text);
|
|
}
|
|
|
|
void kMyMoneyEdit::resetText(void)
|
|
{
|
|
m_edit->setText(m_text);
|
|
m_resetButton->setEnabled(false);
|
|
}
|
|
|
|
void kMyMoneyEdit::theTextChanged(const TQString & theText)
|
|
{
|
|
KLocale * l = KGlobal::locale();
|
|
TQString d = l->monetaryDecimalSymbol();
|
|
TQString l_text = theText;
|
|
TQString nsign, psign;
|
|
if(l->negativeMonetarySignPosition() == KLocale::ParensAround
|
|
|| l->positiveMonetarySignPosition() == KLocale::ParensAround) {
|
|
nsign = psign = "(";
|
|
} else {
|
|
nsign = l->negativeSign();
|
|
psign = l->positiveSign();
|
|
}
|
|
|
|
int i = 0;
|
|
if(isEnabled()) {
|
|
TQValidator::State state = m_edit->validator()->validate( l_text, i);
|
|
if(state == TQValidator::Intermediate) {
|
|
if(l_text.length() == 1) {
|
|
if(l_text != d && l_text != nsign && l_text != psign && l_text != "-")
|
|
state = TQValidator::Invalid;
|
|
}
|
|
}
|
|
if (state==TQValidator::Invalid)
|
|
m_edit->setText(previousText);
|
|
else {
|
|
previousText = l_text;
|
|
emit textChanged(m_edit->text());
|
|
m_resetButton->setEnabled(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void kMyMoneyEdit::ensureFractionalPart(void)
|
|
{
|
|
TQString s(m_edit->text());
|
|
ensureFractionalPart(s);
|
|
m_edit->setText(s);
|
|
}
|
|
|
|
void kMyMoneyEdit::ensureFractionalPart(TQString& s) const
|
|
{
|
|
|
|
KLocale* locale = KGlobal::locale();
|
|
TQString decimalSymbol = locale->monetaryDecimalSymbol();
|
|
if(decimalSymbol.isEmpty())
|
|
decimalSymbol = ".";
|
|
|
|
// If text contains no 'monetaryDecimalSymbol' then add it
|
|
// followed by the required number of 0s
|
|
if (!s.isEmpty()) {
|
|
if(m_prec > 0) {
|
|
if (!s.tqcontains(decimalSymbol)) {
|
|
s += decimalSymbol;
|
|
for (int i=0; i < m_prec; i++)
|
|
s += "0";
|
|
}
|
|
} else if(m_prec == 0) {
|
|
while(s.tqcontains(decimalSymbol)) {
|
|
int pos = s.tqfindRev(decimalSymbol);
|
|
if(pos != -1) {
|
|
s.truncate(pos);
|
|
}
|
|
}
|
|
} else if(s.tqcontains(decimalSymbol)) { // m_prec == -1 && fraction
|
|
// no trailing zeroes
|
|
while(s.endsWith("0")) {
|
|
s.truncate(s.length()-1);
|
|
}
|
|
// no trailing decimalSymbol
|
|
if(s.endsWith(decimalSymbol))
|
|
s.truncate(s.length()-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool kMyMoneyEdit::eventFilter(TQObject * /* o */ , TQEvent *e )
|
|
{
|
|
bool rc = false;
|
|
|
|
// we want to catch some keys that are usually handled by
|
|
// the base class (e.g. '+', '-', etc.)
|
|
if(e->type() == TQEvent::KeyPress) {
|
|
TQKeyEvent *k = TQT_TQKEYEVENT (e);
|
|
|
|
rc = true;
|
|
switch(k->key()) {
|
|
case TQt::Key_Plus:
|
|
case TQt::Key_Minus:
|
|
if(m_edit->hasSelectedText()) {
|
|
m_edit->cut();
|
|
}
|
|
if(m_edit->text().length() == 0) {
|
|
rc = false;
|
|
break;
|
|
}
|
|
// in case of '-' we do not enter the calculator when
|
|
// the current position is the beginning and there is
|
|
// no '-' sign at the first position.
|
|
if(k->key() == TQt::Key_Minus) {
|
|
if(m_edit->cursorPosition() == 0 && m_edit->text()[0] != '-') {
|
|
rc = false;
|
|
break;
|
|
}
|
|
}
|
|
// otherwise, tricky fall through here!
|
|
|
|
case TQt::Key_Slash:
|
|
case TQt::Key_Asterisk:
|
|
case TQt::Key_Percent:
|
|
if(m_edit->hasSelectedText()) {
|
|
// remove the selected text
|
|
m_edit->cut();
|
|
}
|
|
calculatorOpen(k);
|
|
break;
|
|
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
} else if(e->type() == TQEvent::FocusOut) {
|
|
if(!m_edit->text().isEmpty() || !allowEmpty)
|
|
ensureFractionalPart();
|
|
|
|
if(MyMoneyMoney(m_edit->text()) != MyMoneyMoney(m_text)
|
|
&& !m_calculator->isVisible()) {
|
|
emit valueChanged(m_edit->text());
|
|
}
|
|
m_text = m_edit->text();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void kMyMoneyEdit::slotCalculatorOpen(void)
|
|
{
|
|
calculatorOpen(0);
|
|
}
|
|
|
|
void kMyMoneyEdit::calculatorOpen(TQKeyEvent* k)
|
|
{
|
|
m_calculator->setInitialValues(m_edit->text(), k);
|
|
|
|
int h = m_calculatorFrame->height();
|
|
int w = m_calculatorFrame->width();
|
|
|
|
// usually, the calculator widget is shown underneath the MoneyEdit widget
|
|
// if it does not fit on the screen, we show it above this widget
|
|
TQPoint p = mapToGlobal(TQPoint(0,0));
|
|
if(p.y() + height() + h > TQApplication::desktop()->height())
|
|
p.setY(p.y() - h);
|
|
else
|
|
p.setY(p.y() + height());
|
|
|
|
// usually, it is shown left aligned. If it does not fit, we align it
|
|
// to the right edge of the widget
|
|
if(p.x() + w > TQApplication::desktop()->width())
|
|
p.setX(p.x() + width() - w);
|
|
|
|
TQRect r = m_calculator->tqgeometry();
|
|
r.moveTopLeft(p);
|
|
m_calculatorFrame->setGeometry(r);
|
|
m_calculatorFrame->show();
|
|
m_calculator->setFocus();
|
|
}
|
|
|
|
void kMyMoneyEdit::slotCalculatorResult(void)
|
|
{
|
|
TQString result;
|
|
if(m_calculator != 0) {
|
|
m_calculatorFrame->hide();
|
|
m_edit->setText(m_calculator->result());
|
|
ensureFractionalPart();
|
|
emit valueChanged(m_edit->text());
|
|
m_text = m_edit->text();
|
|
}
|
|
}
|
|
|
|
TQWidget* kMyMoneyEdit::tqfocusWidget(void) const
|
|
{
|
|
TQWidget* w = m_edit;
|
|
while(w->focusProxy())
|
|
w = TQT_TQWIDGET(w->focusProxy());
|
|
return w;
|
|
}
|
|
|
|
void kMyMoneyEdit::setCalculatorButtonVisible(const bool show)
|
|
{
|
|
m_calcButton->setShown(show);
|
|
}
|
|
|
|
void kMyMoneyEdit::setResetButtonVisible(const bool show)
|
|
{
|
|
m_resetButton->setShown(show);
|
|
}
|
|
|
|
void kMyMoneyEdit::setAllowEmpty(bool allowed)
|
|
{
|
|
allowEmpty = allowed;
|
|
}
|
|
|
|
bool kMyMoneyEdit::isCalculatorButtonVisible(void) const
|
|
{
|
|
return m_calcButton->isVisible();
|
|
}
|
|
|
|
bool kMyMoneyEdit::isResetButtonVisible(void) const
|
|
{
|
|
return m_resetButton->isVisible();
|
|
}
|
|
|
|
bool kMyMoneyEdit::isEmptyAllowed(void) const
|
|
{
|
|
return allowEmpty;
|
|
}
|
|
|
|
void kMyMoneyEdit::setHint(const TQString& hint) const
|
|
{
|
|
if(m_edit)
|
|
m_edit->setHint(hint);
|
|
}
|
|
|
|
bool kMyMoneyEdit::isReadOnly(void) const
|
|
{
|
|
if(m_edit)
|
|
return m_edit->isReadOnly();
|
|
return false;
|
|
}
|
|
|
|
void kMyMoneyEdit::setReadOnly(bool readOnly)
|
|
{
|
|
// we use the TQLineEdit::setReadOnly() method directly to avoid
|
|
// changing the background between readonly and read/write mode
|
|
// as it is done by the KLineEdit code.
|
|
if(m_edit)
|
|
m_edit->TQLineEdit::setReadOnly(readOnly);
|
|
}
|
|
|
|
#include "kmymoneyedit.moc"
|