|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2002 Peter Simonsson <psn@linux.se>
|
|
|
|
Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library 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
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this program; see the file COPYING. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <tqlayout.h>
|
|
|
|
#include <tqstyle.h>
|
|
|
|
#include <tqwindowsstyle.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqapplication.h>
|
|
|
|
#include <tqclipboard.h>
|
|
|
|
|
|
|
|
#include "kexicomboboxtableedit.h"
|
|
|
|
#include <widget/utils/kexicomboboxdropdownbutton.h>
|
|
|
|
#include "kexicomboboxpopup.h"
|
|
|
|
#include "kexitableview.h"
|
|
|
|
#include "kexitableitem.h"
|
|
|
|
#include "kexi.h"
|
|
|
|
|
|
|
|
#include <klineedit.h>
|
|
|
|
|
|
|
|
//! @internal
|
|
|
|
class KexiComboBoxTableEdit::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Private()
|
|
|
|
: popup(0)
|
|
|
|
, currentEditorWidth(0)
|
|
|
|
, visibleTableViewColumn(0)
|
|
|
|
, internalEditor(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
~Private()
|
|
|
|
{
|
|
|
|
delete internalEditor;
|
|
|
|
delete visibleTableViewColumn;
|
|
|
|
}
|
|
|
|
|
|
|
|
KPushButton *button;
|
|
|
|
KexiComboBoxPopup *popup;
|
|
|
|
int currentEditorWidth;
|
|
|
|
TQSize totalSize;
|
|
|
|
KexiTableViewColumn* visibleTableViewColumn;
|
|
|
|
KexiTableEdit* internalEditor;
|
|
|
|
};
|
|
|
|
|
|
|
|
//======================================================
|
|
|
|
|
|
|
|
KexiComboBoxTableEdit::KexiComboBoxTableEdit(KexiTableViewColumn &column, TQWidget *parent)
|
|
|
|
: KexiInputTableEdit(column, parent)
|
|
|
|
, KexiComboBoxBase()
|
|
|
|
, d(new Private())
|
|
|
|
{
|
|
|
|
setName("KexiComboBoxTableEdit");
|
|
|
|
m_setVisibleValueOnSetValueInternal = true;
|
|
|
|
d->button = new KexiComboBoxDropDownButton( parentWidget() /*usually a viewport*/ );
|
|
|
|
d->button->hide();
|
|
|
|
d->button->setFocusPolicy( TQWidget::NoFocus );
|
|
|
|
connect(d->button, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotButtonClicked()));
|
|
|
|
|
|
|
|
connect(m_lineedit, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotLineEditTextChanged(const TQString&)));
|
|
|
|
|
|
|
|
// m_lineedit = new KLineEdit(this, "lineedit");
|
|
|
|
// m_lineedit->setFrame(false);
|
|
|
|
// m_lineedit->setFrameStyle( TQFrame::Plain | TQFrame::Box );
|
|
|
|
// m_lineedit->setLineWidth( 1 );
|
|
|
|
// if (f.isNumericType()) {
|
|
|
|
// m_lineedit->setAlignment(AlignRight);
|
|
|
|
// }
|
|
|
|
// setView( m_lineedit );
|
|
|
|
|
|
|
|
// layout->addWidget(m_view);
|
|
|
|
// m_combo->setEditable( true );
|
|
|
|
// m_combo->clear();
|
|
|
|
// m_combo->insertStringList(f.enumHints());
|
|
|
|
// TQStringList::ConstIterator it, end( f.enumHints().constEnd() );
|
|
|
|
// for ( it = f.enumHints().constBegin(); it != end; ++it) {
|
|
|
|
// if(!hints.at(i).isEmpty())
|
|
|
|
// m_combo->insertItem(hints.at(i));
|
|
|
|
// }
|
|
|
|
|
|
|
|
//js: TODO
|
|
|
|
//js static_cast<KComboBox*>(m_view)->insertStringList(list);
|
|
|
|
//js static_cast<KComboBox*>(m_view)->setCurrentItem(static_cast<int>(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
KexiComboBoxTableEdit::~KexiComboBoxTableEdit()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::createInternalEditor(KexiDB::QuerySchema& schema)
|
|
|
|
{
|
|
|
|
if (!m_column->visibleLookupColumnInfo || d->visibleTableViewColumn/*sanity*/)
|
|
|
|
return;
|
|
|
|
const KexiDB::Field::Type t = m_column->visibleLookupColumnInfo->field->type();
|
|
|
|
//! @todo subtype?
|
|
|
|
KexiCellEditorFactoryItem *item = KexiCellEditorFactory::item(t);
|
|
|
|
if (!item || item->className()=="KexiInputTableEdit")
|
|
|
|
return; //unsupported type or there is no need to use subeditor for KexiInputTableEdit
|
|
|
|
//special cases: BLOB, Bool datatypes
|
|
|
|
//todo
|
|
|
|
//find real type to display
|
|
|
|
KexiDB::QueryColumnInfo *ci = m_column->visibleLookupColumnInfo;
|
|
|
|
KexiDB::QueryColumnInfo *visibleLookupColumnInfo = 0;
|
|
|
|
if (ci->indexForVisibleLookupValue() != -1) {
|
|
|
|
//Lookup field is defined
|
|
|
|
visibleLookupColumnInfo = schema.expandedOrInternalField( ci->indexForVisibleLookupValue() );
|
|
|
|
}
|
|
|
|
d->visibleTableViewColumn = new KexiTableViewColumn(schema, *ci, visibleLookupColumnInfo);
|
|
|
|
//! todo set d->internalEditor visible and use it to enable data entering by hand
|
|
|
|
d->internalEditor = KexiCellEditorFactory::createEditor(*d->visibleTableViewColumn, 0);
|
|
|
|
m_lineedit->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
KexiComboBoxPopup *KexiComboBoxTableEdit::popup() const
|
|
|
|
{
|
|
|
|
return d->popup;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::setPopup(KexiComboBoxPopup *popup)
|
|
|
|
{
|
|
|
|
d->popup = popup;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::showFocus( const TQRect& r, bool readOnly )
|
|
|
|
{
|
|
|
|
// d->button->move( pos().x()+ width(), pos().y() );
|
|
|
|
updateFocus( r );
|
|
|
|
d->button->setEnabled(!readOnly);
|
|
|
|
if (readOnly)
|
|
|
|
d->button->hide();
|
|
|
|
else
|
|
|
|
d->button->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::resize(int w, int h)
|
|
|
|
{
|
|
|
|
d->totalSize = TQSize(w,h);
|
|
|
|
if (!column()->isReadOnly()) {
|
|
|
|
d->button->resize( h, h );
|
|
|
|
TQWidget::resize(w - d->button->width(), h);
|
|
|
|
}
|
|
|
|
m_rightMarginWhenFocused = m_rightMargin + (column()->isReadOnly() ? 0 : d->button->width());
|
|
|
|
TQRect r( pos().x(), pos().y(), w+1, h+1 );
|
|
|
|
if (m_scrollView)
|
|
|
|
r.moveBy(m_scrollView->contentsX(), m_scrollView->contentsY());
|
|
|
|
updateFocus( r );
|
|
|
|
if (popup()) {
|
|
|
|
popup()->updateSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// internal
|
|
|
|
void KexiComboBoxTableEdit::updateFocus( const TQRect& r )
|
|
|
|
{
|
|
|
|
if (!column()->isReadOnly()) {
|
|
|
|
if (d->button->width() > r.width())
|
|
|
|
moveChild(d->button, r.right() + 1, r.top());
|
|
|
|
else
|
|
|
|
moveChild(d->button, r.right() - d->button->width(), r.top() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::hideFocus()
|
|
|
|
{
|
|
|
|
d->button->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQVariant KexiComboBoxTableEdit::visibleValue()
|
|
|
|
{
|
|
|
|
return KexiComboBoxBase::visibleValue();
|
|
|
|
/* KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
|
|
|
|
if (!popup() || !lookupFieldSchema)
|
|
|
|
return TQVariant();
|
|
|
|
KexiTableItem *it = popup()->tableView()->selectedItem();
|
|
|
|
return it ? it->at( lookupFieldSchema->visibleColumn() ) : TQVariant();*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::clear()
|
|
|
|
{
|
|
|
|
m_lineedit->clear();
|
|
|
|
KexiComboBoxBase::clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KexiComboBoxTableEdit::valueChanged()
|
|
|
|
{
|
|
|
|
const tristate res = valueChangedInternal();
|
|
|
|
if (~res) //no result: just compare values
|
|
|
|
return KexiInputTableEdit::valueChanged();
|
|
|
|
return res == true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::paintFocusBorders( TQPainter *p, TQVariant &, int x, int y, int w, int h )
|
|
|
|
{
|
|
|
|
// d->currentEditorWidth = w;
|
|
|
|
if (!column()->isReadOnly()) {
|
|
|
|
if (w > d->button->width())
|
|
|
|
w -= d->button->width();
|
|
|
|
}
|
|
|
|
p->drawRect(x, y, w, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::setupContents( TQPainter *p, bool focused, const TQVariant& val,
|
|
|
|
TQString &txt, int &align, int &x, int &y_offset, int &w, int &h )
|
|
|
|
{
|
|
|
|
if (d->internalEditor) {
|
|
|
|
d->internalEditor->setupContents( p, focused, val, txt, align, x, y_offset, w, h );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
KexiInputTableEdit::setupContents( p, focused, val, txt, align, x, y_offset, w, h );
|
|
|
|
}
|
|
|
|
if (!column()->isReadOnly() && focused && (w > d->button->width()))
|
|
|
|
w -= (d->button->width() - x);
|
|
|
|
if (!val.isNull()) {
|
|
|
|
KexiTableViewData *relData = column()->relatedData();
|
|
|
|
KexiDB::LookupFieldSchema *lookupFieldSchema = 0;
|
|
|
|
if (relData) {
|
|
|
|
int rowToHighlight;
|
|
|
|
txt = valueForString(val.toString(), &rowToHighlight, 0, 1);
|
|
|
|
}
|
|
|
|
else if ((lookupFieldSchema = this->lookupFieldSchema())) {
|
|
|
|
/* handled at at KexiTableView level
|
|
|
|
if (popup()) {
|
|
|
|
KexiTableItem *it = popup()->tableView()->selectedItem();
|
|
|
|
if (it && lookupFieldSchema->visibleColumn()!=-1 && (int)it->size() >= lookupFieldSchema->visibleColumn())
|
|
|
|
txt = it->at( lookupFieldSchema->visibleColumn() ).toString();
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//use 'enum hints' model
|
|
|
|
txt = field()->enumHint( val.toInt() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::slotButtonClicked()
|
|
|
|
{
|
|
|
|
// this method is sometimes called by hand:
|
|
|
|
// do not allow to simulate clicks when the button is disabled
|
|
|
|
if (column()->isReadOnly() || !d->button->isEnabled())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_mouseBtnPressedWhenPopupVisible) {
|
|
|
|
m_mouseBtnPressedWhenPopupVisible = false;
|
|
|
|
d->button->setOn(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
kdDebug() << "KexiComboBoxTableEdit::slotButtonClicked()" << endl;
|
|
|
|
if (!popup() || !popup()->isVisible()) {
|
|
|
|
kdDebug() << "SHOW POPUP" << endl;
|
|
|
|
showPopup();
|
|
|
|
d->button->setOn(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::slotPopupHidden()
|
|
|
|
{
|
|
|
|
d->button->setOn(false);
|
|
|
|
// d->currentEditorWidth = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::updateButton()
|
|
|
|
{
|
|
|
|
d->button->setOn(popup()->isVisible());
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::hide()
|
|
|
|
{
|
|
|
|
KexiInputTableEdit::hide();
|
|
|
|
KexiComboBoxBase::hide();
|
|
|
|
d->button->setOn(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::show()
|
|
|
|
{
|
|
|
|
KexiInputTableEdit::show();
|
|
|
|
if (!column()->isReadOnly()) {
|
|
|
|
d->button->show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KexiComboBoxTableEdit::handleKeyPress( TQKeyEvent *ke, bool editorActive )
|
|
|
|
{
|
|
|
|
const int k = ke->key();
|
|
|
|
if ((ke->state()==TQt::NoButton && k==TQt::Key_F4)
|
|
|
|
|| (ke->state()==AltButton && k==TQt::Key_Down))
|
|
|
|
{
|
|
|
|
//show popup
|
|
|
|
slotButtonClicked();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (editorActive) {
|
|
|
|
const bool enterPressed = k==TQt::Key_Enter || k==TQt::Key_Return;
|
|
|
|
if (enterPressed && m_internalEditorValueChanged) {
|
|
|
|
createPopup(false);
|
|
|
|
selectItemForEnteredValueInLookupTable( m_userEnteredValue );
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return handleKeyPressForPopup( ke );
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::slotLineEditTextChanged(const TQString& s)
|
|
|
|
{
|
|
|
|
slotInternalEditorValueChanged(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
int KexiComboBoxTableEdit::widthForValue( TQVariant &val, const TQFontMetrics &fm )
|
|
|
|
{
|
|
|
|
KexiTableViewData *relData = column() ? column()->relatedData() : 0;
|
|
|
|
if (lookupFieldSchema() || relData) {
|
|
|
|
// in 'lookupFieldSchema' or or 'related table data' model
|
|
|
|
// we're assuming val is already the text, not the index
|
|
|
|
//! @todo ok?
|
|
|
|
return TQMAX(KEXITV_MINIMUM_COLUMN_WIDTH, fm.width(val.toString()));
|
|
|
|
}
|
|
|
|
//use 'enum hints' model
|
|
|
|
TQValueVector<TQString> hints = field()->enumHints();
|
|
|
|
bool ok;
|
|
|
|
int idx = val.toInt(&ok);
|
|
|
|
if (!ok || idx < 0 || idx > int(hints.size()-1))
|
|
|
|
return KEXITV_MINIMUM_COLUMN_WIDTH;
|
|
|
|
TQString txt = hints.at( idx, &ok );
|
|
|
|
if (!ok)
|
|
|
|
return KEXITV_MINIMUM_COLUMN_WIDTH;
|
|
|
|
return fm.width( txt );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KexiComboBoxTableEdit::eventFilter( TQObject *o, TQEvent *e )
|
|
|
|
{
|
|
|
|
if (!column()->isReadOnly() && e->type()==TQEvent::MouseButtonPress && m_scrollView) {
|
|
|
|
TQPoint gp = static_cast<TQMouseEvent*>(e)->globalPos()
|
|
|
|
+ TQPoint(m_scrollView->childX(d->button), m_scrollView->childY(d->button));
|
|
|
|
TQRect r(d->button->mapToGlobal(d->button->geometry().topLeft()),
|
|
|
|
d->button->mapToGlobal(d->button->geometry().bottomRight()));
|
|
|
|
if (o==popup() && popup()->isVisible() && r.contains( gp )) {
|
|
|
|
m_mouseBtnPressedWhenPopupVisible = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQSize KexiComboBoxTableEdit::totalSize() const
|
|
|
|
{
|
|
|
|
return d->totalSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQWidget *KexiComboBoxTableEdit::internalEditor() const
|
|
|
|
{
|
|
|
|
return m_lineedit;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::moveCursorToEndInInternalEditor()
|
|
|
|
{
|
|
|
|
moveCursorToEnd();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::selectAllInInternalEditor()
|
|
|
|
{
|
|
|
|
selectAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::moveCursorToEnd()
|
|
|
|
{
|
|
|
|
m_lineedit->end(false/*!mark*/);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::moveCursorToStart()
|
|
|
|
{
|
|
|
|
m_lineedit->home(false/*!mark*/);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::selectAll()
|
|
|
|
{
|
|
|
|
m_lineedit->selectAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::setValueInInternalEditor(const TQVariant& value)
|
|
|
|
{
|
|
|
|
m_lineedit->setText(value.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
TQVariant KexiComboBoxTableEdit::valueFromInternalEditor()
|
|
|
|
{
|
|
|
|
return m_lineedit->text();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQPoint KexiComboBoxTableEdit::mapFromParentToGlobal(const TQPoint& pos) const
|
|
|
|
{
|
|
|
|
KexiTableView *tv = dynamic_cast<KexiTableView*>(m_scrollView);
|
|
|
|
if (!tv)
|
|
|
|
return TQPoint(-1,-1);
|
|
|
|
return tv->viewport()->mapToGlobal(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
int KexiComboBoxTableEdit::popupWidthHint() const
|
|
|
|
{
|
|
|
|
return m_lineedit->width() + m_leftMargin + m_rightMarginWhenFocused; //TQMAX(popup()->width(), d->currentEditorWidth);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::handleCopyAction(const TQVariant& value, const TQVariant& visibleValue)
|
|
|
|
{
|
|
|
|
Q_UNUSED(value);
|
|
|
|
//! @todo does not work with BLOBs!
|
|
|
|
tqApp->clipboard()->setText( visibleValue.toString() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KexiComboBoxTableEdit::handleAction(const TQString& actionName)
|
|
|
|
{
|
|
|
|
const bool alreadyVisible = m_lineedit->isVisible();
|
|
|
|
|
|
|
|
if (actionName=="edit_paste") {
|
|
|
|
if (!alreadyVisible) { //paste as the entire text if the cell was not in edit mode
|
|
|
|
emit editRequested();
|
|
|
|
m_lineedit->clear();
|
|
|
|
}
|
|
|
|
//! @todo does not work with BLOBs!
|
|
|
|
setValueInInternalEditor( tqApp->clipboard()->text() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
KexiInputTableEdit::handleAction(actionName);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
KEXI_CELLEDITOR_FACTORY_ITEM_IMPL(KexiComboBoxEditorFactoryItem, KexiComboBoxTableEdit)
|
|
|
|
|
|
|
|
#include "kexicomboboxtableedit.moc"
|