kexi

kexiinputtableedit.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
00003    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00004 
00005    This program is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this program; see the file COPYING.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kexiinputtableedit.h"
00022 
00023 #include <kexidb/field.h>
00024 
00025 #include <qregexp.h>
00026 #include <qevent.h>
00027 #include <qlayout.h>
00028 #include <qtimer.h>
00029 #include <qpainter.h>
00030 #include <qapplication.h>
00031 #include <qclipboard.h>
00032 #include <qtooltip.h>
00033 
00034 #include <kglobal.h>
00035 #include <klocale.h>
00036 #include <kdebug.h>
00037 #include <kglobalsettings.h>
00038 #include <kcompletionbox.h>
00039 #include <knumvalidator.h>
00040 #include <kexiutils/longlongvalidator.h>
00041 
00043 class MyLineEdit : public KLineEdit
00044 {
00045     public:
00046         MyLineEdit(QWidget *parent, const char *name) : KLineEdit(parent,name)
00047         {}
00048     protected:
00049         virtual void drawFrame ( QPainter * p ) {
00050             p->setPen( QPen( colorGroup().text() ) );
00051             QRect r = rect();
00052             p->moveTo( r.topLeft() );
00053             p->lineTo( r.topRight() );
00054             p->lineTo( r.bottomRight() );
00055             p->lineTo( r.bottomLeft() );
00056             if (pos().x() == 0) //draw left side only when it is @ the edge
00057                 p->lineTo( r.topLeft() );
00058         }
00059 };
00060 
00061 //======================================================
00062 
00063 KexiInputTableEdit::KexiInputTableEdit(KexiTableViewColumn &column, QWidget *parent)
00064  : KexiTableEdit(column, parent)
00065 {
00066     setName("KexiInputTableEdit");
00067 //  m_type = f.type(); //copied because the rest of code uses m_type
00068 //  m_field = &f;
00069 //  m_origValue = value;//original value
00070     init();
00071 }
00072 
00073 KexiInputTableEdit::~KexiInputTableEdit()
00074 {
00075 }
00076 
00077 void KexiInputTableEdit::init()
00078 {
00079     kdDebug() << "KexiInputTableEdit: m_origValue.typeName()==" << m_origValue.typeName() << endl;
00080     kdDebug() << "KexiInputTableEdit: type== " << field()->typeName() << endl;
00081     kdDebug() << "KexiInputTableEdit: displayed type== " << displayedField()->typeName() << endl;
00082 
00083     //init settings
00084     m_decsym = KGlobal::locale()->decimalSymbol();
00085     if (m_decsym.isEmpty())
00086         m_decsym=".";//default
00087 
00088     const bool align_right = displayedField()->isNumericType();
00089 
00090     if (!align_right) {
00091         //create layer for internal editor
00092         QHBoxLayout *lyr = new QHBoxLayout(this);
00093         lyr->addSpacing(4);
00094         lyr->setAutoAdd(true);
00095     }
00096 
00097     //create internal editor
00098     m_lineedit = new MyLineEdit(this, "KexiInputTableEdit-KLineEdit");
00099     setViewWidget(m_lineedit);
00100     if (align_right)
00101         m_lineedit->setAlignment(AlignRight);
00102 //  m_cview->setFrame(false);
00103 //  m_cview->setFrameStyle( QFrame::Plain | QFrame::Box );
00104 //  m_cview->setLineWidth( 1 );
00105     m_calculatedCell = false;
00106 
00107 #if 0 //js TODO
00108     connect(m_cview->completionBox(), SIGNAL(activated(const QString &)),
00109      this, SLOT(completed(const QString &)));
00110     connect(m_cview->completionBox(), SIGNAL(highlighted(const QString &)),
00111      this, SLOT(completed(const QString &)));
00112      m_cview->completionBox()->setTabHandling(true);
00113 #endif
00114 
00115 }
00116 
00117 void KexiInputTableEdit::setValueInternal(const QVariant& add, bool removeOld)
00118 {
00119     QString text( valueToText(removeOld ? QVariant() : m_origValue, add.toString(), true/*setValidator*/) );
00120 
00121     if (text.isEmpty()) {
00122         if (m_origValue.toString().isEmpty()) {
00123             //we have to set NULL initial value:
00124             m_lineedit->setText(QString::null);
00125         }
00126     }
00127     else {
00128         m_lineedit->setText(text);
00129     }
00130 
00131 #if 0
00132 //move to end is better by default
00133         m_cview->selectAll();
00134 #else
00135 //js TODO: by default we're moving to the end of editor, ADD OPTION allowing "select all chars"
00136         m_lineedit->end(false);
00137 #endif
00138 }
00139 
00140 QString KexiInputTableEdit::valueToText(const QVariant& value, const QString& add, bool setValidator)
00141 {
00142     QString text; //result
00143 
00144     if (field()->isFPNumericType()) {
00147         text = QString::number(value.toDouble(), 'f', 
00148             QMAX(field()->visibleDecimalPlaces(), 10)); //<-- 10 is quite good maximum for fractional digits 
00150         if (value.toDouble() == 0.0) {
00151             text = add.isEmpty() ? "0" : add; //eat 0
00152         }
00153         else {
00155             QStringList sl = QStringList::split(".", text);
00156             if (text.isEmpty()) {
00157                 //nothing
00158             }
00159             else if (sl.count()==2) {
00160                 kdDebug() << "sl.count()=="<<sl.count()<< " " <<sl[0] << " | " << sl[1] << endl;
00161                 const QString sl1 = sl[1];
00162                 int pos = sl1.length()-1;
00163                 if (pos>=1) {
00164                     for (;pos>=0 && sl1[pos]=='0';pos--)
00165                         ;
00166                     pos++;
00167                 }
00168                 if (pos>0)
00169                     text = sl[0] + m_decsym + sl1.left(pos);
00170                 else
00171                     text = sl[0]; //no decimal point
00172             }
00173             text += add;
00174         }
00175         if (setValidator) {
00176             QValidator *validator = new KDoubleValidator(m_lineedit);
00177             m_lineedit->setValidator( validator );
00178         }
00179     }
00180     else {
00181         text = value.toString();
00182         if (field()->isIntegerType()) {
00183             if (value.toInt() == 0) {
00184                 text = add; //eat 0
00185             }
00186             else {
00187                 text += add;
00188             }
00190             if (setValidator) {
00191                 QValidator *validator;
00192                 if (KexiDB::Field::BigInteger == field()->type()) {
00194                     validator = new KexiUtils::LongLongValidator(m_lineedit); 
00195                 }
00196                 else {
00197                     validator = new KIntValidator(m_lineedit);
00198                 }
00199                 m_lineedit->setValidator( validator );
00200             }
00201         }
00202         else {//default: text
00203             text += add;
00204         }
00205     }
00206 
00207     return text;
00208 }
00209 
00210 void KexiInputTableEdit::paintEvent ( QPaintEvent * /*e*/ )
00211 {
00212     QPainter p(this);
00213     p.setPen( QPen( colorGroup().text() ) );
00214     p.drawRect( rect() );
00215 }
00216 
00217 void
00218 KexiInputTableEdit::setRestrictedCompletion()
00219 {
00220 #if 0 //js TODO
00221 kdDebug() << "KexiInputTableEdit::setRestrictedCompletion()" << endl;
00222 //  KLineEdit *content = static_cast<KLineEdit*>(m_view);
00223     if(m_cview->text().isEmpty())
00224         return;
00225 
00226     kdDebug() << "KexiInputTableEdit::setRestrictedCompletion(): something to do" << endl;
00227 
00228     m_cview->useGlobalKeyBindings();
00229 
00230     QStringList newC;
00231     QStringList::ConstIterator it, end( m_comp.constEnd() );
00232     for( it = m_comp.constBegin(); it != end; ++it)
00233     {
00234         if((*it).startsWith(m_cview->text()))
00235             newC.append(*it);
00236     }
00237     m_cview->setCompletedItems(newC);
00238 #endif
00239 }
00240 
00241 void
00242 KexiInputTableEdit::completed(const QString &s)
00243 {
00244     kdDebug() << "KexiInputTableEdit::completed(): " << s << endl;
00245     m_lineedit->setText(s);
00246 }
00247 
00248 bool KexiInputTableEdit::valueChanged()
00249 {
00250     //not needed? if (m_lineedit->text()!=m_origValue.toString())
00251     //not needed?   return true;
00252     return KexiTableEdit::valueChanged();
00253 }
00254 
00255 bool KexiInputTableEdit::valueIsNull()
00256 {
00257     return m_lineedit->text().isNull();
00258 }
00259 
00260 bool KexiInputTableEdit::valueIsEmpty()
00261 {
00262     return !m_lineedit->text().isNull() && m_lineedit->text().isEmpty();
00263 }
00264 
00265 QVariant KexiInputTableEdit::value()
00266 {
00267     if (field()->isFPNumericType()) {//==KexiDB::Field::Double || m_type==KexiDB::Field::Float) {
00269         QString txt = m_lineedit->text();
00270         if (m_decsym!=".")
00271             txt = txt.replace(m_decsym,".");//convert back
00272         bool ok;
00273         const double result = txt.toDouble(&ok);
00274         return ok ? QVariant(result) : QVariant();
00275     }
00276     else if (field()->isIntegerType()) {
00278         bool ok;
00279         if (KexiDB::Field::BigInteger == field()->type()) {
00280             if (field()->isUnsigned()) {
00281                 const Q_ULLONG result = m_lineedit->text().toULongLong(&ok);
00282                 return ok ? QVariant(result) : QVariant();
00283             }
00284             else {
00285                 const Q_LLONG result = m_lineedit->text().toLongLong(&ok);
00286                 return ok ? QVariant(result) : QVariant();
00287             }
00288         }
00289         if (KexiDB::Field::Integer == field()->type()) {
00290             if (field()->isUnsigned()) {
00291                 const uint result = m_lineedit->text().toUInt(&ok);
00292                 return ok ? QVariant(result) : QVariant();
00293             }
00294         }
00295         //default: signed int
00296         const int result = m_lineedit->text().toInt(&ok);
00297         return ok ? QVariant(result) : QVariant();
00298     }
00299     //default: text
00300     return QVariant( m_lineedit->text() );
00301 }
00302 #if 0
00303     //let qt&mysql understand what we mean... (numeric values)
00304     QString v;
00305     switch(m_type)
00306     {
00307         case QVariant::UInt:
00308         case QVariant::Int:
00309         case QVariant::Double:
00310 //          QString v;
00311             if(!m_calculatedCell)
00312             {
00313                 v = m_cview->text().replace(QRegExp("\\" + KGlobal::locale()->thousandsSeparator()), "");
00314                 v = v.replace(QRegExp("\\" + KGlobal::locale()->decimalSymbol()), ".");
00315                 v = v.replace(QRegExp("\\" + KGlobal::locale()->negativeSign()), "-");
00316                 kdDebug() << "KexiInputTableEdit::value() converting => " << v.latin1() << endl;
00317                 return QVariant(v);
00318             }
00319             else
00320             {
00321                 //ok here should the formula be parsed so, just feel like in perl :)
00322                 kdDebug() << "KexiInputTableEdit::value() calculating..." << endl;
00323                 double result = 0;
00324                 QString real = m_cview->text().right(m_cview->text().length() - 1);
00325                 real = real.replace(QRegExp("\\" + KGlobal::locale()->thousandsSeparator()), "");
00326                 real = real.replace(QRegExp("\\" + KGlobal::locale()->decimalSymbol()), ".");
00327                 QStringList values = QStringList::split(QRegExp("[\\+|\\*|\\/|-]"), real, false);
00328                 QStringList ops = QStringList::split(QRegExp("[0-9]{1,8}(?:\\.[0-9]+)?"), real, false);
00329 
00330                 double lastValue = 0;
00331                 QString lastOp = "";
00332                 for(int i=0; i < (int)values.count(); i++)
00333                 {
00334                     double next;
00335 
00336                     QString op = QString((*ops.at(i))).stripWhiteSpace();
00337 
00338                     if(!((*values.at(i+1)).isEmpty()) && i == 0)
00339                     {
00340                         double local = (*values.at(i)).toDouble();
00341                         next = (*values.at(i+1)).toDouble();
00342 
00343                         QString op = (*ops.at(i));
00344                         if(op == "+")
00345                             result = local + next;
00346                         else if(op == "-")
00347                             result = local - next;
00348                         else if(op == "*")
00349                             result = local * next;
00350                         else
00351                             result = local / next;
00352                     }
00353                     else if(!(*values.at(i+1)).isEmpty())
00354                     {
00355                         next = (*values.at(i+1)).toDouble();
00356 
00357                         QString op = QString((*ops.at(i))).stripWhiteSpace();
00358                         if(op == "+")
00359                             result = result + next;
00360                         else if(op == "-")
00361                             result = result - next;
00362                         else if(op == "*")
00363                             result = result * next;
00364                         else
00365                             result = result / next;
00366                     }
00367 
00368                 }
00369 
00370                 return QVariant(result);
00371 
00372             }
00373             break;
00374 
00375         default:
00376             kdDebug() << "KexiInputTableEdit::value() default..." << endl;
00377             return QVariant(m_cview->text());
00378     }
00379 
00380 void
00381 KexiInputTableEdit::end(bool mark)
00382 {
00383     m_cview->end(mark);
00384 }
00385 
00386 void
00387 KexiInputTableEdit::backspace()
00388 {
00389     m_cview->backspace();
00390 }
00391 #endif
00392 
00393 
00394 void
00395 KexiInputTableEdit::clear()
00396 {
00397     m_lineedit->clear();
00398 }
00399 
00400 bool KexiInputTableEdit::cursorAtStart()
00401 {
00402     return m_lineedit->cursorPosition()==0;
00403 }
00404 
00405 bool KexiInputTableEdit::cursorAtEnd()
00406 {
00407     return m_lineedit->cursorPosition()==(int)m_lineedit->text().length();
00408 }
00409 
00410 QSize KexiInputTableEdit::totalSize()
00411 {
00412     if (!m_lineedit)
00413         return size();
00414     return m_lineedit->size();
00415 }
00416 
00417 void KexiInputTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
00418 {
00419     Q_UNUSED(visibleValue);
00421     qApp->clipboard()->setText( valueToText(value, QString::null) );
00422 }
00423 
00424 void KexiInputTableEdit::handleAction(const QString& actionName)
00425 {
00426     const bool alreadyVisible = m_lineedit->isVisible();
00427 
00428     if (actionName=="edit_paste") {
00429         if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
00430             emit editRequested();
00431             m_lineedit->clear();
00432         }
00433         m_lineedit->paste();
00434     }
00435     else if (actionName=="edit_cut") {
00437         if (!alreadyVisible) { //cut the entire text if the cell was not in edit mode
00438             emit editRequested();
00439             m_lineedit->selectAll();
00440         }
00441         m_lineedit->cut();
00442     }
00443 }
00444 
00445 bool KexiInputTableEdit::showToolTipIfNeeded(const QVariant& value, const QRect& rect, 
00446     const QFontMetrics& fm, bool focused)
00447 {
00448     QString text( value.type()==QVariant::String ? value.toString()
00449         : valueToText(value, QString::null, false ) );
00450     QRect internalRect(rect);
00451     internalRect.setLeft(rect.x()+leftMargin());
00452     internalRect.setWidth(internalRect.width()-rightMargin(focused)-2*3);
00453     kexidbg << rect << " " << internalRect << " " << fm.width(text) << endl;
00454     return fm.width(text) > internalRect.width();
00455 }
00456 
00457 void KexiInputTableEdit::moveCursorToEnd()
00458 {
00459     m_lineedit->end(false);
00460 }
00461 
00462 void KexiInputTableEdit::moveCursorToStart()
00463 {
00464     m_lineedit->home(false);
00465 }
00466 
00467 void KexiInputTableEdit::selectAll()
00468 {
00469     m_lineedit->selectAll();
00470 }
00471 
00472 KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiInputEditorFactoryItem, KexiInputTableEdit)
00473 
00474 #include "kexiinputtableedit.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys