kexi

kexicomboboxtableedit.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002   Peter Simonsson <psn@linux.se>
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 <qlayout.h>
00022 #include <qstyle.h>
00023 #include <qwindowsstyle.h>
00024 #include <qpainter.h>
00025 #include <qapplication.h>
00026 #include <qclipboard.h>
00027 
00028 #include "kexicomboboxtableedit.h"
00029 #include <widget/utils/kexicomboboxdropdownbutton.h>
00030 #include "kexicomboboxpopup.h"
00031 #include "kexitableview.h"
00032 #include "kexitableitem.h"
00033 #include "kexi.h"
00034 
00035 #include <klineedit.h>
00036 
00038 class KexiComboBoxTableEdit::Private
00039 {
00040 public:
00041     Private()
00042      : popup(0)
00043      , currentEditorWidth(0)
00044      , visibleTableViewColumn(0)
00045      , internalEditor(0)
00046     {
00047     }
00048     ~Private()
00049     {
00050         delete internalEditor;
00051         delete visibleTableViewColumn;
00052     }
00053 
00054     KPushButton *button;
00055     KexiComboBoxPopup *popup;
00056     int currentEditorWidth;
00057     QSize totalSize;
00058     KexiTableViewColumn* visibleTableViewColumn;
00059     KexiTableEdit* internalEditor;
00060 };
00061 
00062 //======================================================
00063 
00064 KexiComboBoxTableEdit::KexiComboBoxTableEdit(KexiTableViewColumn &column, QWidget *parent)
00065  : KexiInputTableEdit(column, parent)
00066  , KexiComboBoxBase()
00067  , d(new Private())
00068 {
00069     setName("KexiComboBoxTableEdit");
00070     m_setVisibleValueOnSetValueInternal = true;
00071     d->button = new KexiComboBoxDropDownButton( parentWidget() /*usually a viewport*/ );
00072     d->button->hide();
00073     d->button->setFocusPolicy( NoFocus );
00074     connect(d->button, SIGNAL(clicked()), this, SLOT(slotButtonClicked()));
00075 
00076     connect(m_lineedit, SIGNAL(textChanged(const QString&)), this, SLOT(slotLineEditTextChanged(const QString&)));
00077 
00078 //  m_lineedit = new KLineEdit(this, "lineedit");
00079 //  m_lineedit->setFrame(false);
00080 //  m_lineedit->setFrameStyle( QFrame::Plain | QFrame::Box );
00081 //  m_lineedit->setLineWidth( 1 );
00082 //  if (f.isNumericType()) {
00083 //      m_lineedit->setAlignment(AlignRight);
00084 //  }
00085 //  setView( m_lineedit );
00086 
00087 //  layout->addWidget(m_view);
00088 //  m_combo->setEditable( true );
00089 //  m_combo->clear();
00090 //  m_combo->insertStringList(f.enumHints());
00091 //  QStringList::ConstIterator it, end( f.enumHints().constEnd() );
00092 //  for ( it = f.enumHints().constBegin(); it != end; ++it) {
00093 //      if(!hints.at(i).isEmpty())
00094 //          m_combo->insertItem(hints.at(i));
00095 //  }
00096 
00097 //js:   TODO
00098 //js    static_cast<KComboBox*>(m_view)->insertStringList(list);
00099 //js    static_cast<KComboBox*>(m_view)->setCurrentItem(static_cast<int>(t));
00100 }
00101 
00102 KexiComboBoxTableEdit::~KexiComboBoxTableEdit()
00103 {
00104     delete d;
00105 }
00106 
00107 void KexiComboBoxTableEdit::createInternalEditor(KexiDB::QuerySchema& schema)
00108 {
00109     if (!m_column->visibleLookupColumnInfo || d->visibleTableViewColumn/*sanity*/)
00110         return;
00111     const KexiDB::Field::Type t = m_column->visibleLookupColumnInfo->field->type();
00113     KexiCellEditorFactoryItem *item = KexiCellEditorFactory::item(t);
00114     if (!item || item->className()=="KexiInputTableEdit")
00115         return; //unsupported type or there is no need to use subeditor for KexiInputTableEdit
00116     //special cases: BLOB, Bool datatypes
00117 //todo
00118     //find real type to display
00119     KexiDB::QueryColumnInfo *ci = m_column->visibleLookupColumnInfo;
00120     KexiDB::QueryColumnInfo *visibleLookupColumnInfo = 0;
00121     if (ci->indexForVisibleLookupValue() != -1) {
00122         //Lookup field is defined
00123         visibleLookupColumnInfo = schema.expandedOrInternalField( ci->indexForVisibleLookupValue() );
00124     }
00125     d->visibleTableViewColumn = new KexiTableViewColumn(schema, *ci, visibleLookupColumnInfo);
00127     d->internalEditor = KexiCellEditorFactory::createEditor(*d->visibleTableViewColumn, 0);
00128     m_lineedit->hide();
00129 }
00130 
00131 KexiComboBoxPopup *KexiComboBoxTableEdit::popup() const
00132 {
00133     return d->popup;
00134 }
00135 
00136 void KexiComboBoxTableEdit::setPopup(KexiComboBoxPopup *popup)
00137 {
00138     d->popup = popup;
00139 }
00140 
00141 void KexiComboBoxTableEdit::showFocus( const QRect& r, bool readOnly )
00142 {
00143 //  d->button->move( pos().x()+ width(), pos().y() );
00144     updateFocus( r );
00145     d->button->setEnabled(!readOnly);
00146     if (readOnly)
00147         d->button->hide();
00148     else
00149         d->button->show();
00150 }
00151 
00152 void KexiComboBoxTableEdit::resize(int w, int h)
00153 {
00154     d->totalSize = QSize(w,h);
00155     if (!column()->isReadOnly()) {
00156         d->button->resize( h, h );
00157         QWidget::resize(w - d->button->width(), h);
00158     }
00159     m_rightMarginWhenFocused = m_rightMargin + (column()->isReadOnly() ? 0 : d->button->width());
00160     QRect r( pos().x(), pos().y(), w+1, h+1 );
00161     if (m_scrollView)
00162         r.moveBy(m_scrollView->contentsX(), m_scrollView->contentsY());
00163     updateFocus( r );
00164     if (popup()) {
00165         popup()->updateSize();
00166     }
00167 }
00168 
00169 // internal
00170 void KexiComboBoxTableEdit::updateFocus( const QRect& r )
00171 {
00172     if (!column()->isReadOnly()) {
00173         if (d->button->width() > r.width())
00174             moveChild(d->button, r.right() + 1, r.top());
00175         else
00176             moveChild(d->button, r.right() - d->button->width(), r.top() );
00177     }
00178 }
00179 
00180 void KexiComboBoxTableEdit::hideFocus()
00181 {
00182     d->button->hide();
00183 }
00184 
00185 QVariant KexiComboBoxTableEdit::visibleValue()
00186 {
00187     return KexiComboBoxBase::visibleValue();
00188 /*  KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00189     if (!popup() || !lookupFieldSchema)
00190         return QVariant();
00191     KexiTableItem *it = popup()->tableView()->selectedItem();
00192     return it ? it->at( lookupFieldSchema->visibleColumn() ) : QVariant();*/
00193 }
00194 
00195 void KexiComboBoxTableEdit::clear()
00196 {
00197     m_lineedit->clear();
00198     KexiComboBoxBase::clear();
00199 }
00200 
00201 bool KexiComboBoxTableEdit::valueChanged()
00202 {
00203     const tristate res = valueChangedInternal();
00204     if (~res) //no result: just compare values
00205         return KexiInputTableEdit::valueChanged();
00206     return res == true;
00207 }
00208 
00209 void KexiComboBoxTableEdit::paintFocusBorders( QPainter *p, QVariant &, int x, int y, int w, int h )
00210 {
00211 //  d->currentEditorWidth = w;
00212     if (!column()->isReadOnly()) {
00213         if (w > d->button->width())
00214             w -= d->button->width();
00215     }
00216     p->drawRect(x, y, w, h);
00217 }
00218 
00219 void KexiComboBoxTableEdit::setupContents( QPainter *p, bool focused, const QVariant& val, 
00220     QString &txt, int &align, int &x, int &y_offset, int &w, int &h  )
00221 {
00222     if (d->internalEditor) {
00223         d->internalEditor->setupContents( p, focused, val, txt, align, x, y_offset, w, h );
00224     }
00225     else {
00226         KexiInputTableEdit::setupContents( p, focused, val, txt, align, x, y_offset, w, h );
00227     }
00228     if (!column()->isReadOnly() && focused && (w > d->button->width()))
00229         w -= (d->button->width() - x);
00230     if (!val.isNull()) {
00231         KexiTableViewData *relData = column()->relatedData();
00232         KexiDB::LookupFieldSchema *lookupFieldSchema = 0;
00233         if (relData) {
00234             int rowToHighlight;
00235             txt = valueForString(val.toString(), &rowToHighlight, 0, 1);
00236         }
00237         else if ((lookupFieldSchema = this->lookupFieldSchema())) {
00238         /* handled at at KexiTableView level
00239             if (popup()) {
00240                 KexiTableItem *it = popup()->tableView()->selectedItem();
00241                 if (it && lookupFieldSchema->visibleColumn()!=-1 && (int)it->size() >= lookupFieldSchema->visibleColumn())
00242                     txt = it->at( lookupFieldSchema->visibleColumn() ).toString();
00243             }*/
00244         }
00245         else {
00246             //use 'enum hints' model
00247             txt = field()->enumHint( val.toInt() );
00248         }
00249     }
00250 }
00251 
00252 void KexiComboBoxTableEdit::slotButtonClicked()
00253 {
00254     // this method is sometimes called by hand: 
00255     // do not allow to simulate clicks when the button is disabled
00256     if (column()->isReadOnly() || !d->button->isEnabled())
00257         return;
00258 
00259     if (m_mouseBtnPressedWhenPopupVisible) {
00260         m_mouseBtnPressedWhenPopupVisible = false;
00261         d->button->setOn(false);
00262         return;
00263     }
00264     kdDebug() << "KexiComboBoxTableEdit::slotButtonClicked()" << endl;
00265     if (!popup() || !popup()->isVisible()) {
00266         kdDebug() << "SHOW POPUP" << endl;
00267         showPopup();
00268         d->button->setOn(true);
00269     }
00270 }
00271 
00272 void KexiComboBoxTableEdit::slotPopupHidden()
00273 {
00274     d->button->setOn(false);
00275 //  d->currentEditorWidth = 0;
00276 }
00277 
00278 void KexiComboBoxTableEdit::updateButton()
00279 {
00280     d->button->setOn(popup()->isVisible());
00281 }
00282 
00283 void KexiComboBoxTableEdit::hide()
00284 {
00285     KexiInputTableEdit::hide();
00286     KexiComboBoxBase::hide();
00287     d->button->setOn(false);
00288 }
00289 
00290 void KexiComboBoxTableEdit::show()
00291 {
00292     KexiInputTableEdit::show();
00293     if (!column()->isReadOnly()) {
00294         d->button->show();
00295     }
00296 }
00297 
00298 bool KexiComboBoxTableEdit::handleKeyPress( QKeyEvent *ke, bool editorActive )
00299 {
00300     const int k = ke->key();
00301     if ((ke->state()==NoButton && k==Qt::Key_F4)
00302         || (ke->state()==AltButton && k==Qt::Key_Down))
00303     {
00304         //show popup
00305         slotButtonClicked();
00306         return true;
00307     }
00308     else if (editorActive) {
00309         const bool enterPressed = k==Qt::Key_Enter || k==Qt::Key_Return;
00310         if (enterPressed && m_internalEditorValueChanged) {
00311             createPopup(false);
00312             selectItemForEnteredValueInLookupTable( m_userEnteredValue );
00313             return false;
00314         }
00315 
00316         return handleKeyPressForPopup( ke );
00317     }
00318 
00319     return false;
00320 }
00321 
00322 void KexiComboBoxTableEdit::slotLineEditTextChanged(const QString& s)
00323 {
00324     slotInternalEditorValueChanged(s);
00325 }
00326 
00327 int KexiComboBoxTableEdit::widthForValue( QVariant &val, const QFontMetrics &fm )
00328 {
00329     KexiTableViewData *relData = column() ? column()->relatedData() : 0;
00330     if (lookupFieldSchema() || relData) {
00331         // in 'lookupFieldSchema' or  or 'related table data' model 
00332         // we're assuming val is already the text, not the index
00334         return QMAX(KEXITV_MINIMUM_COLUMN_WIDTH, fm.width(val.toString()));
00335     }
00336     //use 'enum hints' model
00337     QValueVector<QString> hints = field()->enumHints();
00338     bool ok;
00339     int idx = val.toInt(&ok);
00340     if (!ok || idx < 0 || idx > int(hints.size()-1))
00341         return KEXITV_MINIMUM_COLUMN_WIDTH;
00342     QString txt = hints.at( idx, &ok );
00343     if (!ok)
00344         return KEXITV_MINIMUM_COLUMN_WIDTH;
00345     return fm.width( txt );
00346 }
00347 
00348 bool KexiComboBoxTableEdit::eventFilter( QObject *o, QEvent *e )
00349 {
00350     if (!column()->isReadOnly() && e->type()==QEvent::MouseButtonPress && m_scrollView) {
00351         QPoint gp = static_cast<QMouseEvent*>(e)->globalPos() 
00352             + QPoint(m_scrollView->childX(d->button), m_scrollView->childY(d->button));
00353         QRect r(d->button->mapToGlobal(d->button->geometry().topLeft()), 
00354             d->button->mapToGlobal(d->button->geometry().bottomRight()));
00355         if (o==popup() && popup()->isVisible() && r.contains( gp )) {
00356             m_mouseBtnPressedWhenPopupVisible = true;
00357         }
00358     }
00359     return false;
00360 }
00361 
00362 QSize KexiComboBoxTableEdit::totalSize() const
00363 {
00364     return d->totalSize;
00365 }
00366 
00367 QWidget *KexiComboBoxTableEdit::internalEditor() const
00368 {
00369     return m_lineedit;
00370 }
00371 
00372 void KexiComboBoxTableEdit::moveCursorToEndInInternalEditor()
00373 {
00374     moveCursorToEnd();
00375 }
00376 
00377 void KexiComboBoxTableEdit::selectAllInInternalEditor()
00378 {
00379     selectAll();
00380 }
00381 
00382 void KexiComboBoxTableEdit::moveCursorToEnd()
00383 {
00384     m_lineedit->end(false);
00385 }
00386 
00387 void KexiComboBoxTableEdit::moveCursorToStart()
00388 {
00389     m_lineedit->home(false);
00390 }
00391 
00392 void KexiComboBoxTableEdit::selectAll()
00393 {
00394     m_lineedit->selectAll();
00395 }
00396 
00397 void KexiComboBoxTableEdit::setValueInInternalEditor(const QVariant& value)
00398 {
00399     m_lineedit->setText(value.toString());
00400 }
00401 
00402 QVariant KexiComboBoxTableEdit::valueFromInternalEditor()
00403 {
00404     return m_lineedit->text();
00405 }
00406 
00407 QPoint KexiComboBoxTableEdit::mapFromParentToGlobal(const QPoint& pos) const
00408 {
00409     KexiTableView *tv = dynamic_cast<KexiTableView*>(m_scrollView);
00410     if (!tv)
00411         return QPoint(-1,-1);
00412     return tv->viewport()->mapToGlobal(pos);
00413 }
00414 
00415 int KexiComboBoxTableEdit::popupWidthHint() const
00416 {
00417     return m_lineedit->width() + m_leftMargin + m_rightMarginWhenFocused; //QMAX(popup()->width(), d->currentEditorWidth);
00418 }
00419 
00420 void KexiComboBoxTableEdit::handleCopyAction(const QVariant& value, const QVariant& visibleValue)
00421 {
00422     Q_UNUSED(value);
00424     qApp->clipboard()->setText( visibleValue.toString() );
00425 }
00426 
00427 void KexiComboBoxTableEdit::handleAction(const QString& actionName)
00428 {
00429     const bool alreadyVisible = m_lineedit->isVisible();
00430 
00431     if (actionName=="edit_paste") {
00432         if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
00433             emit editRequested();
00434             m_lineedit->clear();
00435         }
00437         setValueInInternalEditor( qApp->clipboard()->text() );
00438     }
00439     else
00440         KexiInputTableEdit::handleAction(actionName);
00441 }
00442 
00443 
00444 KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiComboBoxEditorFactoryItem, KexiComboBoxTableEdit)
00445 
00446 #include "kexicomboboxtableedit.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys