diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/plugins/forms/widgets | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kexi/plugins/forms/widgets')
36 files changed, 7945 insertions, 0 deletions
diff --git a/kexi/plugins/forms/widgets/Makefile.am b/kexi/plugins/forms/widgets/Makefile.am new file mode 100644 index 00000000..5ca5cbd8 --- /dev/null +++ b/kexi/plugins/forms/widgets/Makefile.am @@ -0,0 +1,28 @@ +include $(top_srcdir)/kexi/Makefile.global + +noinst_LTLIBRARIES = libkexiformutilswidgets.la + +libkexiformutilswidgets_la_SOURCES = \ + kexidbutils.cpp \ + kexidbautofield.cpp \ + kexidbform.cpp \ + kexidbsubform.cpp \ + kexidblabel.cpp \ + kexidbimagebox.cpp \ + kexipushbutton.cpp \ + kexiframe.cpp \ + kexidblineedit.cpp \ + kexidbcheckbox.cpp \ + kexidbtextedit.cpp \ + kexidbcombobox.cpp + +libkexiformutilswidgets_la_LDFLAGS = $(all_libraries) -Wnounresolved +libkexiformutilswidgets_la_LIBADD = + +libkexiformutilswidgets_la_METASOURCES = AUTO + +SUBDIRS = . + +# set the include path for X, qt and KDE +INCLUDES= -I$(top_srcdir)/kexi -I$(top_srcdir)/kexi/plugins/forms -I$(top_srcdir)/kexi/core $(all_includes) + diff --git a/kexi/plugins/forms/widgets/kexidbautofield.cpp b/kexi/plugins/forms/widgets/kexidbautofield.cpp new file mode 100644 index 00000000..36fbdb1a --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbautofield.cpp @@ -0,0 +1,846 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de> + Copyright (C) 2005-2007 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 "kexidbautofield.h" + +#include <qlabel.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qmetaobject.h> +#include <qapplication.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "kexidbcheckbox.h" +#include "kexidbimagebox.h" +#include "kexidblabel.h" +#include "kexidblineedit.h" +#include "kexidbtextedit.h" +#include "kexidbcombobox.h" +#include "kexipushbutton.h" +#include "kexidbform.h" + +#include <kexidb/queryschema.h> +#include <formeditor/utils.h> +#include <kexiutils/utils.h> + +#define KexiDBAutoField_SPACING 10 //10 pixel for spacing between a label and an editor widget + +//! @internal +class KexiDBAutoField::Private +{ + public: + Private() + { + } + + WidgetType widgetType; //!< internal: equal to m_widgetType_property or equal to result + //!< of widgetTypeForFieldType() if widgetTypeForFieldType is Auto + WidgetType widgetType_property; //!< provides widget type or Auto + LabelPosition lblPosition; + QBoxLayout *layout; + QLabel *label; + QString caption; + KexiDB::Field::Type fieldTypeInternal; + QString fieldCaptionInternal; + QColor baseColor; //!< needed because for unbound mode editor==0 + QColor textColor; //!< needed because for unbound mode editor==0 + bool autoCaption : 1; + bool focusPolicyChanged : 1; + bool designMode : 1; +}; + +//------------------------------------- + +KexiDBAutoField::KexiDBAutoField(const QString &text, WidgetType type, LabelPosition pos, + QWidget *parent, const char *name, bool designMode) + : QWidget(parent, name) + , KexiFormDataItemInterface() + , KFormDesigner::DesignTimeDynamicChildWidgetHandler() + , d( new Private() ) +{ + d->designMode = designMode; + init(text, type, pos); +} + +KexiDBAutoField::KexiDBAutoField(QWidget *parent, const char *name, bool designMode, LabelPosition pos) + : QWidget(parent, name) + , KexiFormDataItemInterface() + , KFormDesigner::DesignTimeDynamicChildWidgetHandler() + , d( new Private() ) +{ + d->designMode = designMode; + init(QString::null/*i18n("Auto Field")*/, Auto, pos); +} + +KexiDBAutoField::~KexiDBAutoField() +{ + setUpdatesEnabled(false); + if (m_subwidget) + m_subwidget->setUpdatesEnabled(false); + delete d; +} + +void +KexiDBAutoField::init(const QString &text, WidgetType type, LabelPosition pos) +{ + d->fieldTypeInternal = KexiDB::Field::InvalidType; + d->layout = 0; + m_subwidget = 0; + d->label = new QLabel(text, this); + d->label->installEventFilter( this ); + //QFontMetrics fm( font() ); + //d->label->setFixedWidth( fm.width("This is a test string length") ); + d->autoCaption = true; + d->focusPolicyChanged = false; + d->widgetType = Auto; + d->widgetType_property = (type==Auto ? Text : type); //to force "differ" to be true in setWidgetType() + setLabelPosition(pos); + setWidgetType(type); + d->baseColor = palette().active().base(); + d->textColor = palette().active().text(); +} + +void +KexiDBAutoField::setWidgetType(WidgetType type) +{ + const bool differ = (type != d->widgetType_property); + d->widgetType_property = type; + if(differ) { + if(type == Auto) {// try to guess type from data source type + if (visibleColumnInfo()) + d->widgetType = KexiDBAutoField::widgetTypeForFieldType(visibleColumnInfo()->field->type()); + else + d->widgetType = Auto; + } + else + d->widgetType = d->widgetType_property; + createEditor(); + } +} + +void +KexiDBAutoField::createEditor() +{ + if(m_subwidget) { + delete (QWidget *)m_subwidget; + } + + QWidget *newSubwidget; + switch( d->widgetType ) { + case Text: + case Double: //! @todo setup validator + case Integer: //! @todo setup validator + case Date: + case Time: + case DateTime: + newSubwidget = new KexiDBLineEdit( this, QCString("KexiDBAutoField_KexiDBLineEdit:")+name() ); + break; + case MultiLineText: + newSubwidget = new KexiDBTextEdit( this, QCString("KexiDBAutoField_KexiDBTextEdit:")+name() ); + break; + case Boolean: + newSubwidget = new KexiDBCheckBox(dataSource(), this, QCString("KexiDBAutoField_KexiDBCheckBox:")+name()); + break; + case Image: + newSubwidget = new KexiDBImageBox(d->designMode, this, QCString("KexiDBAutoField_KexiDBImageBox:")+name()); + break; + case ComboBox: + newSubwidget = new KexiDBComboBox(this, QCString("KexiDBAutoField_KexiDBComboBox:")+name(), d->designMode); + break; + default: + newSubwidget = 0; + changeText(d->caption); + //d->label->setText( d->dataSource.isEmpty() ? "<datasource>" : d->dataSource ); + break; + } + + setSubwidget( newSubwidget ); //this will also allow to declare subproperties, see KFormDesigner::WidgetWithSubpropertiesInterface + if(newSubwidget) { + newSubwidget->setName( QCString("KexiDBAutoField_") + newSubwidget->className() ); + dynamic_cast<KexiDataItemInterface*>(newSubwidget)->setParentDataItemInterface(this); + dynamic_cast<KexiFormDataItemInterface*>(newSubwidget) + ->setColumnInfo(columnInfo()); //needed at least by KexiDBImageBox + dynamic_cast<KexiFormDataItemInterface*>(newSubwidget) + ->setVisibleColumnInfo(visibleColumnInfo()); //needed at least by KexiDBComboBox + newSubwidget->setProperty("dataSource", dataSource()); //needed at least by KexiDBImageBox + KFormDesigner::DesignTimeDynamicChildWidgetHandler::childWidgetAdded(this); + newSubwidget->show(); + d->label->setBuddy(newSubwidget); + if (d->focusPolicyChanged) {//if focusPolicy is changed at top level, editor inherits it + newSubwidget->setFocusPolicy(focusPolicy()); + } + else {//if focusPolicy is not changed at top level, inherit it from editor + QWidget::setFocusPolicy(newSubwidget->focusPolicy()); + } + setFocusProxy(newSubwidget); //ok? + if (parentWidget()) + newSubwidget->setPalette( qApp->palette() ); + copyPropertiesToEditor(); +// KFormDesigner::installRecursiveEventFilter(newSubwidget, this); + } + + setLabelPosition(labelPosition()); +} + +void KexiDBAutoField::copyPropertiesToEditor() +{ + if (m_subwidget) { +// kdDebug() << "KexiDBAutoField::copyPropertiesToEditor(): base col: " << d->baseColor.name() << +// "; text col: " << d->textColor.name() << endl; + QPalette p( m_subwidget->palette() ); + p.setColor( QPalette::Active, QColorGroup::Base, d->baseColor ); + if(d->widgetType == Boolean) + p.setColor( QPalette::Active, QColorGroup::Foreground, d->textColor ); + else + p.setColor( QPalette::Active, QColorGroup::Text, d->textColor ); + m_subwidget->setPalette(p); + //m_subwidget->setPaletteBackgroundColor( d->baseColor ); + } +} + +void +KexiDBAutoField::setLabelPosition(LabelPosition position) +{ + d->lblPosition = position; + if(d->layout) { + QBoxLayout *lyr = d->layout; + d->layout = 0; + delete lyr; + } + + if(m_subwidget) + m_subwidget->show(); + //! \todo support right-to-left layout where positions are inverted + if (position==Top || position==Left) { + int align = d->label->alignment(); + if(position == Top) { + d->layout = (QBoxLayout*) new QVBoxLayout(this); + align |= AlignVertical_Mask; + align ^= AlignVertical_Mask; + align |= AlignTop; + } + else { + d->layout = (QBoxLayout*) new QHBoxLayout(this); + align |= AlignVertical_Mask; + align ^= AlignVertical_Mask; + align |= AlignVCenter; + } + d->label->setAlignment(align); + if(d->widgetType == Boolean + || (d->widgetType == Auto && fieldTypeInternal() == KexiDB::Field::InvalidType && !d->designMode)) + { + d->label->hide(); + } + else { + d->label->show(); + } + d->layout->addWidget(d->label, 0, position == Top ? int(Qt::AlignLeft) : 0); + if(position == Left && d->widgetType != Boolean) + d->layout->addSpacing(KexiDBAutoField_SPACING); + d->layout->addWidget(m_subwidget, 1); + KexiSubwidgetInterface *subwidgetInterface = dynamic_cast<KexiSubwidgetInterface*>((QWidget*)m_subwidget); + if (subwidgetInterface) { + if (subwidgetInterface->appendStretchRequired(this)) + d->layout->addStretch(0); + if (subwidgetInterface->subwidgetStretchRequired(this)) { + QSizePolicy sizePolicy( m_subwidget->sizePolicy() ); + if(position == Left) { + sizePolicy.setHorData( QSizePolicy::Minimum ); + d->label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + } + else { + sizePolicy.setVerData( QSizePolicy::Minimum ); + d->label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + } + m_subwidget->setSizePolicy(sizePolicy); + } + } +// if(m_subwidget) + // m_subwidget->setSizePolicy(...); + } + else { + d->layout = (QBoxLayout*) new QHBoxLayout(this); + d->label->hide(); + d->layout->addWidget(m_subwidget); + } + //a hack to force layout to be refreshed (any better idea for this?) + resize(size()+QSize(1,0)); + resize(size()-QSize(1,0)); + if (dynamic_cast<KexiDBAutoField*>((QWidget*)m_subwidget)) { + //needed for KexiDBComboBox + dynamic_cast<KexiDBAutoField*>((QWidget*)m_subwidget)->setLabelPosition(position); + } +} + +void +KexiDBAutoField::setInvalidState( const QString &text ) +{ + // Widget with an invalid dataSource is just a QLabel + if (d->designMode) + return; + d->widgetType = Auto; + createEditor(); + setFocusPolicy(QWidget::NoFocus); + if (m_subwidget) + m_subwidget->setFocusPolicy(QWidget::NoFocus); +//! @todo or set this to editor's text? + d->label->setText( text ); +} + +bool +KexiDBAutoField::isReadOnly() const +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + return iface->isReadOnly(); + else + return false; +} + +void +KexiDBAutoField::setReadOnly( bool readOnly ) +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + iface->setReadOnly(readOnly); +} + +void +KexiDBAutoField::setValueInternal(const QVariant& add, bool removeOld) +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + iface->setValue(m_origValue, add, removeOld); +// iface->setValueInternal(add, removeOld); +} + +QVariant +KexiDBAutoField::value() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + return iface->value(); + return QVariant(); +} + +bool +KexiDBAutoField::valueIsNull() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + return iface->valueIsNull(); + return true; +} + +bool +KexiDBAutoField::valueIsEmpty() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + return iface->valueIsEmpty(); + return true; +} + +bool +KexiDBAutoField::valueIsValid() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + return iface->valueIsValid(); + return true; +} + +bool +KexiDBAutoField::valueChanged() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + kexipluginsdbg << m_origValue << endl; + if(iface) + return iface->valueChanged(); + return false; +} + +void +KexiDBAutoField::installListener(KexiDataItemChangesListener* listener) +{ + KexiFormDataItemInterface::installListener(listener); + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + iface->installListener(listener); +} + +KexiDBAutoField::WidgetType KexiDBAutoField::widgetType() const +{ + return d->widgetType_property; +} + +KexiDBAutoField::LabelPosition KexiDBAutoField::labelPosition() const +{ + return d->lblPosition; +} + +QString KexiDBAutoField::caption() const +{ + return d->caption; +} + +bool KexiDBAutoField::hasAutoCaption() const +{ + return d->autoCaption; +} + +QWidget* KexiDBAutoField::editor() const +{ + return m_subwidget; +} + +QLabel* KexiDBAutoField::label() const +{ + return d->label; +} + +int KexiDBAutoField::fieldTypeInternal() const +{ + return d->fieldTypeInternal; +} + +QString KexiDBAutoField::fieldCaptionInternal() const +{ + return d->fieldCaptionInternal; +} + +bool +KexiDBAutoField::cursorAtStart() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + return iface->cursorAtStart(); + return false; +} + +bool +KexiDBAutoField::cursorAtEnd() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + return iface->cursorAtEnd(); + return false; +} + +void +KexiDBAutoField::clear() +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + iface->clear(); +} + +void +KexiDBAutoField::setFieldTypeInternal(int kexiDBFieldType) +{ + d->fieldTypeInternal = (KexiDB::Field::Type)kexiDBFieldType; + KexiDB::Field::Type fieldType; + //find real fied type to use + if (d->fieldTypeInternal==KexiDB::Field::InvalidType) { + if (visibleColumnInfo()) + fieldType = KexiDB::Field::Text; + else + fieldType = KexiDB::Field::InvalidType; + } + else + fieldType = d->fieldTypeInternal; + + const WidgetType newWidgetType = KexiDBAutoField::widgetTypeForFieldType( fieldType ); + + if(d->widgetType != newWidgetType) { + d->widgetType = newWidgetType; + createEditor(); + } + setFieldCaptionInternal(d->fieldCaptionInternal); +} + +void +KexiDBAutoField::setFieldCaptionInternal(const QString& text) +{ + d->fieldCaptionInternal = text; + //change text only if autocaption is set and no columnInfo is available + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if((!iface || !iface->columnInfo()) && d->autoCaption) { + changeText(d->fieldCaptionInternal); + } +} + +void +KexiDBAutoField::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) +{ + KexiFormDataItemInterface::setColumnInfo(cinfo); + setColumnInfoInternal(cinfo, cinfo); +} + +void +KexiDBAutoField::setColumnInfoInternal(KexiDB::QueryColumnInfo* cinfo, KexiDB::QueryColumnInfo* visibleColumnInfo) +{ + // change widget type depending on field type + if(d->widgetType_property == Auto) { + WidgetType newWidgetType = Auto; + KexiDB::Field::Type fieldType; + if (cinfo) + fieldType = visibleColumnInfo->field->type(); + else if (dataSource().isEmpty()) + fieldType = KexiDB::Field::InvalidType; + else + fieldType = KexiDB::Field::Text; + + if (fieldType != KexiDB::Field::InvalidType) { + newWidgetType = KexiDBAutoField::widgetTypeForFieldType( fieldType ); + } + if(d->widgetType != newWidgetType || newWidgetType==Auto) { + d->widgetType = newWidgetType; + createEditor(); + } + } + // update label's text + changeText((cinfo && d->autoCaption) ? cinfo->captionOrAliasOrName() : d->caption); + + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + iface->setColumnInfo(visibleColumnInfo); +} + +//static +KexiDBAutoField::WidgetType +KexiDBAutoField::widgetTypeForFieldType(KexiDB::Field::Type type) +{ + switch(type) { + case KexiDB::Field::Integer: + case KexiDB::Field::ShortInteger: + case KexiDB::Field::BigInteger: + return Integer; + case KexiDB::Field::Boolean: + return Boolean; + case KexiDB::Field::Float: + case KexiDB::Field::Double: + return Double; + case KexiDB::Field::Date: + return Date; + case KexiDB::Field::DateTime: + return DateTime; + case KexiDB::Field::Time: + return Time; + case KexiDB::Field::Text: + return Text; + case KexiDB::Field::LongText: + return MultiLineText; + case KexiDB::Field::Enum: + return ComboBox; + case KexiDB::Field::InvalidType: + return Auto; + case KexiDB::Field::BLOB: + return Image; + default: + break; + } + return Text; +} + +void +KexiDBAutoField::changeText(const QString &text, bool beautify) +{ + QString realText; + bool unbound = false; + if (d->autoCaption && (d->widgetType==Auto || dataSource().isEmpty())) { + if (d->designMode) + realText = QString::fromLatin1(name())+" "+i18n("Unbound Auto Field", "(unbound)"); + else + realText = QString::null; + unbound = true; + } + else { + if (beautify) { + /*! @todo look at appendColonToAutoLabels setting [bool] + @todo look at makeFirstCharacterUpperCaseInCaptions setting [bool] + (see doc/dev/settings.txt) */ + if (!text.isEmpty()) { + realText = text[0].upper() + text.mid(1); + if (d->widgetType!=Boolean) { +//! @todo ":" suffix looks weird for checkbox; remove this condition when [x] is displayed _after_ label +//! @todo support right-to-left layout where position of ":" is inverted + realText += ": "; + } + } + } + else + realText = text; + } + + if (unbound) + d->label->setAlignment( Qt::AlignCenter | Qt::WordBreak ); + else + d->label->setAlignment( Qt::AlignCenter ); +// QWidget* widgetToAlterForegroundColor; + if(d->widgetType == Boolean) { + static_cast<QCheckBox*>((QWidget*)m_subwidget)->setText(realText); +// widgetToAlterForegroundColor = m_subwidget; + } + else { + d->label->setText(realText); +// widgetToAlterForegroundColor = d->label; + } +/* + if (unbound) + widgetToAlterForegroundColor->setPaletteForegroundColor( + KexiUtils::blendedColors( + widgetToAlterForegroundColor->paletteForegroundColor(), + widgetToAlterForegroundColor->paletteBackgroundColor(), 2, 1)); + else + widgetToAlterForegroundColor->setPaletteForegroundColor( paletteForegroundColor() );*/ +} + +void +KexiDBAutoField::setCaption(const QString &caption) +{ + d->caption = caption; + if(!d->autoCaption && !caption.isEmpty()) + changeText(d->caption); +} + +void +KexiDBAutoField::setAutoCaption(bool autoCaption) +{ + d->autoCaption = autoCaption; + if(d->autoCaption) { + //d->caption = QString::null; + if(columnInfo()) { + changeText(columnInfo()->captionOrAliasOrName()); + } + else { + changeText(d->fieldCaptionInternal); + } + } + else + changeText(d->caption); +} + +void +KexiDBAutoField::setDataSource( const QString &ds ) { + KexiFormDataItemInterface::setDataSource(ds); + if (ds.isEmpty()) { + setColumnInfo(0); + } +} + +QSize +KexiDBAutoField::sizeHint() const +{ + if (d->lblPosition == NoLabel) + return m_subwidget ? m_subwidget->sizeHint() : QWidget::sizeHint(); + + QSize s1(0,0); + if (m_subwidget) + s1 = m_subwidget->sizeHint(); + QSize s2(d->label->sizeHint()); + if (d->lblPosition == Top) + return QSize(QMAX(s1.width(), s2.width()), s1.height()+KexiDBAutoField_SPACING+s2.height()); + + //left + return QSize(s1.width()+KexiDBAutoField_SPACING+s2.width(), QMAX(s1.height(), s2.height())); +} + +void +KexiDBAutoField::setFocusPolicy( FocusPolicy policy ) +{ + d->focusPolicyChanged = true; + QWidget::setFocusPolicy(policy); + d->label->setFocusPolicy(policy); + if (m_subwidget) + m_subwidget->setFocusPolicy(policy); +} + +void +KexiDBAutoField::updateInformationAboutUnboundField() +{ + if ( (d->autoCaption && (dataSource().isEmpty() || dataSourceMimeType().isEmpty())) + || (!d->autoCaption && d->caption.isEmpty()) ) + { + d->label->setText( QString::fromLatin1(name())+" "+i18n("Unbound Auto Field", " (unbound)") ); + } +// else +// d->label->setText( QString::fromLatin1(name())+" "+i18n(" (unbound)") ); +} + +/*void +KexiDBAutoField::paintEvent( QPaintEvent* pe ) +{ + QWidget::paintEvent( pe ); + + if ( (d->autoCaption && (dataSource().isEmpty() || dataSourceMimeType().isEmpty())) + || (!d->autoCaption && d->caption.isEmpty()) ) + { + QPainter p(this); + p.setPen( d->label->paletteForegroundColor() ); + p.setClipRect(pe->rect()); + p.setFont(d->label->font()); + p.drawText(rect(), Qt::AlignLeft | Qt::WordBreak, + QString::fromLatin1(name())+" "+i18n(" (unbound)")); + } +}*/ + +void +KexiDBAutoField::paletteChange( const QPalette& oldPal ) +{ + Q_UNUSED(oldPal); + d->label->setPalette( palette() ); +} + +void KexiDBAutoField::unsetPalette() +{ + QWidget::unsetPalette(); + +} + +// ===== methods below are just proxies for the internal editor or label ===== + +const QColor & KexiDBAutoField::paletteForegroundColor() const +{ + return d->textColor; +} + +void KexiDBAutoField::setPaletteForegroundColor( const QColor & color ) +{ + d->textColor = color; + copyPropertiesToEditor(); +} + +const QColor & KexiDBAutoField::paletteBackgroundColor() const +{ + return d->baseColor; +} + +void KexiDBAutoField::setPaletteBackgroundColor( const QColor & color ) +{ + d->baseColor = color; + copyPropertiesToEditor(); +} + +const QColor & KexiDBAutoField::foregroundLabelColor() const +{ + if(d->widgetType == Boolean) + return paletteForegroundColor(); + + return d->label->paletteForegroundColor(); +} + +void KexiDBAutoField::setForegroundLabelColor( const QColor & color ) +{ + if(d->widgetType == Boolean) + setPaletteForegroundColor(color); + else { + d->label->setPaletteForegroundColor(color); + QWidget::setPaletteForegroundColor(color); + } +} + +const QColor & KexiDBAutoField::backgroundLabelColor() const +{ + if(d->widgetType == Boolean) + return paletteBackgroundColor(); + + return d->label->paletteBackgroundColor(); +} + +void KexiDBAutoField::setBackgroundLabelColor( const QColor & color ) +{ + if(d->widgetType == Boolean) + setPaletteBackgroundColor(color); + else { + d->label->setPaletteBackgroundColor(color); + QWidget::setPaletteBackgroundColor(color); + } + +// if (m_subwidget) +// m_subwidget->setPalette( qApp->palette() ); +} + +QVariant KexiDBAutoField::property( const char * name ) const +{ + bool ok; + QVariant val = KFormDesigner::WidgetWithSubpropertiesInterface::subproperty(name, ok); + if (ok) + return val; + return QWidget::property(name); +} + +bool KexiDBAutoField::setProperty( const char * name, const QVariant & value ) +{ + bool ok = KFormDesigner::WidgetWithSubpropertiesInterface::setSubproperty(name, value); + if (ok) + return true; + return QWidget::setProperty(name, value); +} + +bool KexiDBAutoField::eventFilter( QObject *o, QEvent *e ) +{ + if (o==d->label && d->label->buddy() && e->type()==QEvent::MouseButtonRelease) { + //focus label's buddy when user clicked the label + d->label->buddy()->setFocus(); + } + return QWidget::eventFilter(o, e); +} + +void KexiDBAutoField::setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue) +{ + KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue); + if (dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget)) + dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget)->setDisplayDefaultValue(m_subwidget, displayDefaultValue); +} + +void KexiDBAutoField::moveCursorToEnd() +{ + KexiDataItemInterface *iface = dynamic_cast<KexiDataItemInterface*>((QWidget*)m_subwidget); + if (iface) + iface->moveCursorToEnd(); +} + +void KexiDBAutoField::moveCursorToStart() +{ + KexiDataItemInterface *iface = dynamic_cast<KexiDataItemInterface*>((QWidget*)m_subwidget); + if (iface) + iface->moveCursorToStart(); +} + +void KexiDBAutoField::selectAll() +{ + KexiDataItemInterface *iface = dynamic_cast<KexiDataItemInterface*>((QWidget*)m_subwidget); + if (iface) + iface->selectAll(); +} + +bool KexiDBAutoField::keyPressed(QKeyEvent *ke) +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if (iface && iface->keyPressed(ke)) + return true; + return false; +} + +#include "kexidbautofield.moc" diff --git a/kexi/plugins/forms/widgets/kexidbautofield.h b/kexi/plugins/forms/widgets/kexidbautofield.h new file mode 100644 index 00000000..981a0519 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbautofield.h @@ -0,0 +1,210 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de> + Copyright (C) 2005-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. +*/ + +#ifndef KEXIDBAUTOFIELD_H +#define KEXIDBAUTOFIELD_H + +#include <qwidget.h> +#include <kexidb/field.h> +#include <formeditor/container.h> +#include <formeditor/widgetwithsubpropertiesinterface.h> +#include "kexiformdataiteminterface.h" + +class QBoxLayout; +class QLabel; + +//! Universal "Auto Field" widget for Kexi forms +/*! It acts as a container for most data-aware widgets. */ +class KEXIFORMUTILS_EXPORT KexiDBAutoField : + public QWidget, + public KexiFormDataItemInterface, + public KFormDesigner::DesignTimeDynamicChildWidgetHandler, + public KFormDesigner::WidgetWithSubpropertiesInterface +{ + Q_OBJECT +//'caption' is uncovered now Q_PROPERTY(QString labelCaption READ caption WRITE setCaption DESIGNABLE true) + Q_OVERRIDE(QString caption READ caption WRITE setCaption DESIGNABLE true) + Q_OVERRIDE(QColor paletteForegroundColor READ paletteForegroundColor WRITE setPaletteForegroundColor DESIGNABLE true RESET unsetPalette) + Q_OVERRIDE(QColor paletteBackgroundColor READ paletteBackgroundColor WRITE setPaletteBackgroundColor DESIGNABLE true RESET unsetPalette) + Q_PROPERTY(QColor foregroundLabelColor READ foregroundLabelColor WRITE setForegroundLabelColor DESIGNABLE true RESET unsetPalette) + Q_PROPERTY(QColor backgroundLabelColor READ backgroundLabelColor WRITE setBackgroundLabelColor DESIGNABLE true RESET unsetPalette) + Q_PROPERTY(bool autoCaption READ hasAutoCaption WRITE setAutoCaption DESIGNABLE true) + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY(LabelPosition labelPosition READ labelPosition WRITE setLabelPosition DESIGNABLE true) + Q_PROPERTY(WidgetType widgetType READ widgetType WRITE setWidgetType DESIGNABLE true) + /*internal, for design time only*/ + Q_PROPERTY(int fieldTypeInternal READ fieldTypeInternal WRITE setFieldTypeInternal DESIGNABLE true STORED false) + Q_PROPERTY(QString fieldCaptionInternal READ fieldCaptionInternal WRITE setFieldCaptionInternal DESIGNABLE true STORED false) + Q_ENUMS( WidgetType LabelPosition ) + + public: + enum WidgetType { Auto = 100, Text, Integer, Double, Boolean, Date, Time, DateTime, + MultiLineText, ComboBox, Image }; + enum LabelPosition { Left = 300, Top, NoLabel }; + + KexiDBAutoField(const QString &text, WidgetType type, LabelPosition pos, + QWidget *parent = 0, const char *name = 0, bool designMode = true); + KexiDBAutoField(QWidget *parent = 0, const char *name = 0, bool designMode = true, + LabelPosition pos = Left); + + virtual ~KexiDBAutoField(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual void setDataSource( const QString &ds ); + virtual void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo); + + virtual void setInvalidState(const QString& text); + virtual bool isReadOnly() const; + virtual void setReadOnly( bool readOnly ); + + virtual QVariant value(); + virtual bool valueIsNull(); + virtual bool valueIsEmpty(); + virtual bool valueIsValid(); + virtual bool valueChanged(); + virtual void clear(); + + //! Reimpelmented to also install \a listenter for internal editor + virtual void installListener(KexiDataItemChangesListener* listener); + + WidgetType widgetType() const; + void setWidgetType(WidgetType type); + + LabelPosition labelPosition() const; + virtual void setLabelPosition(LabelPosition position); + + QString caption() const; + void setCaption(const QString &caption); + + bool hasAutoCaption() const; + void setAutoCaption(bool autoCaption); + + /*! If \a displayDefaultValue is true, the value set by KexiDataItemInterface::setValue() + is displayed in a special way. Used by KexiFormDataProvider::fillDataItems(). + \a widget is equal to 'this'. + Reimplemented after KexiFormDataItemInterface. */ + virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue); + + QWidget* editor() const; + QLabel* label() const; + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + + static WidgetType widgetTypeForFieldType(KexiDB::Field::Type type); + + /*! On design time it is not possible to pass a reference to KexiDB::Field object + so we're just providing field type. Only used when widget type is Auto. + @internal */ + void setFieldTypeInternal(int kexiDBFieldType); + + /*! On design time it is not possible to pass a reference to KexiDB::Field object + so we're just providing field caption. Only used when widget type is Auto. + @internal */ + void setFieldCaptionInternal(const QString& text); + + /*! @internal */ + int fieldTypeInternal() const; + + /*! @internal */ + QString fieldCaptionInternal() const; + + virtual QSize sizeHint() const; + virtual void setFocusPolicy ( FocusPolicy policy ); + + //! Reimplemented to return internal editor's color. + const QColor & paletteForegroundColor() const; + + //! Reimplemented to set internal editor's color. + void setPaletteForegroundColor( const QColor & color ); + + //! Reimplemented to return internal editor's color. + const QColor & paletteBackgroundColor() const; + + //! Reimplemented to set internal editor's color. + virtual void setPaletteBackgroundColor( const QColor & color ); + + //! \return label's foreground color + const QColor & foregroundLabelColor() const; + + //! Sets label's foreground color + virtual void setForegroundLabelColor( const QColor & color ); + + //! \return label's background color + const QColor & backgroundLabelColor() const; + + //! Sets label's background color + virtual void setBackgroundLabelColor( const QColor & color ); + + //! Reimplemented to accept subproperties. @see KFormDesigner::WidgetWithSubpropertiesInterface + virtual QVariant property( const char * name ) const; + + //! Reimplemented to accept subproperties. @see KFormDesigner::WidgetWithSubpropertiesInterface + virtual bool setProperty( const char * name, const QVariant & value ); + + /*! Called by the top-level form on key press event to consume widget-specific shortcuts. */ + virtual bool keyPressed(QKeyEvent *ke); + + public slots: + virtual void unsetPalette(); + + protected slots: +// void slotValueChanged(); + virtual void paletteChange( const QPalette& oldPal ); + + //! Implemented for KexiDataItemInterface + virtual void moveCursorToEnd(); + + //! Implemented for KexiDataItemInterface + virtual void moveCursorToStart(); + + //! Implemented for KexiDataItemInterface + virtual void selectAll(); + + protected: + virtual void setValueInternal(const QVariant&add, bool removeOld); + void init(const QString &text, WidgetType type, LabelPosition pos); + virtual void createEditor(); + void changeText(const QString &text, bool beautify = true); +// virtual void paintEvent( QPaintEvent* pe ); + void updateInformationAboutUnboundField(); + + //! internal editor can be created too late, so certain properties should be copied + void copyPropertiesToEditor(); + + virtual bool eventFilter( QObject *o, QEvent *e ); + + //! Used by @ref setLabelPositionInternal(LabelPosition) + void setLabelPositionInternal(LabelPosition position, bool noLabel); + + //! Used by KexiDBAutoField::setColumnInfo() and KexiDBComboBox::setColumnInfo() + void setColumnInfoInternal(KexiDB::QueryColumnInfo* cinfo, KexiDB::QueryColumnInfo* visibleColumnInfo); + + private: + class Private; + Private *d; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbcheckbox.cpp b/kexi/plugins/forms/widgets/kexidbcheckbox.cpp new file mode 100644 index 00000000..6b63851a --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbcheckbox.cpp @@ -0,0 +1,175 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-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 "kexidbcheckbox.h" + +#include <kexiutils/utils.h> +#include <kexidb/queryschema.h> + +KexiDBCheckBox::KexiDBCheckBox(const QString &text, QWidget *parent, const char *name) + : QCheckBox(text, parent, name), KexiFormDataItemInterface() + , m_invalidState(false) + , m_tristateChanged(false) + , m_tristate(TristateDefault) +{ + setFocusPolicy(QWidget::StrongFocus); + updateTristate(); + connect(this, SIGNAL(stateChanged(int)), this, SLOT(slotStateChanged(int))); +} + +KexiDBCheckBox::~KexiDBCheckBox() +{ +} + +void KexiDBCheckBox::setInvalidState( const QString& displayText ) +{ + setEnabled(false); + setState(NoChange); + m_invalidState = true; +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); + setText(displayText); +} + +void +KexiDBCheckBox::setEnabled(bool enabled) +{ + if(enabled && m_invalidState) + return; + QCheckBox::setEnabled(enabled); +} + +void +KexiDBCheckBox::setReadOnly(bool readOnly) +{ + setEnabled(!readOnly); +} + +void KexiDBCheckBox::setValueInternal(const QVariant &add, bool removeOld) +{ + Q_UNUSED(add); + Q_UNUSED(removeOld); + if (isTristateInternal()) + setState( m_origValue.isNull() ? NoChange : (m_origValue.toBool() ? On : Off) ); + else + setState( m_origValue.toBool() ? On : Off ); +} + +QVariant +KexiDBCheckBox::value() +{ + if (state()==NoChange) + return QVariant(); + return QVariant(state()==On, 1); +} + +void KexiDBCheckBox::slotStateChanged(int ) +{ + signalValueChanged(); +} + +bool KexiDBCheckBox::valueIsNull() +{ + return state() == NoChange; +} + +bool KexiDBCheckBox::valueIsEmpty() +{ + return false; +} + +bool KexiDBCheckBox::isReadOnly() const +{ + return !isEnabled(); +} + +QWidget* +KexiDBCheckBox::widget() +{ + return this; +} + +bool KexiDBCheckBox::cursorAtStart() +{ + return false; //! \todo ? +} + +bool KexiDBCheckBox::cursorAtEnd() +{ + return false; //! \todo ? +} + +void KexiDBCheckBox::clear() +{ + setState(NoChange); +} + +void KexiDBCheckBox::setTristate(KexiDBCheckBox::Tristate tristate) +{ + m_tristateChanged = true; + m_tristate = tristate; + updateTristate(); +} + +KexiDBCheckBox::Tristate KexiDBCheckBox::isTristate() const +{ + return m_tristate; +} + +bool KexiDBCheckBox::isTristateInternal() const +{ + if (m_tristate == TristateDefault) + return !dataSource().isEmpty(); + + return m_tristate == TristateOn; +} + +void KexiDBCheckBox::updateTristate() +{ + if (m_tristate == TristateDefault) { +//! @todo the data source may be defined as NOT NULL... thus disallowing NULL state + QCheckBox::setTristate( !dataSource().isEmpty() ); + } + else { + QCheckBox::setTristate( m_tristate == TristateOn ); + } +} + +void KexiDBCheckBox::setDataSource(const QString &ds) +{ + KexiFormDataItemInterface::setDataSource(ds); + updateTristate(); +} + +void KexiDBCheckBox::setDisplayDefaultValue(QWidget *widget, bool displayDefaultValue) +{ + KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue); + // initialize display parameters for default / entered value + KexiDisplayUtils::DisplayParameters * const params + = displayDefaultValue ? m_displayParametersForDefaultValue : m_displayParametersForEnteredValue; +// setFont(params->font); + QPalette pal(palette()); +// pal.setColor(QPalette::Active, QColorGroup::Text, params->textColor); + pal.setColor(QPalette::Active, QColorGroup::Foreground, params->textColor); + setPalette(pal); +} + +#include "kexidbcheckbox.moc" diff --git a/kexi/plugins/forms/widgets/kexidbcheckbox.h b/kexi/plugins/forms/widgets/kexidbcheckbox.h new file mode 100644 index 00000000..d4a68bf3 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbcheckbox.h @@ -0,0 +1,99 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-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. +*/ + +#ifndef KexiDBCheckBox_H +#define KexiDBCheckBox_H + +#include "kexiformdataiteminterface.h" +#include <qcheckbox.h> + +//! @short A db-aware check box +class KEXIFORMUTILS_EXPORT KexiDBCheckBox : public QCheckBox, public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + Q_OVERRIDE( Tristate tristate READ isTristate WRITE setTristate ) + Q_ENUMS( Tristate ) + + public: + KexiDBCheckBox(const QString &text, QWidget *parent, const char *name=0); + virtual ~KexiDBCheckBox(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + virtual void setEnabled(bool enabled); + + enum Tristate { TristateDefault, TristateOn, TristateOff }; + + void setTristate(Tristate tristate); + Tristate isTristate() const; + + /*! Reimplemented after KexiFormDataItemInterface. */ + virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue); + + public slots: + void setDataSource(const QString &ds); + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + void slotStateChanged(int state); + + //! This implementation just disables read only widget + virtual void setReadOnly( bool readOnly ); + + protected: + virtual void setValueInternal(const QVariant& add, bool removeOld); + + //! \return true in isTristate() == TristateDefault and the widget has bound data source + //! or if isTristate() == TristateOn, else false is returned. + bool isTristateInternal() const; + + //! Updates tristate in QCheckBox itself according to m_tristate. + void updateTristate(); + + private: + bool m_invalidState : 1; + bool m_tristateChanged : 1; //!< used in setTristate() + Tristate m_tristate; //!< used in isTristate() and setTristate() +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbcombobox.cpp b/kexi/plugins/forms/widgets/kexidbcombobox.cpp new file mode 100644 index 00000000..19366a15 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbcombobox.cpp @@ -0,0 +1,550 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 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 "kexidbcombobox.h" +#include "kexidblineedit.h" +#include "../kexiformscrollview.h" + +#include <kcombobox.h> +#include <kdebug.h> +#include <kapplication.h> + +#include <qmetaobject.h> +#include <qpainter.h> +#include <qstyle.h> +#include <qdrawutil.h> +#include <qptrdict.h> +#include <qcursor.h> + +#include <kexidb/queryschema.h> +#include <widget/tableview/kexicomboboxpopup.h> +#include <widget/tableview/kexicelleditorfactory.h> +#include <kexiutils/utils.h> + +//! @internal +class KexiDBComboBox::Private +{ + public: + Private() + : popup(0) + , visibleColumnInfo(0) + , subWidgetsWithDisabledEvents(0) + , isEditable(false) + , buttonPressed(false) + , mouseOver(false) + , dataEnteredByHand(true) + { + } + ~Private() + { + delete subWidgetsWithDisabledEvents; + subWidgetsWithDisabledEvents = 0; + } + + KexiComboBoxPopup *popup; + KComboBox *paintedCombo; //!< fake combo used only to pass it as 'this' for QStyle (because styles use <static_cast>) + QSize sizeHint; //!< A cache for KexiDBComboBox::sizeHint(), + //!< rebuilt by KexiDBComboBox::fontChange() and KexiDBComboBox::styleChange() + KexiDB::QueryColumnInfo* visibleColumnInfo; + QPtrDict<char> *subWidgetsWithDisabledEvents; //! used to collect subwidget and its children (if isEditable is false) + bool isEditable : 1; //!< true is the combo box is editable + bool buttonPressed : 1; + bool mouseOver : 1; + bool dataEnteredByHand : 1; + bool designMode : 1; +}; + +//------------------------------------- + +KexiDBComboBox::KexiDBComboBox(QWidget *parent, const char *name, bool designMode) + : KexiDBAutoField(parent, name, designMode, NoLabel) + , KexiComboBoxBase() + , d(new Private()) +{ + setMouseTracking(true); + setFocusPolicy(WheelFocus); + installEventFilter(this); + d->designMode = designMode; + d->paintedCombo = new KComboBox(this); + d->paintedCombo->hide(); + d->paintedCombo->move(0,0); +} + +KexiDBComboBox::~KexiDBComboBox() +{ + delete d; +} + +KexiComboBoxPopup *KexiDBComboBox::popup() const +{ + return d->popup; +} + +void KexiDBComboBox::setPopup(KexiComboBoxPopup *popup) +{ + d->popup = popup; +} + +void KexiDBComboBox::setEditable(bool set) +{ + if (d->isEditable == set) + return; + d->isEditable = set; + d->paintedCombo->setEditable(set); + if (set) + createEditor(); + else { + delete m_subwidget; + m_subwidget = 0; + } + update(); +} + +bool KexiDBComboBox::isEditable() const +{ + return d->isEditable; +} + +void KexiDBComboBox::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + QColorGroup cg( palette().active() ); +// if ( hasFocus() ) +// cg.setColor(QColorGroup::Base, cg.highlight()); +// else + cg.setColor(QColorGroup::Base, paletteBackgroundColor()); //update base color using (reimplemented) bg color + p.setPen(cg.text()); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (d->mouseOver) + flags |= QStyle::Style_MouseOver; + + if ( width() < 5 || height() < 5 ) { + qDrawShadePanel( &p, rect(), cg, FALSE, 2, &cg.brush( QColorGroup::Button ) ); + return; + } + +//! @todo support reverse layout +//bool reverse = QApplication::reverseLayout(); + style().drawComplexControl( QStyle::CC_ComboBox, &p, d->paintedCombo /*this*/, rect(), cg, + flags, (uint)QStyle::SC_All, + (d->buttonPressed ? QStyle::SC_ComboBoxArrow : QStyle::SC_None ) + ); + + if (d->isEditable) { + //if editable, editor paints itself, nothing to do + } + else { //not editable: we need to paint the current item + QRect editorGeometry( this->editorGeometry() ); + if ( hasFocus() ) { + if (0==qstrcmp(style().name(), "windows")) //a hack + p.fillRect( editorGeometry, cg.brush( QColorGroup::Highlight ) ); + QRect r( QStyle::visualRect( style().subRect( QStyle::SR_ComboBoxFocusRect, d->paintedCombo ), this ) ); + r = QRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2); //enlare by 1 pixel each side to avoid covering by the subwidget + style().drawPrimitive( QStyle::PE_FocusRect, &p, + r, cg, flags | QStyle::Style_FocusAtBorder, QStyleOption(cg.highlight())); + } + //todo + } +} + +QRect KexiDBComboBox::editorGeometry() const +{ + QRect r( QStyle::visualRect( + style().querySubControlMetrics(QStyle::CC_ComboBox, d->paintedCombo, + QStyle::SC_ComboBoxEditField), d->paintedCombo ) ); + + //if ((height()-r.bottom())<6) + // r.setBottom(height()-6); + return r; +} + +void KexiDBComboBox::createEditor() +{ + KexiDBAutoField::createEditor(); + if (m_subwidget) { + m_subwidget->setGeometry( editorGeometry() ); + if (!d->isEditable) { + m_subwidget->setCursor(QCursor(Qt::ArrowCursor)); // widgets like listedit have IbeamCursor, we don't want that +//! @todo Qt4: set transparent background, for now we're setting button color + QPalette subwidgetPalette( m_subwidget->palette() ); + subwidgetPalette.setColor(QPalette::Active, QColorGroup::Base, + subwidgetPalette.color(QPalette::Active, QColorGroup::Button)); + m_subwidget->setPalette( subwidgetPalette ); + if (d->subWidgetsWithDisabledEvents) + d->subWidgetsWithDisabledEvents->clear(); + else + d->subWidgetsWithDisabledEvents = new QPtrDict<char>(); + d->subWidgetsWithDisabledEvents->insert(m_subwidget, (char*)1); + m_subwidget->installEventFilter(this); + QObjectList *l = m_subwidget->queryList( "QWidget" ); + for ( QObjectListIt it( *l ); it.current(); ++it ) { + d->subWidgetsWithDisabledEvents->insert(it.current(), (char*)1); + it.current()->installEventFilter(this); + } + delete l; + } + } + updateGeometry(); +} + +void KexiDBComboBox::setLabelPosition(LabelPosition position) +{ + if(m_subwidget) { + if (-1 != m_subwidget->metaObject()->findProperty("frameShape", true)) + m_subwidget->setProperty("frameShape", QVariant((int)QFrame::NoFrame)); + m_subwidget->setGeometry( editorGeometry() ); + } +// KexiSubwidgetInterface *subwidgetInterface = dynamic_cast<KexiSubwidgetInterface*>((QWidget*)m_subwidget); + // update size policy +// if (subwidgetInterface && subwidgetInterface->subwidgetStretchRequired(this)) { + QSizePolicy sizePolicy( this->sizePolicy() ); + if(position == Left) + sizePolicy.setHorData( QSizePolicy::Minimum ); + else + sizePolicy.setVerData( QSizePolicy::Minimum ); + //m_subwidget->setSizePolicy(sizePolicy); + setSizePolicy(sizePolicy); + //} +// } +} + +QRect KexiDBComboBox::buttonGeometry() const +{ + QRect arrowRect( + style().querySubControlMetrics( QStyle::CC_ComboBox, d->paintedCombo, QStyle::SC_ComboBoxArrow) ); + arrowRect = QStyle::visualRect(arrowRect, d->paintedCombo); + arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); // a fix for Motif style + return arrowRect; +} + +bool KexiDBComboBox::handleMousePressEvent(QMouseEvent *e) +{ + if ( e->button() != Qt::LeftButton || d->designMode ) + return true; +/*todo if ( m_discardNextMousePress ) { + d->discardNextMousePress = FALSE; + return; + }*/ + + if ( /*count() &&*/ ( !isEditable() || buttonGeometry().contains( e->pos() ) ) ) { + d->buttonPressed = false; + +/* if ( d->usingListBox() ) { + listBox()->blockSignals( TRUE ); + qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll + listBox()->setCurrentItem(d->current); + listBox()->blockSignals( FALSE ); + popup(); + if ( arrowRect.contains( e->pos() ) ) { + d->arrowPressed = TRUE; + d->arrowDown = TRUE; + repaint( FALSE ); + } + } else {*/ + showPopup(); + return true; + } + return false; +} + +bool KexiDBComboBox::handleKeyPressEvent(QKeyEvent *ke) +{ + const int k = ke->key(); + const bool dropDown = (ke->state() == Qt::NoButton && ((k==Qt::Key_F2 && !d->isEditable) || k==Qt::Key_F4)) + || (ke->state() == Qt::AltButton && k==Qt::Key_Down); + const bool escPressed = ke->state() == Qt::NoButton && k==Qt::Key_Escape; + const bool popupVisible = popup() && popup()->isVisible(); + if ((dropDown || escPressed) && popupVisible) { + popup()->hide(); + return true; + } + else if (dropDown && !popupVisible) { + d->buttonPressed = false; + showPopup(); + return true; + } + else if (popupVisible) { + const bool enterPressed = k==Qt::Key_Enter || k==Qt::Key_Return; + if (enterPressed/* && m_internalEditorValueChanged*/) { + acceptPopupSelection(); + return true; + } + return handleKeyPressForPopup( ke ); + } + + return false; +} + +bool KexiDBComboBox::keyPressed(QKeyEvent *ke) +{ + if (KexiDBAutoField::keyPressed(ke)) + return true; + + const int k = ke->key(); + const bool popupVisible = popup() && popup()->isVisible(); + const bool escPressed = ke->state() == Qt::NoButton && k==Qt::Key_Escape; + if (escPressed && popupVisible) { + popup()->hide(); + return true; + } + if (ke->state() == Qt::NoButton && (k==Qt::Key_PageDown || k==Qt::Key_PageUp) && popupVisible) + return true; + return false; +} + +void KexiDBComboBox::mousePressEvent( QMouseEvent *e ) +{ + if (handleMousePressEvent(e)) + return; + +// QTimer::singleShot( 200, this, SLOT(internalClickTimeout())); +// d->shortClick = TRUE; +// } + KexiDBAutoField::mousePressEvent( e ); +} + +void KexiDBComboBox::mouseDoubleClickEvent( QMouseEvent *e ) +{ + mousePressEvent( e ); +} + +bool KexiDBComboBox::eventFilter( QObject *o, QEvent *e ) +{ + if (o==this) { + if (e->type()==QEvent::Resize) { + d->paintedCombo->resize(size()); + if (m_subwidget) + m_subwidget->setGeometry( editorGeometry() ); + } + else if (e->type()==QEvent::Enter) { + if (!d->isEditable + || /*over button if editable combo*/buttonGeometry().contains( static_cast<QMouseEvent*>(e)->pos() )) + { + d->mouseOver = true; + update(); + } + } + else if (e->type()==QEvent::MouseMove) { + if (d->isEditable) { + const bool overButton = buttonGeometry().contains( static_cast<QMouseEvent*>(e)->pos() ); + if (overButton != d->mouseOver) { + d->mouseOver = overButton; + update(); + } + } + } + else if (e->type()==QEvent::Leave) { + d->mouseOver = false; + update(); + } + else if (e->type()==QEvent::KeyPress) { + // handle F2/F4 + if (handleKeyPressEvent(static_cast<QKeyEvent*>(e))) + return true; + } + else if (e->type()==QEvent::FocusOut) { + if (popup() && popup()->isVisible()) { + popup()->hide(); + undoChanges(); + } + } + } + else if (!d->isEditable && d->subWidgetsWithDisabledEvents && d->subWidgetsWithDisabledEvents->find(o)) { + if (e->type()==QEvent::MouseButtonPress) { + // clicking the subwidget should mean the same as clicking the combo box (i.e. show the popup) + if (handleMousePressEvent(static_cast<QMouseEvent*>(e))) + return true; + } + else if (e->type()==QEvent::KeyPress) { + if (handleKeyPressEvent(static_cast<QKeyEvent*>(e))) + return true; + } + return e->type()!=QEvent::Paint; + } + return KexiDBAutoField::eventFilter( o, e ); +} + +bool KexiDBComboBox::subwidgetStretchRequired(KexiDBAutoField* autoField) const +{ + Q_UNUSED(autoField); + return true; +} + +void KexiDBComboBox::setPaletteBackgroundColor( const QColor & color ) +{ + KexiDBAutoField::setPaletteBackgroundColor(color); + QPalette pal(palette()); + QColorGroup cg(pal.active()); + pal.setColor(QColorGroup::Base, red); + pal.setColor(QColorGroup::Background, red); + pal.setActive(cg); + QWidget::setPalette(pal); + update(); +} + +bool KexiDBComboBox::valueChanged() +{ + kdDebug() << "KexiDataItemInterface::valueChanged(): " << m_origValue.toString() << " ? " << value().toString() << endl; + return m_origValue != value(); +} + +void +KexiDBComboBox::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) +{ + KexiFormDataItemInterface::setColumnInfo(cinfo); +} + +void KexiDBComboBox::setVisibleColumnInfo(KexiDB::QueryColumnInfo* cinfo) +{ + d->visibleColumnInfo = cinfo; + // we're assuming we already have columnInfo() + setColumnInfoInternal(columnInfo(), d->visibleColumnInfo); +} + +KexiDB::QueryColumnInfo* KexiDBComboBox::visibleColumnInfo() const +{ + return d->visibleColumnInfo; +} + +void KexiDBComboBox::moveCursorToEndInInternalEditor() +{ + if (d->isEditable && m_moveCursorToEndInInternalEditor_enabled) + moveCursorToEnd(); +} + +void KexiDBComboBox::selectAllInInternalEditor() +{ + if (d->isEditable && m_selectAllInInternalEditor_enabled) + selectAll(); +} + +void KexiDBComboBox::setValueInternal(const QVariant& add, bool removeOld) +{ + //// use KexiDBAutoField instead of KexiComboBoxBase::setValueInternal + //// expects existing popup(), but we want to have delayed creation + if (popup()) + popup()->hide(); + KexiComboBoxBase::setValueInternal(add, removeOld); +} + +void KexiDBComboBox::setVisibleValueInternal(const QVariant& value) +{ + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + iface->setValue(value, QVariant(), false /*!removeOld*/); +} + +QVariant KexiDBComboBox::visibleValue() +{ + return KexiComboBoxBase::visibleValue(); +} + +void KexiDBComboBox::setValueInInternalEditor(const QVariant& value) +{ + if (!m_setValueInInternalEditor_enabled) + return; + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if(iface) + iface->setValue(value, QVariant(), false/*!removeOld*/); +} + +QVariant KexiDBComboBox::valueFromInternalEditor() +{ + return KexiDBAutoField::value(); +} + +QPoint KexiDBComboBox::mapFromParentToGlobal(const QPoint& pos) const +{ +// const KexiFormScrollView* view = KexiUtils::findParentConst<const KexiFormScrollView>(this, "KexiFormScrollView"); + if (!parentWidget()) + return QPoint(-1,-1); + return parentWidget()->mapToGlobal(pos); +// return view->viewport()->mapToGlobal(pos); +} + +int KexiDBComboBox::popupWidthHint() const +{ + return width(); //popup() ? popup()->width() : 0; +} + +void KexiDBComboBox::fontChange( const QFont & oldFont ) +{ + d->sizeHint = QSize(); //force rebuild the cache + KexiDBAutoField::fontChange(oldFont); +} + +void KexiDBComboBox::styleChange( QStyle& oldStyle ) +{ + KexiDBAutoField::styleChange( oldStyle ); + d->sizeHint = QSize(); //force rebuild the cache + if (m_subwidget) + m_subwidget->setGeometry( editorGeometry() ); +} + +QSize KexiDBComboBox::sizeHint() const +{ + if ( isVisible() && d->sizeHint.isValid() ) + return d->sizeHint; + + const int maxWidth = 7 * fontMetrics().width(QChar('x')) + 18; + const int maxHeight = QMAX( fontMetrics().lineSpacing(), 14 ) + 2; + d->sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, d->paintedCombo, + QSize(maxWidth, maxHeight)).expandedTo(QApplication::globalStrut())); + + return d->sizeHint; +} + +void KexiDBComboBox::editRequested() +{ +} + +void KexiDBComboBox::acceptRequested() +{ + signalValueChanged(); +} + +void KexiDBComboBox::slotRowAccepted(KexiTableItem *item, int row) +{ + d->dataEnteredByHand = false; + KexiComboBoxBase::slotRowAccepted(item, row); + d->dataEnteredByHand = true; +} + +void KexiDBComboBox::beforeSignalValueChanged() +{ + if (d->dataEnteredByHand) { + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget); + if (iface) { + slotInternalEditorValueChanged( iface->value() ); + } + } +} + +void KexiDBComboBox::undoChanges() +{ + KexiDBAutoField::undoChanges(); + KexiComboBoxBase::undoChanges(); +} + +#include "kexidbcombobox.moc" diff --git a/kexi/plugins/forms/widgets/kexidbcombobox.h b/kexi/plugins/forms/widgets/kexidbcombobox.h new file mode 100644 index 00000000..5208d37d --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbcombobox.h @@ -0,0 +1,181 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 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. +*/ + +#ifndef KexiDBComboBox_H +#define KexiDBComboBox_H + +#include "kexidbutils.h" +#include "kexidbautofield.h" +#include <widget/tableview/kexicomboboxbase.h> + +//! @short Combo box widget for Kexi forms +/*! This widget is implemented on top of KexiDBAutoField, + so as it uses KexiDBAutoField's ability of embedding subwidgets, + it can display not only a line edit but also text edit or image box + (more can be added in the future). + A drop-down button is added to mimic native combo box widget's functionality. +*/ +class KEXIFORMUTILS_EXPORT KexiDBComboBox : + public KexiDBAutoField, public KexiComboBoxBase +{ + Q_OBJECT + Q_PROPERTY( bool editable READ isEditable WRITE setEditable ) + //properties from KexiDBAutoField that should not be visible: + Q_OVERRIDE(QColor paletteBackgroundColor READ paletteBackgroundColor WRITE setPaletteBackgroundColor DESIGNABLE true RESET unsetPalette) + Q_OVERRIDE(QColor foregroundLabelColor DESIGNABLE false) + Q_OVERRIDE(QColor backgroundLabelColor DESIGNABLE false) + Q_OVERRIDE(bool autoCaption DESIGNABLE false) + + public: + KexiDBComboBox(QWidget *parent, const char *name=0, bool designMode = true); + virtual ~KexiDBComboBox(); + + //! Implemented for KexiComboBoxBase: form has no 'related data' model (only the full database model) + virtual KexiTableViewColumn *column() const { return 0; } + + //! Implemented for KexiComboBoxBase + virtual KexiDB::Field *field() const { return KexiDBAutoField::field(); } + + //! Implemented for KexiComboBoxBase + virtual QVariant origValue() const { return m_origValue; } + + void setEditable(bool set); + bool isEditable() const; + + virtual void setLabelPosition(LabelPosition position); + + virtual QVariant value() { return KexiComboBoxBase::value(); } + + virtual QVariant visibleValue(); + + //! Reimpemented because to avoid taking value from the internal editor (index is taken from the popup instead) + virtual bool valueChanged(); + + virtual QSize sizeHint() const; + + //! Reimplemented after KexiDBAutoField: jsut sets \a cinfo without initializing a subwidget. + //! Initialization is performed by \ref setVisibleColumnInfo(). + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo); + + /*! Used internally to set visible database column information. + Reimplemented: performs initialization of the subwidget. */ + virtual void setVisibleColumnInfo(KexiDB::QueryColumnInfo* cinfo); + + /*! \return visible database column information for this item. + Reimplemented. */ + virtual KexiDB::QueryColumnInfo* visibleColumnInfo() const; + + const QColor & paletteBackgroundColor() const { return KexiDBAutoField::paletteBackgroundColor(); } + + //! Reimplemented to also set 'this' widget's background color, not only subwidget's. + virtual void setPaletteBackgroundColor( const QColor & color ); + + /*! Undoes changes made to this item - just resets the widget to original value. + Reimplemented after KexiFormDataItemInterface to also revert the visible value + (i.e. text) to the original state. */ + virtual void undoChanges(); + + public slots: + void slotRowAccepted(KexiTableItem *item, int row); + void slotItemSelected(KexiTableItem* item) { KexiComboBoxBase::slotItemSelected(item); } + + protected slots: + void slotInternalEditorValueChanged(const QVariant& v) + { KexiComboBoxBase::slotInternalEditorValueChanged(v); } + + protected: + QRect buttonGeometry() const; + + virtual void paintEvent( QPaintEvent * ); + + virtual void mousePressEvent( QMouseEvent *e ); + + void mouseDoubleClickEvent( QMouseEvent *e ); + + virtual bool eventFilter( QObject *o, QEvent *e ); + + //! \return internal editor's geometry + QRect editorGeometry() const; + + //! Creates editor. Reimplemented, because if the combo box is not editable, + //! editor should not be created. + virtual void createEditor(); + + /*! Reimplemented */ + virtual void styleChange( QStyle& oldStyle ); + + /*! Reimplemented */ + virtual void fontChange( const QFont & oldFont ); + + virtual bool subwidgetStretchRequired(KexiDBAutoField* autoField) const; + + //! Implemented for KexiComboBoxBase + virtual QWidget *internalEditor() const { return /*WidgetWithSubpropertiesInterface*/m_subwidget; } + + //! Implemented for KexiComboBoxBase. Does nothing if the widget is not editable. + virtual void moveCursorToEndInInternalEditor(); + + //! Implemented for KexiComboBoxBase. Does nothing if the widget is not editable. + virtual void selectAllInInternalEditor(); + + //! Implemented for KexiComboBoxBase + virtual void setValueInInternalEditor(const QVariant& value); + + //! Implemented for KexiComboBoxBase + virtual QVariant valueFromInternalEditor(); + + //! Implemented for KexiComboBoxBase + virtual void editRequested(); + + //! Implemented for KexiComboBoxBase + virtual void acceptRequested(); + + //! Implement this to return a position \a pos mapped from parent (e.g. viewport) + //! to global coordinates. QPoint(-1, -1) should be returned if this cannot be computed. + virtual QPoint mapFromParentToGlobal(const QPoint& pos) const; + + //! Implement this to return a hint for popup width. + virtual int popupWidthHint() const; + + virtual void setValueInternal(const QVariant& add, bool removeOld); + + //! Implemented to handle visible value instead of index + virtual void setVisibleValueInternal(const QVariant& value); + + bool handleMousePressEvent(QMouseEvent *e); + + bool handleKeyPressEvent(QKeyEvent *ke); + + //! Implemented for KexiDataItemInterface + virtual void beforeSignalValueChanged(); + + virtual KexiComboBoxPopup *popup() const; + virtual void setPopup(KexiComboBoxPopup *popup); + + /*! Called by top-level form on key press event. + Used for Key_Escape to if the popup is visible, + so the key press won't be consumed to perform "cancel editing". + Also used for grabbing page down/up keys. */ + virtual bool keyPressed(QKeyEvent *ke); + + class Private; + Private * const d; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbdateedit.cpp b/kexi/plugins/forms/widgets/kexidbdateedit.cpp new file mode 100644 index 00000000..32584fce --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbdateedit.cpp @@ -0,0 +1,230 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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 "kexidbdateedit.h" +#include <qlayout.h> +#include <qtoolbutton.h> +#include <kpopupmenu.h> +#include <kdatepicker.h> +#include <kdatetbl.h> + +#include <kexiutils/utils.h> +#include <kexidb/queryschema.h> + +KexiDBDateEdit::KexiDBDateEdit(const QDate &date, QWidget *parent, const char *name) + : QWidget(parent, name), KexiFormDataItemInterface() +{ + m_invalidState = false; + m_cleared = false; + m_readOnly = false; + + m_edit = new QDateEdit(date, this); + m_edit->setAutoAdvance(true); + m_edit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + connect( m_edit, SIGNAL(valueChanged(const QDate&)), this, SLOT(slotValueChanged(const QDate&)) ); + connect( m_edit, SIGNAL(valueChanged(const QDate&)), this, SIGNAL(dateChanged(const QDate&)) ); + + QToolButton* btn = new QToolButton(this); + btn->setText("..."); + btn->setFixedWidth( QFontMetrics(btn->font()).width(" ... ") ); + btn->setPopupDelay(1); //1 ms + +#ifdef QDateTimeEditor_HACK + m_dte_date = KexiUtils::findFirstChild<QDateTimeEditor>(m_edit, "QDateTimeEditor"); +#else + m_dte_date = 0; +#endif + + m_datePickerPopupMenu = new KPopupMenu(0, "date_popup"); + connect(m_datePickerPopupMenu, SIGNAL(aboutToShow()), this, SLOT(slotShowDatePicker())); + m_datePicker = new KDatePicker(m_datePickerPopupMenu, QDate::currentDate(), 0); + + KDateTable *dt = KexiUtils::findFirstChild<KDateTable>(m_datePicker, "KDateTable"); + if (dt) + connect(dt, SIGNAL(tableClicked()), this, SLOT(acceptDate())); + m_datePicker->setCloseButton(true); + m_datePicker->installEventFilter(this); + m_datePickerPopupMenu->insertItem(m_datePicker); + btn->setPopup(m_datePickerPopupMenu); + + QHBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(m_edit, 1); + layout->addWidget(btn, 0); + + setFocusProxy(m_edit); +} + +KexiDBDateEdit::~KexiDBDateEdit() +{ +} + +void KexiDBDateEdit::setInvalidState( const QString& ) +{ + setEnabled(false); + setReadOnly(true); + m_invalidState = true; +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); +} + +void +KexiDBDateEdit::setEnabled(bool enabled) +{ + // prevent the user from reenabling the widget when it is in invalid state + if(enabled && m_invalidState) + return; + QWidget::setEnabled(enabled); +} + +void KexiDBDateEdit::setValueInternal(const QVariant &add, bool removeOld) +{ + int setNumberOnFocus = -1; + QDate d; + QString addString(add.toString()); + if (removeOld) { + if (!addString.isEmpty() && addString[0].latin1()>='0' && addString[0].latin1() <='9') { + setNumberOnFocus = addString[0].latin1()-'0'; + d = QDate(setNumberOnFocus*1000, 1, 1); + } + } + else + d = m_origValue.toDate(); + + m_edit->setDate(d); +} + +QVariant +KexiDBDateEdit::value() +{ + return QVariant(m_edit->date()); +} + +bool KexiDBDateEdit::valueIsNull() +{ + return !m_edit->date().isValid() || m_edit->date().isNull(); +} + +bool KexiDBDateEdit::valueIsEmpty() +{ + return m_cleared; +} + +bool KexiDBDateEdit::isReadOnly() const +{ + //! @todo: data/time edit API has no readonly flag, + //! so use event filter to avoid changes made by keyboard or mouse when m_readOnly==true + return m_readOnly; //!isEnabled(); +} + +void KexiDBDateEdit::setReadOnly(bool set) +{ + m_readOnly = set; +} + +QWidget* +KexiDBDateEdit::widget() +{ + return this; +} + +bool KexiDBDateEdit::cursorAtStart() +{ +#ifdef QDateTimeEditor_HACK + return m_dte_date && m_edit->hasFocus() && m_dte_date->focusSection()==0; +#else + return false; +#endif +} + +bool KexiDBDateEdit::cursorAtEnd() +{ +#ifdef QDateTimeEditor_HACK + return m_dte_date && m_edit->hasFocus() + && m_dte_date->focusSection()==int(m_dte_date->sectionCount()-1); +#else + return false; +#endif +} + +void KexiDBDateEdit::clear() +{ + m_edit->setDate(QDate()); + m_cleared = true; +} + +void +KexiDBDateEdit::slotValueChanged(const QDate&) +{ + m_cleared = false; +} + +void +KexiDBDateEdit::slotShowDatePicker() +{ + QDate date = m_edit->date(); + + m_datePicker->setDate(date); + m_datePicker->setFocus(); + m_datePicker->show(); + m_datePicker->setFocus(); +} + +void +KexiDBDateEdit::acceptDate() +{ + m_edit->setDate(m_datePicker->date()); + m_datePickerPopupMenu->hide(); +} + +bool +KexiDBDateEdit::eventFilter(QObject *o, QEvent *e) +{ + if (o != m_datePicker) + return false; + + switch (e->type()) { + case QEvent::Hide: + m_datePickerPopupMenu->hide(); + break; + case QEvent::KeyPress: + case QEvent::KeyRelease: { + QKeyEvent *ke = (QKeyEvent *)e; + if (ke->key()==Qt::Key_Enter || ke->key()==Qt::Key_Return) { + //accepting picker + acceptDate(); + return true; + } + else if (ke->key()==Qt::Key_Escape) { + //canceling picker + m_datePickerPopupMenu->hide(); + return true; + } + else + m_datePickerPopupMenu->setFocus(); + break; + } + default: + break; + } + return false; +} + +#include "kexidbdateedit.moc" diff --git a/kexi/plugins/forms/widgets/kexidbdateedit.h b/kexi/plugins/forms/widgets/kexidbdateedit.h new file mode 100644 index 00000000..2ad693a8 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbdateedit.h @@ -0,0 +1,118 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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. +*/ + +#ifndef KexiDBDateEdit_H +#define KexiDBDateEdit_H + +#include "kexiformdataiteminterface.h" +#include <qdatetimeedit.h> + +class KPopupMenu; +class KDatePicker; +class QDateTimeEditor; + +//! @short A db-aware date editor +class KEXIFORMUTILS_EXPORT KexiDBDateEdit : public QWidget, public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + // properties copied from QDateEdit + Q_ENUMS( Order ) + Q_PROPERTY( Order order READ order WRITE setOrder DESIGNABLE true) + Q_PROPERTY( QDate date READ date WRITE setDate DESIGNABLE true) + Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance DESIGNABLE true) + Q_PROPERTY( QDate maxValue READ maxValue WRITE setMaxValue DESIGNABLE true) + Q_PROPERTY( QDate minValue READ minValue WRITE setMinValue DESIGNABLE true) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true ) + + public: + enum Order { DMY = QDateEdit::DMY, MDY = QDateEdit::MDY, YMD = QDateEdit::YMD, YDM = QDateEdit::YDM }; + + KexiDBDateEdit(const QDate &date, QWidget *parent, const char *name=0); + virtual ~KexiDBDateEdit(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + virtual void setEnabled(bool enabled); + + // property functions + inline QDate date() const { return m_edit->date(); } + inline void setOrder(Order order) { m_edit->setOrder( (QDateEdit::Order) order); } + inline Order order() const { return (Order)m_edit->order(); } + inline void setAutoAdvance( bool advance ) { m_edit->setAutoAdvance(advance); } + inline bool autoAdvance() const { return m_edit->autoAdvance(); } + inline void setMinValue(const QDate& d) { m_edit->setMinValue(d); } + inline QDate minValue() const { return m_edit->minValue(); } + inline void setMaxValue(const QDate& d) { m_edit->setMaxValue(d); } + inline QDate maxValue() const { return m_edit->maxValue(); } + + signals: + void dateChanged(const QDate &date); + + public slots: + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + inline void setDate(const QDate& date) { m_edit->setDate(date); } + virtual void setReadOnly(bool set); + + protected slots: + void slotValueChanged(const QDate&); + void slotShowDatePicker(); + void acceptDate(); + + protected: + virtual void setValueInternal(const QVariant& add, bool removeOld); + virtual bool eventFilter(QObject *o, QEvent *e); + + private: + KDatePicker *m_datePicker; + QDateEdit *m_edit; + KPopupMenu *m_datePickerPopupMenu; + QDateTimeEditor *m_dte_date; + bool m_invalidState : 1; + bool m_cleared : 1; + bool m_readOnly : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbdatetimeedit.cpp b/kexi/plugins/forms/widgets/kexidbdatetimeedit.cpp new file mode 100644 index 00000000..faaeca66 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbdatetimeedit.cpp @@ -0,0 +1,243 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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 "kexidbdatetimeedit.h" + +#include <qtoolbutton.h> +#include <qlayout.h> +#include <kpopupmenu.h> +#include <kdatepicker.h> +#include <kdatetbl.h> +#include <kexiutils/utils.h> + +KexiDBDateTimeEdit::KexiDBDateTimeEdit(const QDateTime &datetime, QWidget *parent, const char *name) + : QWidget(parent, name), KexiFormDataItemInterface() +{ + m_invalidState = false; + m_cleared = false; + m_readOnly = false; + + m_dateEdit = new QDateEdit(datetime.date(), this); + m_dateEdit->setAutoAdvance(true); + m_dateEdit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); +// m_dateEdit->setFixedWidth( QFontMetrics(m_dateEdit->font()).width("8888-88-88___") ); + connect(m_dateEdit, SIGNAL(valueChanged(const QDate&)), this, SLOT(slotValueChanged())); + connect(m_dateEdit, SIGNAL(valueChanged(const QDate&)), this, SIGNAL(dateTimeChanged())); + + QToolButton* btn = new QToolButton(this); + btn->setText("..."); + btn->setFixedWidth( QFontMetrics(btn->font()).width(" ... ") ); + btn->setPopupDelay(1); //1 ms + + m_timeEdit = new QTimeEdit(datetime.time(), this);; + m_timeEdit->setAutoAdvance(true); + m_timeEdit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + connect(m_timeEdit, SIGNAL(valueChanged(const QTime&)), this, SLOT(slotValueChanged())); + connect(m_timeEdit, SIGNAL(valueChanged(const QTime&)), this, SIGNAL(dateTimeChanged())); + +#ifdef QDateTimeEditor_HACK + m_dte_date = KexiUtils::findFirstChild<QDateTimeEditor>(m_dateEdit, "QDateTimeEditor"); + m_dte_time = KexiUtils::findFirstChild<QDateTimeEditor>(m_timeEdit, "QDateTimeEditor"); +#else + m_dte_date = 0; +#endif + + m_datePickerPopupMenu = new KPopupMenu(0, "date_popup"); + connect(m_datePickerPopupMenu, SIGNAL(aboutToShow()), this, SLOT(slotShowDatePicker())); + m_datePicker = new KDatePicker(m_datePickerPopupMenu, QDate::currentDate(), 0); + + KDateTable *dt = KexiUtils::findFirstChild<KDateTable>(m_datePicker, "KDateTable"); + if (dt) + connect(dt, SIGNAL(tableClicked()), this, SLOT(acceptDate())); + m_datePicker->setCloseButton(true); + m_datePicker->installEventFilter(this); + m_datePickerPopupMenu->insertItem(m_datePicker); + btn->setPopup(m_datePickerPopupMenu); + + QHBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(m_dateEdit, 0); + layout->addWidget(btn, 0); + layout->addWidget(m_timeEdit, 0); + //layout->addStretch(1); + + setFocusProxy(m_dateEdit); +} + +KexiDBDateTimeEdit::~KexiDBDateTimeEdit() +{ +} + +void KexiDBDateTimeEdit::setInvalidState(const QString & /*! @todo paint this text: text*/) +{ + setEnabled(false); + setReadOnly(true); + m_invalidState = true; +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); +} + +void +KexiDBDateTimeEdit::setEnabled(bool enabled) +{ + // prevent the user from reenabling the widget when it is in invalid state + if(enabled && m_invalidState) + return; + QWidget::setEnabled(enabled); +} + +void KexiDBDateTimeEdit::setValueInternal(const QVariant &, bool ) +{ + m_dateEdit->setDate(m_origValue.toDate()); + m_timeEdit->setTime(m_origValue.toTime()); +} + +QVariant +KexiDBDateTimeEdit::value() +{ + return QDateTime(m_dateEdit->date(), m_timeEdit->time()); +} + +bool KexiDBDateTimeEdit::valueIsNull() +{ + return !m_dateEdit->date().isValid() || m_dateEdit->date().isNull() + || !m_timeEdit->time().isValid() || m_timeEdit->time().isNull(); +} + +bool KexiDBDateTimeEdit::valueIsEmpty() +{ + return m_cleared; +} + +bool KexiDBDateTimeEdit::isReadOnly() const +{ + //! @todo: data/time edit API has no readonly flag, + //! so use event filter to avoid changes made by keyboard or mouse when m_readOnly==true + return m_readOnly; //!isEnabled(); +} + +void KexiDBDateTimeEdit::setReadOnly(bool set) +{ + m_readOnly = set; +} + +QWidget* +KexiDBDateTimeEdit::widget() +{ + return m_dateEdit; +} + +bool KexiDBDateTimeEdit::cursorAtStart() +{ +#ifdef QDateTimeEditor_HACK + return m_dte_date && m_dateEdit->hasFocus() && m_dte_date->focusSection()==0; +#else + return false; +#endif +} + +bool KexiDBDateTimeEdit::cursorAtEnd() +{ +#ifdef QDateTimeEditor_HACK + return m_dte_time && m_timeEdit->hasFocus() + && m_dte_time->focusSection()==int(m_dte_time->sectionCount()-1); +#else + return false; +#endif +} + +void KexiDBDateTimeEdit::clear() +{ + m_dateEdit->setDate(QDate()); + m_timeEdit->setTime(QTime()); + m_cleared = true; +} + +void +KexiDBDateTimeEdit::slotValueChanged() +{ + m_cleared = false; +} + +void +KexiDBDateTimeEdit::slotShowDatePicker() +{ + QDate date = m_dateEdit->date(); + + m_datePicker->setDate(date); + m_datePicker->setFocus(); + m_datePicker->show(); + m_datePicker->setFocus(); +} + +void +KexiDBDateTimeEdit::acceptDate() +{ + m_dateEdit->setDate(m_datePicker->date()); + m_datePickerPopupMenu->hide(); +} + +bool +KexiDBDateTimeEdit::eventFilter(QObject *o, QEvent *e) +{ + if (o != m_datePicker) + return false; + + switch (e->type()) { + case QEvent::Hide: + m_datePickerPopupMenu->hide(); + break; + case QEvent::KeyPress: + case QEvent::KeyRelease: { + QKeyEvent *ke = (QKeyEvent *)e; + if (ke->key()==Qt::Key_Enter || ke->key()==Qt::Key_Return) { + //accepting picker + acceptDate(); + return true; + } + else if (ke->key()==Qt::Key_Escape) { + //canceling picker + m_datePickerPopupMenu->hide(); + return true; + } + else + m_datePickerPopupMenu->setFocus(); + break; + } + default: + break; + } + return false; +} + +QDateTime +KexiDBDateTimeEdit::dateTime() const +{ + return QDateTime(m_dateEdit->date(), m_timeEdit->time()); +} + +void +KexiDBDateTimeEdit::setDateTime(const QDateTime &dt) +{ + m_dateEdit->setDate(dt.date()); + m_timeEdit->setTime(dt.time()); +} + +#include "kexidbdatetimeedit.moc" diff --git a/kexi/plugins/forms/widgets/kexidbdatetimeedit.h b/kexi/plugins/forms/widgets/kexidbdatetimeedit.h new file mode 100644 index 00000000..1f185b16 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbdatetimeedit.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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. +*/ + +#ifndef KexiDBDateTimeEdit_H +#define KexiDBDateTimeEdit_H + +#include "kexiformdataiteminterface.h" +#include <qdatetimeedit.h> + +class KDatePicker; +class QDateTimeEditor; +class KPopupMenu; + +//! @short A db-aware datetime editor +class KEXIFORMUTILS_EXPORT KexiDBDateTimeEdit : public QWidget, public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + // properties copied from QDateTimeEdit + Q_PROPERTY( QDateTime dateTime READ dateTime WRITE setDateTime ) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true ) + + public: + enum Order { DMY, MDY, YMD, YDM }; + + KexiDBDateTimeEdit(const QDateTime &datetime, QWidget *parent, const char *name=0); + virtual ~KexiDBDateTimeEdit(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + virtual void setEnabled(bool enabled); + + // property functions + QDateTime dateTime() const; + + signals: + void dateTimeChanged(); + + public slots: + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + void setDateTime(const QDateTime &dt); + virtual void setReadOnly(bool set); + + protected: + virtual void setValueInternal(const QVariant& add, bool removeOld); + virtual bool eventFilter(QObject *o, QEvent *e); + + protected slots: + void slotValueChanged(); + void slotShowDatePicker(); + void acceptDate(); + + private: + KDatePicker *m_datePicker; + QDateEdit* m_dateEdit; + QTimeEdit* m_timeEdit; + QDateTimeEditor *m_dte_date, *m_dte_time; + KPopupMenu *m_datePickerPopupMenu; + bool m_invalidState : 1; + bool m_cleared : 1; + bool m_readOnly : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbdoublespinbox.cpp b/kexi/plugins/forms/widgets/kexidbdoublespinbox.cpp new file mode 100644 index 00000000..67a2c1a6 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbdoublespinbox.cpp @@ -0,0 +1,113 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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 "kexidbdoublespinbox.h" + +#include <qlineedit.h> + +KexiDBDoubleSpinBox::KexiDBDoubleSpinBox(QWidget *parent, const char *name) + : KDoubleSpinBox(parent, name) , KexiFormDataItemInterface() +{ + connect(this, SIGNAL(valueChanged(double)), this, SLOT(slotValueChanged())); +} + +KexiDBDoubleSpinBox::~KexiDBDoubleSpinBox() +{ +} + +void KexiDBDoubleSpinBox::setInvalidState( const QString& displayText ) +{ + m_invalidState = true; + setEnabled(false); + setReadOnly(true); +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); + setSpecialValueText(displayText); + KDoubleSpinBox::setValue(minValue()); +} + +void +KexiDBDoubleSpinBox::setEnabled(bool enabled) +{ + // prevent the user from reenabling the widget when it is in invalid state + if(enabled && m_invalidState) + return; + KDoubleSpinBox::setEnabled(enabled); +} + +void KexiDBDoubleSpinBox::setValueInternal(const QVariant&, bool ) +{ + KDoubleSpinBox::setValue(m_origValue.toDouble()); +} + +QVariant +KexiDBDoubleSpinBox::value() +{ + return KDoubleSpinBox::value(); +} + +void KexiDBDoubleSpinBox::slotValueChanged() +{ + signalValueChanged(); +} + +bool KexiDBDoubleSpinBox::valueIsNull() +{ + return cleanText().isEmpty(); +} + +bool KexiDBDoubleSpinBox::valueIsEmpty() +{ + return false; +} + +bool KexiDBDoubleSpinBox::isReadOnly() const +{ + return editor()->isReadOnly(); +} + +void KexiDBDoubleSpinBox::setReadOnly(bool set) +{ + editor()->setReadOnly(set); +} + +QWidget* +KexiDBDoubleSpinBox::widget() +{ + return this; +} + +bool KexiDBDoubleSpinBox::cursorAtStart() +{ + return false; //! \todo ? +} + +bool KexiDBDoubleSpinBox::cursorAtEnd() +{ + return false; //! \todo ? +} + +void KexiDBDoubleSpinBox::clear() +{ + KDoubleSpinBox::setValue(minValue()); +} + +#include "kexidbdoublespinbox.moc" diff --git a/kexi/plugins/forms/widgets/kexidbdoublespinbox.h b/kexi/plugins/forms/widgets/kexidbdoublespinbox.h new file mode 100644 index 00000000..c6bc627d --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbdoublespinbox.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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. +*/ + +#ifndef KexiDBDoubleSpinBox_H +#define KexiDBDoubleSpinBox_H + +#include "kexiformdataiteminterface.h" +#include <qwidget.h> +#include <knuminput.h> + +//! @short A db-aware int spin box +class KEXIFORMUTILS_EXPORT KexiDBDoubleSpinBox : public KDoubleSpinBox, public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true ) + + public: + KexiDBDoubleSpinBox(QWidget *parent, const char *name=0); + virtual ~KexiDBDoubleSpinBox(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + public slots: + virtual void setEnabled(bool enabled); + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + void slotValueChanged(); + virtual void setReadOnly(bool set); + + protected: + virtual void setValueInternal(const QVariant& add, bool removeOld); + + private: + bool m_invalidState : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbform.cpp b/kexi/plugins/forms/widgets/kexidbform.cpp new file mode 100644 index 00000000..cff12c7c --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbform.cpp @@ -0,0 +1,714 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl> + + This library 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 library 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 library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <qobjectlist.h> +#include <qpainter.h> +#include <qcursor.h> +#include <qapplication.h> +#include <qfocusdata.h> + +#include <kdebug.h> + +#include "kexidbform.h" +#include "kexiformpart.h" +#include "kexiformscrollview.h" + +#include <formeditor/objecttree.h> +#include <formeditor/formmanager.h> +#include <formeditor/widgetlibrary.h> +#include <widget/tableview/kexidataawareobjectiface.h> +#include <widget/kexiscrollview.h> +#include <kexiutils/utils.h> + +//! @internal +class KexiDBForm::Private +{ + public: + Private() + : dataAwareObject(0) + , orderedFocusWidgetsIterator(orderedFocusWidgets) + , autoTabStops(false) + , popupFocused(false) + { + } + + ~Private() + { + } + + //! \return index of data-aware widget \a widget + int indexOfDataAwareWidget(QWidget *widget) const + { + if (!dynamic_cast<KexiDataItemInterface*>(widget)) + return -1; + return indexOfDataItem( dynamic_cast<KexiDataItemInterface*>(widget) ); + } + + //! \return index of data item \a item, or -1 if not found + int indexOfDataItem( KexiDataItemInterface* item ) const + { + QMapConstIterator<KexiDataItemInterface*, uint> indicesForDataAwareWidgetsIt( + indicesForDataAwareWidgets.find(item)); + if (indicesForDataAwareWidgetsIt == indicesForDataAwareWidgets.constEnd()) + return -1; + kexipluginsdbg << "KexiDBForm: column # for item: " + << indicesForDataAwareWidgetsIt.data() << endl; + return indicesForDataAwareWidgetsIt.data(); + } + + //! Sets orderedFocusWidgetsIterator member to a position pointing to \a widget + void setOrderedFocusWidgetsIteratorTo( QWidget *widget ) + { + if (orderedFocusWidgetsIterator.current() == widget) + return; + orderedFocusWidgetsIterator.toFirst(); + while (orderedFocusWidgetsIterator.current() && orderedFocusWidgetsIterator.current()!=widget) + ++orderedFocusWidgetsIterator; + } + + KexiDataAwareObjectInterface* dataAwareObject; + //! ordered list of focusable widgets (can be both data-widgets or buttons, etc.) + QPtrList<QWidget> orderedFocusWidgets; + //! ordered list of data-aware widgets + QPtrList<QWidget> orderedDataAwareWidgets; + QMap<KexiDataItemInterface*, uint> indicesForDataAwareWidgets; //!< a subset of orderedFocusWidgets mapped to indices + QPtrListIterator<QWidget> orderedFocusWidgetsIterator; + QPixmap buffer; //!< stores grabbed entire form's area for redraw + QRect prev_rect; //!< previously selected rectangle +// QGuardedPtr<QWidget> widgetFocusedBeforePopup; + bool autoTabStops : 1; + bool popupFocused : 1; //!< used in KexiDBForm::eventFilter() +}; + +//======================== + +KexiDBForm::KexiDBForm(QWidget *parent, KexiDataAwareObjectInterface* dataAwareObject, + const char *name/*, KexiDB::Connection *conn*/) + : KexiDBFormBase(parent, name) + , KexiFormDataItemInterface() + , d(new Private()) +{ + installEventFilter(this); +//test setDisplayMode( KexiGradientWidget::SimpleGradient ); + editedItem = 0; + d->dataAwareObject = dataAwareObject; + m_hasFocusableWidget = false; + + kexipluginsdbg << "KexiDBForm::KexiDBForm(): " << endl; + setCursor(QCursor(Qt::ArrowCursor)); //to avoid keeping Size cursor when moving from form's boundaries + setAcceptDrops( true ); +} + +KexiDBForm::~KexiDBForm() +{ + kexipluginsdbg << "KexiDBForm::~KexiDBForm(): close" << endl; + delete d; +} + +KexiDataAwareObjectInterface* KexiDBForm::dataAwareObject() const { return d->dataAwareObject; } + +//repaint all children widgets +static void repaintAll(QWidget *w) +{ + QObjectList *list = w->queryList("QWidget"); + QObjectListIt it(*list); + for (QObject *obj; (obj=it.current()); ++it ) { + static_cast<QWidget*>(obj)->repaint(); + } + delete list; +} + +void +KexiDBForm::drawRect(const QRect& r, int type) +{ + QValueList<QRect> l; + l.append(r); + drawRects(l, type); +} + +void +KexiDBForm::drawRects(const QValueList<QRect> &list, int type) +{ + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + if (d->prev_rect.isValid()) { + //redraw prev. selection's rectangle + p.drawPixmap( QPoint(d->prev_rect.x()-2, d->prev_rect.y()-2), d->buffer, + QRect(d->prev_rect.x()-2, d->prev_rect.y()-2, d->prev_rect.width()+4, d->prev_rect.height()+4)); + } + p.setBrush(QBrush::NoBrush); + if(type == 1) // selection rect + p.setPen(QPen(white, 1, Qt::DotLine)); + else if(type == 2) // insert rect + p.setPen(QPen(white, 2)); + p.setRasterOp(XorROP); + + d->prev_rect = QRect(); + QValueList<QRect>::ConstIterator endIt = list.constEnd(); + for(QValueList<QRect>::ConstIterator it = list.constBegin(); it != endIt; ++it) { + p.drawRect(*it); + if (d->prev_rect.isValid()) + d->prev_rect = d->prev_rect.unite(*it); + else + d->prev_rect = *it; + } + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); +} + +void +KexiDBForm::initBuffer() +{ + repaintAll(this); + d->buffer.resize( width(), height() ); + d->buffer = QPixmap::grabWindow( winId() ); + d->prev_rect = QRect(); +} + +void +KexiDBForm::clearForm() +{ + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + //redraw entire form surface + p.drawPixmap( QPoint(0,0), d->buffer, QRect(0,0,d->buffer.width(), d->buffer.height()) ); + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); + + repaintAll(this); +} + +void +KexiDBForm::highlightWidgets(QWidget *from, QWidget *to)//, const QPoint &point) +{ + QPoint fromPoint, toPoint; + if(from && from->parentWidget() && (from != this)) + fromPoint = from->parentWidget()->mapTo(this, from->pos()); + if(to && to->parentWidget() && (to != this)) + toPoint = to->parentWidget()->mapTo(this, to->pos()); + + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + if (d->prev_rect.isValid()) { + //redraw prev. selection's rectangle + p.drawPixmap( QPoint(d->prev_rect.x(), d->prev_rect.y()), d->buffer, + QRect(d->prev_rect.x(), d->prev_rect.y(), d->prev_rect.width(), d->prev_rect.height())); + } + + p.setPen( QPen(Qt::red, 2) ); + + if(to) + { + QPixmap pix1 = QPixmap::grabWidget(from); + QPixmap pix2 = QPixmap::grabWidget(to); + + if((from != this) && (to != this)) + p.drawLine( from->parentWidget()->mapTo(this, from->geometry().center()), to->parentWidget()->mapTo(this, to->geometry().center()) ); + + p.drawPixmap(fromPoint.x(), fromPoint.y(), pix1); + p.drawPixmap(toPoint.x(), toPoint.y(), pix2); + + if(to == this) + p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4); + else + p.drawRoundRect(toPoint.x(), toPoint.y(), to->width(), to->height(), 5, 5); + } + + if(from == this) + p.drawRoundRect(2, 2, width()-4, height()-4, 4, 4); + else + p.drawRoundRect(fromPoint.x(), fromPoint.y(), from->width(), from->height(), 5, 5); + + if((to == this) || (from == this)) + d->prev_rect = QRect(0, 0, d->buffer.width(), d->buffer.height()); + else if(to) + { + d->prev_rect.setX( (fromPoint.x() < toPoint.x()) ? (fromPoint.x() - 5) : (toPoint.x() - 5) ); + d->prev_rect.setY( (fromPoint.y() < toPoint.y()) ? (fromPoint.y() - 5) : (toPoint.y() - 5) ); + d->prev_rect.setRight( (fromPoint.x() < toPoint.x()) ? (toPoint.x() + to->width() + 10) : (fromPoint.x() + from->width() + 10) ); + d->prev_rect.setBottom( (fromPoint.y() < toPoint.y()) ? (toPoint.y() + to->height() + 10) : (fromPoint.y() + from->height() + 10) ) ; + } + else + d->prev_rect = QRect(fromPoint.x()- 5, fromPoint.y() -5, from->width() + 10, from->height() + 10); + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); +} + +QSize +KexiDBForm::sizeHint() const +{ + //todo: find better size (user configured?) + return QSize(400,300); +} + +void KexiDBForm::setInvalidState( const QString& displayText ) +{ + Q_UNUSED( displayText ); + + //! @todo draw "invalid data source" text on the surface? +} + +bool KexiDBForm::autoTabStops() const +{ + return d->autoTabStops; +} + +void KexiDBForm::setAutoTabStops(bool set) +{ + d->autoTabStops = set; +} + +QPtrList<QWidget>* KexiDBForm::orderedFocusWidgets() const +{ + return &d->orderedFocusWidgets; +} + +QPtrList<QWidget>* KexiDBForm::orderedDataAwareWidgets() const +{ + return &d->orderedDataAwareWidgets; +} + +void KexiDBForm::updateTabStopsOrder(KFormDesigner::Form* form) +{ + QWidget *fromWidget = 0; + //QWidget *topLevelWidget = form->widget()->topLevelWidget(); +//js form->updateTabStopsOrder(); //certain widgets can have now updated focusPolicy properties, fix this + uint numberOfDataAwareWidgets = 0; +// if (d->orderedFocusWidgets.isEmpty()) { + //generate a new list + for (KFormDesigner::ObjectTreeListIterator it(form->tabStopsIterator()); it.current(); ++it) { + if (it.current()->widget()->focusPolicy() & QWidget::TabFocus) { + //this widget has tab focus: + it.current()->widget()->installEventFilter(this); + //also filter events for data-aware children of this widget (i.e. KexiDBAutoField's editors) + QObjectList *children = it.current()->widget()->queryList("QWidget"); + for (QObjectListIt childrenIt(*children); childrenIt.current(); ++childrenIt) { + // if (dynamic_cast<KexiFormDataItemInterface*>(childrenIt.current())) { + kexipluginsdbg << "KexiDBForm::updateTabStopsOrder(): also adding '" + << childrenIt.current()->className() << " " << childrenIt.current()->name() + << "' child to filtered widgets" << endl; + //it.current()->widget()->installEventFilter(static_cast<QWidget*>(childrenIt.current())); + childrenIt.current()->installEventFilter(this); + // } + } + delete children; + if (fromWidget) { + kexipluginsdbg << "KexiDBForm::updateTabStopsOrder() tab order: " << fromWidget->name() + << " -> " << it.current()->widget()->name() << endl; + // setTabOrder( fromWidget, it.current()->widget() ); + } + fromWidget = it.current()->widget(); + d->orderedFocusWidgets.append( it.current()->widget() ); + } + + KexiFormDataItemInterface* dataItem = dynamic_cast<KexiFormDataItemInterface*>( it.current()->widget() ); + if (dataItem && !dataItem->dataSource().isEmpty()) { + kexipluginsdbg << "#" << numberOfDataAwareWidgets << ": " + << dataItem->dataSource() << " (" << it.current()->widget()->name() << ")" << endl; + +// /*! @todo d->indicesForDataAwareWidgets SHOULDNT BE UPDATED HERE BECAUSE +// THERE CAN BE ALSO NON-TABSTOP DATA WIDGETS! +// */ + d->indicesForDataAwareWidgets.replace( + dataItem, + numberOfDataAwareWidgets ); + numberOfDataAwareWidgets++; + + d->orderedDataAwareWidgets.append( it.current()->widget() ); + } + }//for +// } +/* else { + //restore ordering + for (QPtrListIterator<QWidget> it(d->orderedFocusWidgets); it.current(); ++it) { + if (fromWidget) { + kdDebug() << "KexiDBForm::updateTabStopsOrder() tab order: " << fromWidget->name() + << " -> " << it.current()->name() << endl; + setTabOrder( fromWidget, it.current() ); + } + fromWidget = it.current(); + } +// SET_FOCUS_USING_REASON(focusWidget(), QFocusEvent::Tab); + }*/ +} + +void KexiDBForm::updateTabStopsOrder() +{ + for (QPtrListIterator<QWidget> it( d->orderedFocusWidgets ); it.current();) { + if (! (it.current()->focusPolicy() & QWidget::TabFocus)) + d->orderedFocusWidgets.remove( it.current() ); + else + ++it; + } +} + +void KexiDBForm::updateReadOnlyFlags() +{ + for (QPtrListIterator<QWidget> it(d->orderedDataAwareWidgets); it.current(); ++it) { + KexiFormDataItemInterface* dataItem = dynamic_cast<KexiFormDataItemInterface*>( it.current() ); + if (dataItem && !dataItem->dataSource().isEmpty()) { + if (dataAwareObject()->isReadOnly()) { + dataItem->setReadOnly( true ); + } + } + } +} + +bool KexiDBForm::eventFilter( QObject * watched, QEvent * e ) +{ + //kexipluginsdbg << e->type() << endl; + if (e->type()==QEvent::Resize && watched == this) + kexipluginsdbg << "RESIZE" << endl; + if (e->type()==QEvent::KeyPress) { + if (preview()) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + const int key = ke->key(); + bool tab = ke->state() == Qt::NoButton && key == Qt::Key_Tab; + bool backtab = ((ke->state() == Qt::NoButton || ke->state() == Qt::ShiftButton) && key == Qt::Key_Backtab) + || (ke->state() == Qt::ShiftButton && key == Qt::Key_Tab); + QObject *o = watched; //focusWidget(); + QWidget* realWidget = dynamic_cast<QWidget*>(o); //will beused below (for tab/backtab handling) + + if (!tab && !backtab) { + //for buttons, left/up and right/down keys act like tab/backtab (see qbutton.cpp) + if (realWidget->inherits("QButton")) { + if (ke->state() == Qt::NoButton && (key == Qt::Key_Right || key == Qt::Key_Down)) + tab = true; + else if (ke->state() == Qt::NoButton && (key == Qt::Key_Left || key == Qt::Key_Up)) + backtab = true; + } + } + + if (!tab && !backtab) { + // allow the editor widget to grab the key press event + while (true) { + if (!o || o == dynamic_cast<QObject*>(d->dataAwareObject)) + break; + if (dynamic_cast<KexiFormDataItemInterface*>(o)) { + realWidget = dynamic_cast<QWidget*>(o); //will be used below + if (realWidget == this) //we have encountered 'this' form surface, give up + return false; + KexiFormDataItemInterface* dataItemIface = dynamic_cast<KexiFormDataItemInterface*>(o); + while (dataItemIface) { + if (dataItemIface->keyPressed(ke)) + return false; + dataItemIface = dynamic_cast<KexiFormDataItemInterface*>(dataItemIface->parentInterface()); //try in parent, e.g. in combobox + } + break; + } + o = o->parent(); + } + // try to handle global shortcuts at the KexiDataAwareObjectInterface + // level (e.g. for "next record" action) + int curRow = d->dataAwareObject->currentRow(); + int curCol = d->dataAwareObject->currentColumn(); + bool moveToFirstField; //if true, we'll move focus to the first field (in tab order) + bool moveToLastField; //if true, we'll move focus to the first field (in tab order) + if (! (ke->state() == Qt::NoButton && (key == Qt::Key_Home + || key == Qt::Key_End || key == Qt::Key_Down || key == Qt::Key_Up)) + /* ^^ home/end/down/up are already handled by widgets */ + && d->dataAwareObject->handleKeyPress( + ke, curRow, curCol, false/*!fullRowSelection*/, &moveToFirstField, &moveToLastField)) + { + if (ke->isAccepted()) + return true; + QWidget* widgetToFocus; + if (moveToFirstField) { + widgetToFocus = d->orderedFocusWidgets.first(); //? + curCol = d->indexOfDataAwareWidget( widgetToFocus ); + } + else if (moveToLastField) { + widgetToFocus = d->orderedFocusWidgets.last(); //? + curCol = d->indexOfDataAwareWidget( widgetToFocus ); + } + else + widgetToFocus = d->orderedDataAwareWidgets.at( curCol ); //? + + d->dataAwareObject->setCursorPosition( curRow, curCol ); + + if (widgetToFocus) + widgetToFocus->setFocus(); + else + kexipluginswarn << "KexiDBForm::eventFilter(): widgetToFocus not found!" << endl; + + ke->accept(); + return true; + } + if (key == Qt::Key_Delete && ke->state()==Qt::ControlButton) { +//! @todo remove hardcoded shortcuts: can be reconfigured... + d->dataAwareObject->deleteCurrentRow(); + return true; + } + } + // handle Esc key + if (ke->state() == Qt::NoButton && key == Qt::Key_Escape) { + //cancel field editing/row editing if possible + if (d->dataAwareObject->cancelEditor()) + return true; + else if (d->dataAwareObject->cancelRowEdit()) + return true; + return false; // canceling not needed - pass the event to the active widget + } + // jstaniek: Fix for Qt bug (handling e.g. Alt+2, Ctrl+2 keys on every platform) + // It's important because we're using alt+2 short cut by default + // Damn! I've reported this to Trolltech in November 2004 - still not fixed. + if (ke->isAccepted() && (ke->state() & Qt::AltButton) && ke->text()>="0" && ke->text()<="9") + return true; + + if (tab || backtab) { + //the watched widget can be a subwidget of a real widget, e.g. a drop down button of image box: find it + while (!KexiFormPart::library()->widgetInfoForClassName(realWidget->className())) + realWidget = realWidget->parentWidget(); + if (!realWidget) + return true; //ignore + //the watched widget can be a subwidget of a real widget, e.g. autofield: find it + //QWidget* realWidget = static_cast<QWidget*>(watched); + while (dynamic_cast<KexiDataItemInterface*>(realWidget) && dynamic_cast<KexiDataItemInterface*>(realWidget)->parentInterface()) + realWidget = dynamic_cast<QWidget*>( dynamic_cast<KexiDataItemInterface*>(realWidget)->parentInterface() ); + + d->setOrderedFocusWidgetsIteratorTo( realWidget ); + kexipluginsdbg << realWidget->name() << endl; + + // find next/prev widget to focus + QWidget *widgetToUnfocus = realWidget; + QWidget *widgetToFocus = 0; + bool wasAtFirstWidget = false; //used to protect against infinite loop + while (true) { + if (tab) { + if (d->orderedFocusWidgets.first() && realWidget == d->orderedFocusWidgets.last()) { + if (wasAtFirstWidget) + break; + d->orderedFocusWidgetsIterator.toFirst(); + wasAtFirstWidget = true; + } + else if (realWidget == d->orderedFocusWidgetsIterator.current()) { + ++d->orderedFocusWidgetsIterator; //next + } + else + return true; //ignore + } + else {//backtab + if (d->orderedFocusWidgets.last() && realWidget == d->orderedFocusWidgets.first()) { + d->orderedFocusWidgetsIterator.toLast(); + } + else if (realWidget == d->orderedFocusWidgetsIterator.current()) { + --d->orderedFocusWidgetsIterator; //prev + } + else + return true; //ignore + } + + widgetToFocus = d->orderedFocusWidgetsIterator.current(); + + QObject *pageFor_widgetToFocus = 0; + KFormDesigner::TabWidget *tabWidgetFor_widgetToFocus + = KFormDesigner::findParent<KFormDesigner::TabWidget>( + widgetToFocus, "KFormDesigner::TabWidget", pageFor_widgetToFocus); + if (tabWidgetFor_widgetToFocus && tabWidgetFor_widgetToFocus->currentPage()!=pageFor_widgetToFocus) { + realWidget = widgetToFocus; + continue; //the new widget to focus is placed on invisible tab page: move to next widget + } + break; + }//while + + //set focus, but don't use just setFocus() because certain widgets + //behaves differently (e.g. QLineEdit calls selectAll()) when + //focus event's reason is QFocusEvent::Tab + if (widgetToFocus->focusProxy()) + widgetToFocus = widgetToFocus->focusProxy(); + if (widgetToFocus && d->dataAwareObject->acceptEditor()) { + if (tab) { + //try to accept this will validate the current input (if any) + KexiUtils::unsetFocusWithReason(widgetToUnfocus, QFocusEvent::Tab); + KexiUtils::setFocusWithReason(widgetToFocus, QFocusEvent::Tab); + kexipluginsdbg << "focusing " << widgetToFocus->name() << endl; + } + else {//backtab + KexiUtils::unsetFocusWithReason(widgetToUnfocus, QFocusEvent::Backtab); + //set focus, see above note + KexiUtils::setFocusWithReason(d->orderedFocusWidgetsIterator.current(), QFocusEvent::Backtab); + kexipluginsdbg << "focusing " << d->orderedFocusWidgetsIterator.current()->name() << endl; + } + } + return true; + } + } + } + else if (e->type()==QEvent::FocusIn) { + bool focusDataWidget = preview(); + if (static_cast<QFocusEvent*>(e)->reason()==QFocusEvent::Popup) { + kdDebug() << "->>> focus IN, popup" <<endl; + focusDataWidget = !d->popupFocused; + d->popupFocused = false; +// if (d->widgetFocusedBeforePopup) { +// watched = d->widgetFocusedBeforePopup; +// d->widgetFocusedBeforePopup = 0; +// } + } + + if (focusDataWidget) { + kexipluginsdbg << "KexiDBForm: FocusIn: " << watched->className() << " " << watched->name() << endl; + if (d->dataAwareObject) { + QWidget *dataItem = dynamic_cast<QWidget*>(watched); + while (dataItem) { + while (dataItem && !dynamic_cast<KexiDataItemInterface*>(dataItem)) + dataItem = dataItem->parentWidget(); + if (!dataItem) + break; + kexipluginsdbg << "KexiDBForm: FocusIn: FOUND " << dataItem->className() << " " << dataItem->name() << endl; + + const int index = d->indexOfDataAwareWidget(dataItem); + if (index>=0) { + kexipluginsdbg << "KexiDBForm: moving cursor to column #" << index << endl; + editedItem = 0; + if ((int)index!=d->dataAwareObject->currentColumn()) { + d->dataAwareObject->setCursorPosition( d->dataAwareObject->currentRow(), index /*column*/ ); + } + break; + } + else + dataItem = dataItem->parentWidget(); + + dataItem->update(); + } + } + } + } + else if (e->type()==QEvent::FocusOut) { + if (static_cast<QFocusEvent*>(e)->reason()==QFocusEvent::Popup) { + //d->widgetFocusedBeforePopup = (QWidget*)watched; + d->popupFocused = true; + } + else + d->popupFocused = false; +// d->widgetFocusedBeforePopup = 0; +// kdDebug() << "e->type()==QEvent::FocusOut " << watched->className() << " " <<watched->name() << endl; +// UNSET_FOCUS_USING_REASON(watched, static_cast<QFocusEvent*>(e)->reason()); + } + return KexiDBFormBase::eventFilter(watched, e); +} + +bool KexiDBForm::valueIsNull() +{ + return true; +} + +bool KexiDBForm::valueIsEmpty() +{ + return true; +} + +bool KexiDBForm::isReadOnly() const +{ + if (d->dataAwareObject) + return d->dataAwareObject->isReadOnly(); +//! @todo ? + return false; +} + +void KexiDBForm::setReadOnly( bool readOnly ) +{ + if (d->dataAwareObject) + d->dataAwareObject->setReadOnly( readOnly ); //??? +} + +QWidget* KexiDBForm::widget() +{ + return this; +} + +bool KexiDBForm::cursorAtStart() +{ + return false; +} + +bool KexiDBForm::cursorAtEnd() +{ + return false; +} + +void KexiDBForm::clear() +{ + //! @todo clear all fields? +} + +bool KexiDBForm::preview() const { + return dynamic_cast<KexiScrollView*>(d->dataAwareObject) + ? dynamic_cast<KexiScrollView*>(d->dataAwareObject)->preview() : false; +} + +void KexiDBForm::dragMoveEvent( QDragMoveEvent *e ) +{ + KexiDBFormBase::dragMoveEvent( e ); + emit handleDragMoveEvent(e); +} + +void KexiDBForm::dropEvent( QDropEvent *e ) +{ + KexiDBFormBase::dropEvent( e ); + emit handleDropEvent(e); +} + +void KexiDBForm::setCursor( const QCursor & cursor ) +{ + //js: empty, to avoid fscking problems with random cursors! + //! @todo? + + if (KFormDesigner::FormManager::self()->isInserting()) //exception + KexiDBFormBase::setCursor(cursor); +} + +//! @todo: Qt4? XORed resize rectangles instead of black widgets +/* +void KexiDBForm::paintEvent( QPaintEvent *e ) +{ + QPainter p; + p.begin(this, true); + bool unclipped = testWFlags( WPaintUnclipped ); + setWFlags( WPaintUnclipped ); + + p.setPen(white); + p.setRasterOp(XorROP); + p.drawLine(e->rect().topLeft(), e->rect().bottomRight()); + + if (!unclipped) + clearWFlags( WPaintUnclipped ); + p.end(); + KexiDBFormBase::paintEvent(e); +} +*/ + +#include "kexidbform.moc" diff --git a/kexi/plugins/forms/widgets/kexidbform.h b/kexi/plugins/forms/widgets/kexidbform.h new file mode 100644 index 00000000..81a71bba --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbform.h @@ -0,0 +1,139 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2005-2007 Jaroslaw Staniek <js@iidea.pl> + + This library 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 library 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 library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ +#ifndef KEXIDBFORM_H +#define KEXIDBFORM_H + +#include <qpixmap.h> + +#include <formeditor/form.h> +#include "../kexiformdataiteminterface.h" + +#ifdef KEXI_USE_GRADIENT_WIDGET +#include <kexigradientwidget.h> +# define KexiDBFormBase KexiGradientWidget +#else +# define KexiDBFormBase QWidget +#endif + +class KexiDataAwareObjectInterface; +class KexiFormScrollView; + +//! @short A DB-aware form widget, acting as form's toplevel widget +class KEXIFORMUTILS_EXPORT KexiDBForm : + public KexiDBFormBase, + public KFormDesigner::FormWidget, + public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + Q_PROPERTY(bool autoTabStops READ autoTabStops WRITE setAutoTabStops DESIGNABLE true) + //original "size" property is not designable, so here's a custom (not storable) replacement + Q_PROPERTY( QSize sizeInternal READ sizeInternal WRITE resizeInternal DESIGNABLE true STORED false ) + public: + KexiDBForm(QWidget *parent, KexiDataAwareObjectInterface* dataAwareObject, const char *name="kexi_dbform"); + virtual ~KexiDBForm(); + + KexiDataAwareObjectInterface* dataAwareObject() const; + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + + //! no effect + QVariant value() { return QVariant(); } + + virtual void setInvalidState( const QString& displayText ); + + virtual void drawRect(const QRect& r, int type); + virtual void drawRects(const QValueList<QRect> &list, int type); + virtual void initBuffer(); + virtual void clearForm(); + virtual void highlightWidgets(QWidget *from, QWidget *to/*, const QPoint &p*/); + + virtual QSize sizeHint() const; + + bool autoTabStops() const; + + QPtrList<QWidget>* orderedFocusWidgets() const; + + QPtrList<QWidget>* orderedDataAwareWidgets() const; + + void updateTabStopsOrder(KFormDesigner::Form* form); + + void updateTabStopsOrder(); + + virtual bool eventFilter ( QObject * watched, QEvent * e ); + + virtual bool valueIsNull(); + virtual bool valueIsEmpty(); + virtual bool isReadOnly() const; + virtual QWidget* widget(); + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + bool preview() const; + + virtual void setCursor( const QCursor & cursor ); + + public slots: + void setAutoTabStops(bool set); + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + + //! This implementation just disables read only widget + virtual void setReadOnly( bool readOnly ); + + //! @internal for sizeInternal property + QSize sizeInternal() const { return KexiDBFormBase::size(); } + + //! @internal for sizeInternal property + void resizeInternal(const QSize& s) { KexiDBFormBase::resize(s); } + + signals: + void handleDragMoveEvent(QDragMoveEvent *e); + void handleDropEvent(QDropEvent *e); + + protected: + //! no effect + virtual void setValueInternal(const QVariant&, bool) {} + + //! Used to emit handleDragMoveEvent() signal needed to control dragging over the container's surface + virtual void dragMoveEvent( QDragMoveEvent *e ); + + //! Used to emit handleDropEvent() signal needed to control dropping on the container's surface + virtual void dropEvent( QDropEvent *e ); + + //! called from KexiFormScrollView::initDataContents() + void updateReadOnlyFlags(); +// virtual void paintEvent( QPaintEvent * ); + + //! Points to a currently edited data item. + //! It is cleared when the focus is moved to other + KexiFormDataItemInterface *editedItem; + + class Private; + Private *d; + + friend class KexiFormScrollView; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbimagebox.cpp b/kexi/plugins/forms/widgets/kexidbimagebox.cpp new file mode 100644 index 00000000..82e70086 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbimagebox.cpp @@ -0,0 +1,870 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2007 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 "kexidbimagebox.h" + +#include <qapplication.h> +#include <qpixmap.h> +#include <qstyle.h> +#include <qclipboard.h> +#include <qtooltip.h> +#include <qimage.h> +#include <qbuffer.h> +#include <qfiledialog.h> +#include <qpainter.h> + +#include <kdebug.h> +#include <kpopupmenu.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kfiledialog.h> +#include <kimageio.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> +#include <kimageeffect.h> +#include <kstdaccel.h> +#include <kmessagebox.h> +#include <kguiitem.h> + +#include <widget/utils/kexidropdownbutton.h> +#include <widget/utils/kexicontextmenuutils.h> +#include <kexiutils/utils.h> +#include <kexidb/field.h> +#include <kexidb/utils.h> +#include <kexidb/queryschema.h> +#include <formeditor/widgetlibrary.h> + +#ifdef Q_WS_WIN +#include <win32_utils.h> +#include <krecentdirs.h> +#endif + +#include "kexidbutils.h" +#include "../kexiformpart.h" + +static KStaticDeleter<QPixmap> KexiDBImageBox_pmDeleter; +static QPixmap* KexiDBImageBox_pm = 0; +static KStaticDeleter<QPixmap> KexiDBImageBox_pmSmallDeleter; +static QPixmap* KexiDBImageBox_pmSmall = 0; + +KexiDBImageBox::KexiDBImageBox( bool designMode, QWidget *parent, const char *name ) + : KexiFrame( parent, name, Qt::WNoAutoErase ) + , KexiFormDataItemInterface() + , m_alignment(Qt::AlignAuto|Qt::AlignTop) + , m_designMode(designMode) + , m_readOnly(false) + , m_scaledContents(false) + , m_keepAspectRatio(true) + , m_insideSetData(false) + , m_setFocusOnButtonAfterClosingPopup(false) + , m_lineWidthChanged(false) + , m_paintEventEnabled(true) + , m_dropDownButtonVisible(true) + , m_insideSetPalette(false) +{ + installEventFilter(this); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + //setup popup menu + m_popupMenu = new KexiImageContextMenu(this); + m_popupMenu->installEventFilter(this); + + if (m_designMode) { + m_chooser = 0; + } + else { + m_chooser = new KexiDropDownButton(this); + m_chooser->setFocusPolicy(StrongFocus); + m_chooser->setPopup(m_popupMenu); + setFocusProxy(m_chooser); + m_chooser->installEventFilter(this); +// m_chooser->setPalette(qApp->palette()); +// hlyr->addWidget(m_chooser); + } + + setBackgroundMode(Qt::NoBackground); + setFrameShape(QFrame::Box); + setFrameShadow(QFrame::Plain); + setFrameColor(Qt::black); + + m_paletteBackgroundColorChanged = false; //set this here, not before + + connect(m_popupMenu, SIGNAL(updateActionsAvailabilityRequested(bool&, bool&)), + this, SLOT(slotUpdateActionsAvailabilityRequested(bool&, bool&))); + connect(m_popupMenu, SIGNAL(insertFromFileRequested(const KURL&)), + this, SLOT(handleInsertFromFileAction(const KURL&))); + connect(m_popupMenu, SIGNAL(saveAsRequested(const QString&)), + this, SLOT(handleSaveAsAction(const QString&))); + connect(m_popupMenu, SIGNAL(cutRequested()), + this, SLOT(handleCutAction())); + connect(m_popupMenu, SIGNAL(copyRequested()), + this, SLOT(handleCopyAction())); + connect(m_popupMenu, SIGNAL(pasteRequested()), + this, SLOT(handlePasteAction())); + connect(m_popupMenu, SIGNAL(clearRequested()), + this, SLOT(clear())); + connect(m_popupMenu, SIGNAL(showPropertiesRequested()), + this, SLOT(handleShowPropertiesAction())); + +// connect(m_popupMenu, SIGNAL(aboutToHide()), this, SLOT(slotAboutToHidePopupMenu())); +// if (m_chooser) { + //we couldn't use m_chooser->setPopup() because of drawing problems +// connect(m_chooser, SIGNAL(pressed()), this, SLOT(slotChooserPressed())); +// connect(m_chooser, SIGNAL(released()), this, SLOT(slotChooserReleased())); +// connect(m_chooser, SIGNAL(toggled(bool)), this, SLOT(slotToggled(bool))); +// } + + setDataSource( QString::null ); //to initialize popup menu and actions availability +} + +KexiDBImageBox::~KexiDBImageBox() +{ +} + +KexiImageContextMenu* KexiDBImageBox::contextMenu() const +{ + return m_popupMenu; +} + +QVariant KexiDBImageBox::value() +{ + if (dataSource().isEmpty()) { + //not db-aware + return QVariant(); + } + //db-aware mode + return m_value; //todo + //return QVariant(); //todo +} + +void KexiDBImageBox::setValueInternal( const QVariant& add, bool removeOld, bool loadPixmap ) +{ + if (isReadOnly()) + return; + m_popupMenu->hide(); + if (removeOld) + m_value = add.toByteArray(); + else //do not add "m_origValue" to "add" as this is QByteArray + m_value = m_origValue.toByteArray(); + bool ok = !m_value.isEmpty(); + if (ok) { + ///unused (m_valueMimeType is not available unless the px is inserted) QString type( KImageIO::typeForMime(m_valueMimeType) ); + ///ok = KImageIO::canRead( type ); + ok = loadPixmap ? m_pixmap.loadFromData(m_value) : true; //, type.latin1()); + if (!ok) { + //! @todo inform about error? + } + } + if (!ok) { + m_valueMimeType = QString::null; + m_pixmap = QPixmap(); + } + repaint(); +} + +void KexiDBImageBox::setInvalidState( const QString& displayText ) +{ + Q_UNUSED( displayText ); + +// m_pixmapLabel->setPixmap(QPixmap()); + if (!dataSource().isEmpty()) { + m_value = QByteArray(); + } +// m_pixmap = QPixmap(); +// m_originalFileName = QString::null; + +//! @todo m_pixmapLabel->setText( displayText ); + + if (m_chooser) + m_chooser->hide(); + setReadOnly(true); +} + +bool KexiDBImageBox::valueIsNull() +{ + return m_value.isEmpty(); +// return !m_pixmapLabel->pixmap() || m_pixmapLabel->pixmap()->isNull(); +} + +bool KexiDBImageBox::valueIsEmpty() +{ + return false; +} + +bool KexiDBImageBox::isReadOnly() const +{ + return m_readOnly; +} + +void KexiDBImageBox::setReadOnly(bool set) +{ + m_readOnly = set; +} + +QPixmap KexiDBImageBox::pixmap() const +{ + if (dataSource().isEmpty()) { + //not db-aware + return m_data.pixmap(); + } + //db-aware mode + return m_pixmap; +} + +uint KexiDBImageBox::pixmapId() const +{ + if (dataSource().isEmpty()) {// && !m_data.stored()) { + //not db-aware + return m_data.id(); + } + return 0; +} + +void KexiDBImageBox::setPixmapId(uint id) +{ + if (m_insideSetData) //avoid recursion + return; + setData(KexiBLOBBuffer::self()->objectForId( id, /*unstored*/false )); + repaint(); +} + +uint KexiDBImageBox::storedPixmapId() const +{ + if (dataSource().isEmpty() && m_data.stored()) { + //not db-aware + return m_data.id(); + } + return 0; +} + +void KexiDBImageBox::setStoredPixmapId(uint id) +{ + setData(KexiBLOBBuffer::self()->objectForId( id, /*stored*/true )); + repaint(); +} + +bool KexiDBImageBox::hasScaledContents() const +{ + return m_scaledContents; +// return m_pixmapLabel->hasScaledContents(); +} + +/*void KexiDBImageBox::setPixmap(const QByteArray& pixmap) +{ + setValueInternal(pixmap, true); +// setBackgroundMode(pixmap.isNull() ? Qt::NoBackground : Qt::PaletteBackground); +}*/ + +void KexiDBImageBox::setScaledContents(bool set) +{ +//todo m_pixmapLabel->setScaledContents(set); + m_scaledContents = set; + repaint(); +} + +void KexiDBImageBox::setKeepAspectRatio(bool set) +{ + m_keepAspectRatio = set; + if (m_scaledContents) + repaint(); +} + +QWidget* KexiDBImageBox::widget() +{ + //! @todo +// return m_pixmapLabel; + return this; +} + +bool KexiDBImageBox::cursorAtStart() +{ + return true; +} + +bool KexiDBImageBox::cursorAtEnd() +{ + return true; +} + +QByteArray KexiDBImageBox::data() const +{ + if (dataSource().isEmpty()) { + //static mode + return m_data.data(); + } + else { + //db-aware mode + return m_value; + } +} + +void KexiDBImageBox::insertFromFile() +{ + m_popupMenu->insertFromFile(); +} + +void KexiDBImageBox::handleInsertFromFileAction(const KURL& url) +{ + if (!dataSource().isEmpty() && isReadOnly()) + return; + + if (dataSource().isEmpty()) { + //static mode + KexiBLOBBuffer::Handle h = KexiBLOBBuffer::self()->insertPixmap( url ); + if (!h) + return; + setData(h); + repaint(); + } + else { + //db-aware + QString fileName( url.isLocalFile() ? url.path() : url.prettyURL() ); + + //! @todo download the file if remote, then set fileName properly + QFile f(fileName); + if (!f.open(IO_ReadOnly)) { + //! @todo err msg + return; + } + QByteArray ba = f.readAll(); + if (f.status()!=IO_Ok) { + //! @todo err msg + f.close(); + return; + } + m_valueMimeType = KImageIO::mimeType( fileName ); + setValueInternal( ba, true ); + } + +//! @todo emit signal for setting "dirty" flag within the design + if (!dataSource().isEmpty()) { + signalValueChanged(); + } +} + +void KexiDBImageBox::handleAboutToSaveAsAction(QString& origFilename, QString& fileExtension, bool& dataIsEmpty) +{ + if (data().isEmpty()) { + kdWarning() << "KexiDBImageBox::handleAboutToSaveAs(): no pixmap!" << endl; + dataIsEmpty = false; + return; + } + if (dataSource().isEmpty()) { //for static images filename and mimetype can be available + origFilename = m_data.originalFileName(); + if (!origFilename.isEmpty()) + origFilename = QString("/") + origFilename; + if (!m_data.mimeType().isEmpty()) + fileExtension = KImageIO::typeForMime(m_data.mimeType()).lower(); + } +} + +void KexiDBImageBox::handleSaveAsAction(const QString& fileName) +{ + QFile f(fileName); + if (!f.open(IO_WriteOnly)) { + //! @todo err msg + return; + } + f.writeBlock( data() ); + if (f.status()!=IO_Ok) { + //! @todo err msg + f.close(); + return; + } + f.close(); +} + +void KexiDBImageBox::handleCutAction() +{ + if (!dataSource().isEmpty() && isReadOnly()) + return; + handleCopyAction(); + clear(); +} + +void KexiDBImageBox::handleCopyAction() +{ + qApp->clipboard()->setPixmap(pixmap(), QClipboard::Clipboard); +} + +void KexiDBImageBox::handlePasteAction() +{ + if (isReadOnly() || (!m_designMode && !hasFocus())) + return; + QPixmap pm( qApp->clipboard()->pixmap(QClipboard::Clipboard) ); +// if (!pm.isNull()) +// setValueInternal(pm, true); + if (dataSource().isEmpty()) { + //static mode + setData(KexiBLOBBuffer::self()->insertPixmap( pm )); + } + else { + //db-aware mode + m_pixmap = pm; + QByteArray ba; + QBuffer buffer( ba ); + buffer.open( IO_WriteOnly ); + if (m_pixmap.save( &buffer, "PNG" )) {// write pixmap into ba in PNG format + setValueInternal( ba, true, false/* !loadPixmap */ ); + } + else { + setValueInternal( QByteArray(), true ); + } + } + + repaint(); + if (!dataSource().isEmpty()) { +// emit pixmapChanged(); + signalValueChanged(); + } +} + +void KexiDBImageBox::clear() +{ + if (dataSource().isEmpty()) { + //static mode + setData(KexiBLOBBuffer::Handle()); + } + else { + if (isReadOnly()) + return; + //db-aware mode + setValueInternal(QByteArray(), true); + //m_pixmap = QPixmap(); + } + +// m_originalFileName = QString::null; + + //! @todo emit signal for setting "dirty" flag within the design + +// m_pixmap = QPixmap(); //will be loaded on demand + repaint(); + if (!dataSource().isEmpty()) { +// emit pixmapChanged();//valueChanged(data()); + signalValueChanged(); + } +} + +void KexiDBImageBox::handleShowPropertiesAction() +{ + //! @todo +} + +void KexiDBImageBox::slotUpdateActionsAvailabilityRequested(bool& valueIsNull, bool& valueIsReadOnly) +{ + valueIsNull = !( + (dataSource().isEmpty() && !pixmap().isNull()) /*static pixmap available*/ + || (!dataSource().isEmpty() && !this->valueIsNull()) /*db-aware pixmap available*/ + ); + // read-only if static pixmap or db-aware pixmap for read-only widget: + valueIsReadOnly = !m_designMode && dataSource().isEmpty() || !dataSource().isEmpty() && isReadOnly() + || m_designMode && !dataSource().isEmpty(); +} + +/* +void KexiDBImageBox::slotAboutToHidePopupMenu() +{ +// kexipluginsdbg << "##### slotAboutToHidePopupMenu() " << endl; + m_clickTimer.start(50, true); + if (m_chooser && m_chooser->isOn()) { + m_chooser->toggle(); + if (m_setFocusOnButtonAfterClosingPopup) { + m_setFocusOnButtonAfterClosingPopup = false; + m_chooser->setFocus(); + } + } +}*/ + +void KexiDBImageBox::contextMenuEvent( QContextMenuEvent * e ) +{ + if (popupMenuAvailable()) + m_popupMenu->exec( e->globalPos(), -1 ); +} + +/*void KexiDBImageBox::slotChooserPressed() +{ +// if (!m_clickTimer.isActive()) +// return; +// m_chooser->setDown( false ); +} + +void KexiDBImageBox::slotChooserReleased() +{ +} + +void KexiDBImageBox::slotToggled(bool on) +{ + return; + +// kexipluginsdbg << "##### slotToggled() " << on << endl; + if (m_clickTimer.isActive() || !on) { + m_chooser->disableMousePress = true; + return; + } + m_chooser->disableMousePress = false; + QRect screen = qApp->desktop()->availableGeometry( m_chooser ); + QPoint p; + if ( QApplication::reverseLayout() ) { + if ( (mapToGlobal( m_chooser->rect().bottomLeft() ).y() + m_popupMenu->sizeHint().height()) <= screen.height() ) + p = m_chooser->mapToGlobal( m_chooser->rect().bottomRight() ); + else + p = m_chooser->mapToGlobal( m_chooser->rect().topRight() - QPoint( 0, m_popupMenu->sizeHint().height() ) ); + p.rx() -= m_popupMenu->sizeHint().width(); + } + else { + if ( (m_chooser->mapToGlobal( m_chooser->rect().bottomLeft() ).y() + m_popupMenu->sizeHint().height()) <= screen.height() ) + p = m_chooser->mapToGlobal( m_chooser->rect().bottomLeft() ); + else + p = m_chooser->mapToGlobal( m_chooser->rect().topLeft() - QPoint( 0, m_popupMenu->sizeHint().height() ) ); + } + if (!m_popupMenu->isVisible() && on) { + m_popupMenu->exec( p, -1 ); + m_popupMenu->setFocus(); + } + //m_chooser->setDown( false ); +}*/ + +void KexiDBImageBox::updateActionStrings() +{ + if (!m_popupMenu) + return; + if (m_designMode) { +/* QString titleString( i18n("Image Box") ); + if (!dataSource().isEmpty()) + titleString.prepend(dataSource() + " : "); + m_popupMenu->changeTitle(m_popupMenu->idAt(0), m_popupMenu->titlePixmap(m_popupMenu->idAt(0)), titleString);*/ + } + else { + //update title in data view mode, based on the data source + if (columnInfo()) { + KexiImageContextMenu::updateTitle( m_popupMenu, columnInfo()->captionOrAliasOrName(), + KexiFormPart::library()->iconName(className()) ); + } + } + + if (m_chooser) { + if (popupMenuAvailable() && dataSource().isEmpty()) { //this may work in the future (see @todo below) + QToolTip::add(m_chooser, i18n("Click to show actions for this image box")); + } else { + QString beautifiedImageBoxName; + if (m_designMode) { + beautifiedImageBoxName = dataSource(); + } + else { + beautifiedImageBoxName = columnInfo() ? columnInfo()->captionOrAliasOrName() : QString::null; + /*! @todo look at makeFirstCharacterUpperCaseInCaptions setting [bool] + (see doc/dev/settings.txt) */ + beautifiedImageBoxName = beautifiedImageBoxName[0].upper() + beautifiedImageBoxName.mid(1); + } + QToolTip::add(m_chooser, i18n("Click to show actions for \"%1\" image box").arg(beautifiedImageBoxName)); + } + } +} + +bool KexiDBImageBox::popupMenuAvailable() +{ +/*! @todo add kexi-global setting which anyway, allows to show this button + (read-only actions like copy/save as/print can be available) */ + //chooser button can be only visible when data source is specified + return !dataSource().isEmpty(); +} + +void KexiDBImageBox::setDataSource( const QString &ds ) +{ + KexiFormDataItemInterface::setDataSource( ds ); + setData(KexiBLOBBuffer::Handle()); + updateActionStrings(); + KexiFrame::setFocusPolicy( focusPolicy() ); //set modified policy + + if (m_chooser) { + m_chooser->setEnabled(popupMenuAvailable()); + if (m_dropDownButtonVisible && popupMenuAvailable()) { + m_chooser->show(); + } + else { + m_chooser->hide(); + } + } + + // update some properties s not changed by user +//! @todo get default line width from global style settings + if (!m_lineWidthChanged) { + KexiFrame::setLineWidth( ds.isEmpty() ? 0 : 1 ); + } + if (!m_paletteBackgroundColorChanged && parentWidget()) { + KexiFrame::setPaletteBackgroundColor( + dataSource().isEmpty() ? parentWidget()->paletteBackgroundColor() : palette().active().base() ); + } +} + +QSize KexiDBImageBox::sizeHint() const +{ + if (pixmap().isNull()) + return QSize(80, 80); + return pixmap().size(); +} + +int KexiDBImageBox::realLineWidth() const +{ + if (frameShape()==QFrame::Box && (frameShadow()==QFrame::Sunken || frameShadow()==QFrame::Raised)) + return 2 * lineWidth(); + else + return lineWidth(); +} + +void KexiDBImageBox::paintEvent( QPaintEvent *pe ) +{ + if (!m_paintEventEnabled) + return; + QPainter p(this); + p.setClipRect(pe->rect()); + const int m = realLineWidth() + margin(); + QColor bg(eraseColor()); + if (m_designMode && pixmap().isNull()) { + QPixmap pm(size()-QSize(m, m)); + QPainter p2; + p2.begin(&pm, this); + p2.fillRect(0,0,width(),height(), bg); + + updatePixmap(); + QPixmap *imagBoxPm; + const bool tooLarge = (height()-m-m) <= KexiDBImageBox_pm->height(); + if (tooLarge || (width()-m-m) <= KexiDBImageBox_pm->width()) + imagBoxPm = KexiDBImageBox_pmSmall; + else + imagBoxPm = KexiDBImageBox_pm; + QImage img(imagBoxPm->convertToImage()); + img = KImageEffect::flatten(img, bg.dark(150), + qGray( bg.rgb() ) <= 20 ? QColor(Qt::gray).dark(150) : bg.light(105)); + + QPixmap converted; + converted.convertFromImage(img); +// if (tooLarge) +// p2.drawPixmap(2, 2, converted); +// else + p2.drawPixmap(2, height()-m-m-imagBoxPm->height()-2, converted); + QFont f(qApp->font()); + p2.setFont(f); + p2.setPen( KexiUtils::contrastColor( bg ) ); + p2.drawText(pm.rect(), Qt::AlignCenter, + dataSource().isEmpty() + ? QString::fromLatin1(name())+"\n"+i18n("Unbound Image Box", "(unbound)") //i18n("No Image") + : dataSource()); + p2.end(); + bitBlt(this, m, m, &pm); + } + else { + QSize internalSize(size()); + if (m_chooser && m_dropDownButtonVisible && !dataSource().isEmpty()) + internalSize.setWidth( internalSize.width() - m_chooser->width() ); + + //clearing needed here because we may need to draw a pixmap with transparency + p.fillRect(0,0,width(),height(), bg); + + KexiUtils::drawPixmap( p, m, QRect(QPoint(0,0), internalSize), pixmap(), m_alignment, + m_scaledContents, m_keepAspectRatio ); + } + KexiFrame::drawFrame( &p ); + + // if the widget is focused, draw focus indicator rect _if_ there is no chooser button + if (!m_designMode && !dataSource().isEmpty() && hasFocus() && (!m_chooser || !m_chooser->isVisible())) { + style().drawPrimitive( + QStyle::PE_FocusRect, &p, style().subRect(QStyle::SR_PushButtonContents, this), + palette().active() ); + } +} + +/* virtual void KexiDBImageBox::paletteChange ( const QPalette & oldPalette ) +{ + QFrame::paletteChange(oldPalette); + if (oldPalette.active().background()!=palette().active().background()) { + delete KexiDBImageBox_pm; + KexiDBImageBox_pm = 0; + repaint(); + } +}*/ + +void KexiDBImageBox::updatePixmap() +{ + if (! (m_designMode && pixmap().isNull()) ) + return; + + if (!KexiDBImageBox_pm) { + QString fname( locate("data", QString("kexi/pics/imagebox.png")) ); + KexiDBImageBox_pmDeleter.setObject( KexiDBImageBox_pm, new QPixmap(fname, "PNG") ); + QImage img(KexiDBImageBox_pm->convertToImage()); + KexiDBImageBox_pmSmallDeleter.setObject( KexiDBImageBox_pmSmall, + new QPixmap( img.smoothScale(img.width()/2, img.height()/2, QImage::ScaleMin) ) ); + } +} + +void KexiDBImageBox::setAlignment(int alignment) +{ + m_alignment = alignment; + if (!m_scaledContents || m_keepAspectRatio) + repaint(); +} + +void KexiDBImageBox::setData(const KexiBLOBBuffer::Handle& handle) +{ + if (m_insideSetData) //avoid recursion + return; + m_insideSetData = true; + m_data = handle; + emit idChanged(handle.id()); + m_insideSetData = false; + update(); +} + +void KexiDBImageBox::resizeEvent( QResizeEvent * e ) +{ + KexiFrame::resizeEvent(e); + if (m_chooser) { + QSize s( m_chooser->sizeHint() ); + QSize margin( realLineWidth(), realLineWidth() ); + s.setHeight( height() - 2*margin.height() ); + s = s.boundedTo( size()-2*margin ); + m_chooser->resize( s ); + m_chooser->move( QRect(QPoint(0,0), e->size() - m_chooser->size() - margin + QSize(1,1)).bottomRight() ); + } +} + +/* +bool KexiDBImageBox::setProperty( const char * name, const QVariant & value ) +{ + const bool ret = QLabel::setProperty(name, value); + if (p_shadowEnabled) { + if (0==qstrcmp("indent", name) || 0==qstrcmp("font", name) || 0==qstrcmp("margin", name) + || 0==qstrcmp("frameShadow", name) || 0==qstrcmp("frameShape", name) + || 0==qstrcmp("frameStyle", name) || 0==qstrcmp("midLineWidth", name) + || 0==qstrcmp("lineWidth", name)) { + p_privateLabel->setProperty(name, value); + updatePixmap(); + } + } + return ret; +} +*/ + +void KexiDBImageBox::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) +{ + KexiFormDataItemInterface::setColumnInfo(cinfo); + //updating strings and title is needed + updateActionStrings(); +} + +bool KexiDBImageBox::keyPressed(QKeyEvent *ke) +{ + // Esc key should close the popup + if (ke->state() == Qt::NoButton && ke->key() == Qt::Key_Escape) { + if (m_popupMenu->isVisible()) { + m_setFocusOnButtonAfterClosingPopup = true; + return true; + } + } +// else if (ke->state() == Qt::ControlButton && KStdAccel::shortcut(KStdAccel::Copy).keyCodeQt() == (ke->key()|Qt::CTRL)) { +// } + return false; +} + +void KexiDBImageBox::setLineWidth( int width ) +{ + m_lineWidthChanged = true; + KexiFrame::setLineWidth(width); +} + +void KexiDBImageBox::setPalette( const QPalette &pal ) +{ + KexiFrame::setPalette(pal); + if (m_insideSetPalette) + return; + m_insideSetPalette = true; + setPaletteBackgroundColor(pal.active().base()); + setPaletteForegroundColor(pal.active().foreground()); + m_insideSetPalette = false; +} + +void KexiDBImageBox::setPaletteBackgroundColor( const QColor & color ) +{ + kexipluginsdbg << "KexiDBImageBox::setPaletteBackgroundColor(): " << color.name() << endl; + m_paletteBackgroundColorChanged = true; + KexiFrame::setPaletteBackgroundColor(color); + if (m_chooser) + m_chooser->setPalette( qApp->palette() ); +} + +bool KexiDBImageBox::dropDownButtonVisible() const +{ + return m_dropDownButtonVisible; +} + +void KexiDBImageBox::setDropDownButtonVisible( bool set ) +{ +//! @todo use global default setting for this property + if (m_dropDownButtonVisible == set) + return; + m_dropDownButtonVisible = set; + if (m_chooser) { + if (m_dropDownButtonVisible) + m_chooser->show(); + else + m_chooser->hide(); + } +} + +bool KexiDBImageBox::subwidgetStretchRequired(KexiDBAutoField* autoField) const +{ + Q_UNUSED(autoField); + return true; +} + +bool KexiDBImageBox::eventFilter( QObject * watched, QEvent * e ) +{ + if (watched==this || watched==m_chooser) { //we're watching chooser as well because it's a focus proxy even if invisible + if (e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut || e->type()==QEvent::MouseButtonPress) { + update(); //to repaint focus rect + } + } + // hide popup menu as soon as it loses focus + if (watched==m_popupMenu && e->type()==QEvent::FocusOut) { + m_popupMenu->hide(); + } + return KexiFrame::eventFilter(watched, e); +} + +QWidget::FocusPolicy KexiDBImageBox::focusPolicy() const +{ + if (dataSource().isEmpty()) + return NoFocus; + return m_focusPolicyInternal; +} + +QWidget::FocusPolicy KexiDBImageBox::focusPolicyInternal() const +{ + return m_focusPolicyInternal; +} + +void KexiDBImageBox::setFocusPolicy( FocusPolicy policy ) +{ + m_focusPolicyInternal = policy; + KexiFrame::setFocusPolicy( focusPolicy() ); //set modified policy +} + +#include "kexidbimagebox.moc" diff --git a/kexi/plugins/forms/widgets/kexidbimagebox.h b/kexi/plugins/forms/widgets/kexidbimagebox.h new file mode 100644 index 00000000..3ad2f710 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbimagebox.h @@ -0,0 +1,275 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2007 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. +*/ + +#ifndef KexiDBImageBox_H +#define KexiDBImageBox_H + +#include "kexiformdataiteminterface.h" +#include "kexiframe.h" +#include "kexidbutils.h" +#include <kexiblobbuffer.h> + +class KexiDropDownButton; +class KexiImageContextMenu; + +//! @short A data-aware, editable image box. +/*! Can also act as a normal static image box. +*/ +class KEXIFORMUTILS_EXPORT KexiDBImageBox : + public KexiFrame, + public KexiFormDataItemInterface, + public KexiSubwidgetInterface +{ + Q_OBJECT + Q_PROPERTY( QString dataSource READ dataSource WRITE setDataSource ) + Q_PROPERTY( QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType ) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) +// Q_PROPERTY( QPixmap pixmap READ pixmap WRITE setPixmap ) +// Q_PROPERTY( QByteArray pixmapData READ pixmapData WRITE setPixmapData ) + Q_PROPERTY( uint pixmapId READ pixmapId WRITE setPixmapId DESIGNABLE true STORED false ) + Q_PROPERTY( uint storedPixmapId READ storedPixmapId WRITE setStoredPixmapId DESIGNABLE false STORED true ) + Q_PROPERTY( bool scaledContents READ hasScaledContents WRITE setScaledContents ) + Q_PROPERTY( bool keepAspectRatio READ keepAspectRatio WRITE setKeepAspectRatio ) + Q_PROPERTY( Alignment alignment READ alignment WRITE setAlignment ) +// Q_PROPERTY( QString originalFileName READ originalFileName WRITE setOriginalFileName DESIGNABLE false ) +// Q_OVERRIDE( FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy ) + Q_PROPERTY( bool dropDownButtonVisible READ dropDownButtonVisible WRITE setDropDownButtonVisible ) + Q_OVERRIDE( int lineWidth READ lineWidth WRITE setLineWidth ) + Q_OVERRIDE( FocusPolicy focusPolicy READ focusPolicyInternal WRITE setFocusPolicy ) + + public: + KexiDBImageBox( bool designMode, QWidget *parent, const char *name = 0 ); + virtual ~KexiDBImageBox(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + + virtual QVariant value(); // { return m_value.data(); } + +// QByteArray pixmapData() const { return m_value.data(); } + + QPixmap pixmap() const; + + uint pixmapId() const; + + uint storedPixmapId() const; +// + virtual void setInvalidState( const QString& displayText ); + + virtual bool valueIsNull(); + + virtual bool valueIsEmpty(); + + virtual QWidget* widget(); + + //! always true + virtual bool cursorAtStart(); + + //! always true + virtual bool cursorAtEnd(); + +// //! used to catch setIndent(), etc. +// virtual bool setProperty ( const char * name, const QVariant & value ); + + virtual bool isReadOnly() const; + + bool hasScaledContents() const; + +// bool designMode() const { return m_designMode; } + + int alignment() const { return m_alignment; } + + bool keepAspectRatio() const { return m_keepAspectRatio; } + + virtual QSize sizeHint() const; + + KexiImageContextMenu *contextMenu() const; + + /*! \return original file name of image loaded from a file. + This can be later reused for displaying the image within a collection (to be implemented) + or on saving the image data back to file. */ +//todo QString originalFileName() const { return m_value.originalFileName(); } + + //! Reimplemented to override behaviour of "lineWidth" property. + virtual void setLineWidth( int width ); + + //! Reimplemented to override behaviour of "paletteBackgroundColor" + //! and "paletteForegroundColor" properties. + virtual void setPalette( const QPalette &pal ); + + //! Reimplemented to override behaviour of "paletteBackgroundColor" property. + virtual void setPaletteBackgroundColor( const QColor & color ); + + //! \return true id drop down button should be visible (the default). + bool dropDownButtonVisible() const; + + //! For overridden property + int lineWidth() const { return KexiFrame::lineWidth(); } + + /*! Overriden to change the policy behaviour a bit: + NoFocus is returned regardless the real focus flag + if the data source is empty (see dataSource()). */ + FocusPolicy focusPolicy() const; + + //! \return the internal focus policy value, i.e. the one unrelated to data source presence. + FocusPolicy focusPolicyInternal() const; + + /*! Sets the internal focus policy value. + "Internal" means that if there is no data source set, real policy becomes NoFocus. */ + virtual void setFocusPolicy( FocusPolicy policy ); + + public slots: + void setPixmapId(uint id); + + void setStoredPixmapId(uint id); + + //! Sets the datasource to \a ds + virtual void setDataSource( const QString &ds ); + + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + + virtual void setReadOnly(bool set); + + //! Sets \a pixmapData data for this widget. If the widget has data source set, + //! the pixmap will be also placed inside of the buffer and saved later. +//todo void setPixmapData(const QByteArray& pixmapData) { m_value.setData(pixmapData); } + + /*! Sets original file name of image loaded from a file. + @see originalFileName() */ +//todo void setOriginalFileName(const QString& name) { m_value.setOriginalFileName(name); } + + void setScaledContents(bool set); + + void setAlignment(int alignment); + + void setKeepAspectRatio(bool set); + +// void updateActionsAvailability(); + + //! @internal +// void slotToggled( bool on ); + + //! \return sets dropDownButtonVisible property. @see dropDownButtonVisible() + void setDropDownButtonVisible( bool set ); + + //! Forces execution of "insert from file" action + void insertFromFile(); + + signals: + //! Used for db-aware mode. Emitted when value has been changed. + //! Actual value can be obtained using value(). +// virtual void pixmapChanged(); +// virtual void valueChanged(const QByteArray& data); + + void idChanged(long id); + + protected slots: + void slotUpdateActionsAvailabilityRequested(bool& valueIsNull, bool& valueIsReadOnly); + + void handleInsertFromFileAction(const KURL& url); + void handleAboutToSaveAsAction(QString& origFilename, QString& fileExtension, bool& dataIsEmpty); + void handleSaveAsAction(const QString& fileName); + void handleCutAction(); + void handleCopyAction(); + void handlePasteAction(); + virtual void clear(); + void handleShowPropertiesAction(); + + protected: + //! \return data depending on the current mode (db-aware or static) + QByteArray data() const; + + virtual void contextMenuEvent ( QContextMenuEvent * e ); +// virtual void mousePressEvent( QMouseEvent *e ); + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo); + virtual void paintEvent( QPaintEvent* ); + virtual void resizeEvent( QResizeEvent* e ); + virtual bool eventFilter( QObject * watched, QEvent * e ); + + //! Sets value \a value for a widget. + virtual void setValueInternal( const QVariant& add, bool removeOld ) { + setValueInternal( add, removeOld, true /*loadPixmap*/ ); + } + + //! @internal, added \a loadPixmap option used by paste(). + void setValueInternal( const QVariant& add, bool removeOld, bool loadPixmap ); + + //! Updates i18n'd action strings after datasource change + void updateActionStrings(); + void updatePixmap(); + + //! @internal + void setData(const KexiBLOBBuffer::Handle& handle); + + bool popupMenuAvailable(); + + /*! Called by top-level form on key press event. + Used for Key_Escape to if the popup is visible, + so the key press won't be consumed to perform "cancel editing". */ + virtual bool keyPressed(QKeyEvent *ke); + + //! \return real line width, i.e. for Boxed sunken or Boxed raised + //! frames returns doubled width value. + int realLineWidth() const; + + //! Implemented for KexiSubwidgetInterface + virtual bool subwidgetStretchRequired(KexiDBAutoField* autoField) const; + +// virtual void drawContents ( QPainter *p ); + +// virtual void fontChange( const QFont& font ); +// virtual void styleChange( QStyle& style ); +// virtual void enabledChange( bool enabled ); + +// virtual void paletteChange( const QPalette& pal ); +// virtual void frameChanged(); +// virtual void showEvent( QShowEvent* e ); + +// void updatePixmapLater(); +// class ImageLabel; +// ImageLabel *m_pixmapLabel; + QPixmap m_pixmap; + QByteArray m_value; //!< for db-aware mode + QString m_valueMimeType; //!< for db-aware mode +// PixmapData m_value; + KexiBLOBBuffer::Handle m_data; +// QString m_originalFileName; + KexiDropDownButton *m_chooser; + KexiImageContextMenu *m_popupMenu; +//moved KActionCollection m_actionCollection; +//moved KAction *m_insertFromFileAction, *m_saveAsAction, *m_cutAction, *m_copyAction, *m_pasteAction, +// *m_deleteAction, *m_propertiesAction; +// QTimer m_clickTimer; + int m_alignment; + FocusPolicy m_focusPolicyInternal; //!< Used for focusPolicyInternal() + bool m_designMode : 1; + bool m_readOnly : 1; + bool m_scaledContents : 1; + bool m_keepAspectRatio : 1; + bool m_insideSetData : 1; + bool m_setFocusOnButtonAfterClosingPopup : 1; + bool m_lineWidthChanged : 1; + bool m_paletteBackgroundColorChanged : 1; + bool m_paintEventEnabled : 1; //!< used to disable paintEvent() + bool m_dropDownButtonVisible : 1; + bool m_insideSetPalette : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbintspinbox.cpp b/kexi/plugins/forms/widgets/kexidbintspinbox.cpp new file mode 100644 index 00000000..ac923347 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbintspinbox.cpp @@ -0,0 +1,114 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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 "kexidbintspinbox.h" + +#include <qlineedit.h> +#include <knumvalidator.h> + +KexiDBIntSpinBox::KexiDBIntSpinBox(QWidget *parent, const char *name) + : KIntSpinBox(parent, name) , KexiFormDataItemInterface() +{ + connect(this, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged())); +} + +KexiDBIntSpinBox::~KexiDBIntSpinBox() +{ +} + +void KexiDBIntSpinBox::setInvalidState( const QString& displayText ) +{ + m_invalidState = true; + setEnabled(false); + setReadOnly(true); +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); + setSpecialValueText(displayText); + KIntSpinBox::setValue(minValue()); +} + +void +KexiDBIntSpinBox::setEnabled(bool enabled) +{ + // prevent the user from reenabling the widget when it is in invalid state + if(enabled && m_invalidState) + return; + KIntSpinBox::setEnabled(enabled); +} + +void KexiDBIntSpinBox::setValueInternal(const QVariant&, bool) +{ + KIntSpinBox::setValue(m_origValue.toInt()); +} + +QVariant +KexiDBIntSpinBox::value() +{ + return KIntSpinBox::value(); +} + +void KexiDBIntSpinBox::slotValueChanged() +{ + signalValueChanged(); +} + +bool KexiDBIntSpinBox::valueIsNull() +{ + return cleanText().isEmpty(); +} + +bool KexiDBIntSpinBox::valueIsEmpty() +{ + return false; +} + +bool KexiDBIntSpinBox::isReadOnly() const +{ + return editor()->isReadOnly(); +} + +void KexiDBIntSpinBox::setReadOnly(bool set) +{ + editor()->setReadOnly(set); +} + +QWidget* +KexiDBIntSpinBox::widget() +{ + return this; +} + +bool KexiDBIntSpinBox::cursorAtStart() +{ + return false; //! \todo ? +} + +bool KexiDBIntSpinBox::cursorAtEnd() +{ + return false; //! \todo ? +} + +void KexiDBIntSpinBox::clear() +{ + KIntSpinBox::setValue(minValue()); //! \todo ? +} + +#include "kexidbintspinbox.moc" diff --git a/kexi/plugins/forms/widgets/kexidbintspinbox.h b/kexi/plugins/forms/widgets/kexidbintspinbox.h new file mode 100644 index 00000000..cddc614e --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbintspinbox.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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. +*/ + +#ifndef KexiDBIntSpinBox_H +#define KexiDBIntSpinBox_H + +#include "kexiformdataiteminterface.h" +#include <qwidget.h> +#include <knuminput.h> + +//! @short A db-aware int spin box +class KEXIFORMUTILS_EXPORT KexiDBIntSpinBox : public KIntSpinBox, public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true ) + + public: + KexiDBIntSpinBox(QWidget *parent, const char *name=0); + virtual ~KexiDBIntSpinBox(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + virtual void setEnabled(bool enabled); + + public slots: + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + void slotValueChanged(); + virtual void setReadOnly(bool set); + + protected: + virtual void setValueInternal(const QVariant& add, bool removeOld); + + private: + bool m_invalidState : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidblabel.cpp b/kexi/plugins/forms/widgets/kexidblabel.cpp new file mode 100644 index 00000000..e30cc19e --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidblabel.cpp @@ -0,0 +1,650 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de> + Copyright (C) 2005 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 "kexidblabel.h" + +#include <qbitmap.h> +#include <qpainter.h> +#include <qdrawutil.h> +#include <qapplication.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <kimageeffect.h> + +#include <kexidb/field.h> +#include <kexiutils/utils.h> + +#define SHADOW_OFFSET_X 3 +#define SHADOW_OFFSET_Y 3 +#define SHADOW_FACTOR 16.0 +#define SHADOW_OPACITY 50.0 +#define SHADOW_AXIS_FACTOR 2.0 +#define SHADOW_DIAGONAL_FACTOR 1.0 +#define SHADOW_THICKNESS 1 + +//! @internal +class KexiDBInternalLabel : public QLabel { + friend class KexiDBLabel; + public: + KexiDBInternalLabel( KexiDBLabel* ); + virtual ~KexiDBInternalLabel(); + + protected: + void updateFrame(); + + QImage makeShadow( const QImage& textImage, const QColor &bgColor, const QRect& boundingRect ); + QRect getBounding( const QImage &image, const QRect& startRect ); +// double defaultDecay( QImage& source, int i, int j ); + KPixmap getShadowPixmap(); + + QRect m_shadowRect; + KexiDBLabel *m_parentLabel; +}; + +KexiDBInternalLabel::KexiDBInternalLabel( KexiDBLabel* parent ) + : QLabel( parent ) + , m_parentLabel(parent) +{ + int a = alignment() | Qt::WordBreak; + a &= (0xffffff ^ Qt::AlignVertical_Mask); + a |= Qt::AlignTop; + setAlignment( a ); + updateFrame(); +} + +void KexiDBInternalLabel::updateFrame() +{ + setIndent(m_parentLabel->indent()); + setMargin(m_parentLabel->margin()); + setFont(m_parentLabel->font()); + + setFrameShadow(m_parentLabel->frameShadow()); + setFrameShape(m_parentLabel->frameShape()); + setFrameStyle(m_parentLabel->frameStyle()); + setMidLineWidth(m_parentLabel->midLineWidth()); + setLineWidth(m_parentLabel->lineWidth()); +} + +KexiDBInternalLabel::~KexiDBInternalLabel() +{ +} + +/*! +* This method is copied from kdebase/kdesktop/kshadowengine.cpp +* Some modifactions were made. +* -- +* Christian Nitschkowski +*/ +QImage KexiDBInternalLabel::makeShadow( const QImage& textImage, + const QColor &bgColor, const QRect& boundingRect ) +{ + QImage result; + QString origText( text() ); + + // create a new image for for the shaddow + const int w = textImage.width(); + const int h = textImage.height(); + + // avoid calling these methods for every pixel + const int bgRed = bgColor.red(); + const int bgGreen = bgColor.green(); + const int bgBlue = bgColor.blue(); + + const int startX = boundingRect.x() + SHADOW_THICKNESS; + const int startY = boundingRect.y() + SHADOW_THICKNESS; + const int effectWidth = boundingRect.bottomRight().x() - SHADOW_THICKNESS; + const int effectHeight = boundingRect.bottomRight().y() - SHADOW_THICKNESS; +// const int period = (effectWidth - startX) / 10; + + double alphaShadow; + + /* + * This is the source pixmap + */ + QImage img = textImage.convertDepth( 32 ); + + /* + * Resize the image if necessary + */ + if ( ( result.width() != w ) || ( result.height() != h ) ) { + result.create( w, h, 32 ); + } + +// result.fill( 0 ); // all black + double realOpacity = SHADOW_OPACITY + QMIN(50.0/double(256.0-qGray(bgColor.rgb())), 50.0); + //int _h, _s, _v; + //.getHsv( &_h, &_s, &_v ); + if (colorGroup().background()==Qt::red)//_s>=250 && _v>=250) //for colors like cyan or red, make the result more white + realOpacity += 50.0; + result.fill( (int)realOpacity ); + result.setAlphaBuffer( true ); + + for ( int i = startX; i < effectWidth; i++ ) { + for ( int j = startY; j < effectHeight; j++ ) { + /*! + * This method is copied from kdebase/kdesktop/kshadowengine.cpp + * Some modifactions were made. + * -- + * Christian Nitschkowski + */ + if ( ( i < 1 ) || ( j < 1 ) || ( i > img.width() - 2 ) || ( j > img.height() - 2 ) ) + continue; + else + alphaShadow = ( qGray( img.pixel( i - 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR + + qGray( img.pixel( i - 1, j ) ) * SHADOW_AXIS_FACTOR + + qGray( img.pixel( i - 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR + + qGray( img.pixel( i , j - 1 ) ) * SHADOW_AXIS_FACTOR + + 0 + + qGray( img.pixel( i , j + 1 ) ) * SHADOW_AXIS_FACTOR + + qGray( img.pixel( i + 1, j - 1 ) ) * SHADOW_DIAGONAL_FACTOR + + qGray( img.pixel( i + 1, j ) ) * SHADOW_AXIS_FACTOR + + qGray( img.pixel( i + 1, j + 1 ) ) * SHADOW_DIAGONAL_FACTOR ) / SHADOW_FACTOR; + + // update the shadow's i,j pixel. + if (alphaShadow > 0) + result.setPixel( i, j, qRgba( bgRed, bgGreen , bgBlue, + ( int ) (( alphaShadow > realOpacity ) ? realOpacity : alphaShadow) + ) ); + } +/*caused too much redraw problems if (period && i % period) { + qApp->processEvents(); + if (text() != origText) //text has been changed in the meantime: abort + return QImage(); + }*/ + } + return result; +} + +KPixmap KexiDBInternalLabel::getShadowPixmap() { + /*! + * Backup the default color used to draw text. + */ + const QColor textColor = colorGroup().foreground(); + + /*! + * Temporary storage for the generated shadow + */ + KPixmap finalPixmap, tempPixmap; + QImage shadowImage, tempImage; + QPainter painter; + + m_shadowRect = QRect(); + + tempPixmap.resize( size() ); + tempPixmap.fill( Qt::black ); + tempPixmap.setMask( tempPixmap.createHeuristicMask( true ) ); + + /*! + * The textcolor has to be white for creating shadows! + */ + setPaletteForegroundColor( Qt::white ); + + /*! + Draw the label "as usual" in a pixmap + */ + painter.begin( &tempPixmap ); + painter.setFont( font() ); + drawContents( &painter ); + painter.end(); + setPaletteForegroundColor( textColor ); + + /*! + * Calculate the first bounding rect. + * This will fit around the unmodified text. + */ + shadowImage = tempPixmap; + tempPixmap.setMask( QBitmap() ); + + /*! + Get the first bounding rect. + This may speed up makeShadow later. + */ + m_shadowRect = getBounding( shadowImage, m_shadowRect ); + + /*! + * Enlarge the bounding rect to make sure the shadow + * will fit in. + * The new rect has to fit in the pixmap. + * I have to admit this isn't really nice code... + */ + m_shadowRect.setX( QMAX( m_shadowRect.x() - ( m_shadowRect.width() / 4 ), 0 ) ); + m_shadowRect.setY( QMAX( m_shadowRect.y() - ( m_shadowRect.height() / 4 ), 0 ) ); + m_shadowRect.setBottomRight( QPoint( + QMIN( m_shadowRect.x() + ( m_shadowRect.width() * 3 / 2 ), shadowImage.width() ), + QMIN( m_shadowRect.y() + ( m_shadowRect.height() * 3 / 2 ), shadowImage.height() ) ) ); + + shadowImage = makeShadow( shadowImage, + qGray( colorGroup().background().rgb() ) < 127 ? Qt::white : Qt::black, + m_shadowRect ); + if (shadowImage.isNull()) + return KPixmap(); + + /*! + Now get the final bounding rect. + */ + m_shadowRect = getBounding( shadowImage, m_shadowRect ); + + /*! + Paint the labels background in a new pixmap. + */ + finalPixmap.resize( size() ); + painter.begin( &finalPixmap ); + painter.fillRect( 0, 0, finalPixmap.width(), finalPixmap.height(), + palette().brush( + isEnabled() ? QPalette::Active : QPalette::Disabled, + QColorGroup::Background ) ); + painter.end(); + + /*! + Copy the part of the background the shadow will be on + to another pixmap. + */ + tempPixmap.resize( m_shadowRect.size() ); + if (!finalPixmap.isNull()) { + bitBlt( &tempPixmap, 0, 0, &finalPixmap, + m_shadowRect.x() + SHADOW_OFFSET_X, + m_shadowRect.y() + SHADOW_OFFSET_Y, + m_shadowRect.width(), + m_shadowRect.height() ); + } + /*! + Replace the big background pixmap with the + part we could out just before. + */ + finalPixmap = tempPixmap; + + /*! + Copy the "interesting" part of the shadow image + to a new image. + I tried to copy this to a pixmap directly, + but it didn't work correctly. + Maybe a Qt bug? + */ + tempImage = shadowImage.copy( m_shadowRect ); + tempPixmap.convertFromImage( tempImage ); + /*! + Anyways, merge the shadow with the background. + */ + if (!tempPixmap.isNull()) { + bitBlt( &finalPixmap, 0, 0, &tempPixmap ); + } + + /** + Now move the rect. + Don't do this before the shadow is copied from shadowImage! + */ + m_shadowRect.moveBy( SHADOW_OFFSET_X, SHADOW_OFFSET_Y ); + + return finalPixmap; +} + +QRect KexiDBInternalLabel::getBounding( const QImage &image, const QRect& startRect ) { + QPoint topLeft; + QPoint bottomRight; + + const int startX = startRect.x(); + const int startY = startRect.y(); + /*! + * Ugly beast to get the correct width and height + */ + const int width = QMIN( ( startRect.bottomRight().x() > 0 + ? startRect.bottomRight().x() : QCOORD_MAX ), + image.width() ); + const int height = QMIN( ( startRect.bottomRight().y() > 0 + ? startRect.bottomRight().y() : QCOORD_MAX ), + image.height() ); + + /*! + Assume the first pixel has the color of the + background that has to be cut away. + Qt uses the four corner pixels to guess the + correct color, but in this case the topleft + pixel should be enough. + */ + QRgb trans = image.pixel( 0, 0 ); + + for ( int y = startY; y < height; y++ ) { + for ( int x = startX; x < width; x++ ) { + if ( image.pixel( x, y ) != trans ) { + topLeft.setY( y ); + y = height; + break; + } + } + } + + for ( int x = startX; x < width; x++ ) { + for ( int y = startY; y < height; y++ ) { + if ( image.pixel( x, y ) != trans ) { + topLeft.setX( x ); + x = width; + break; + } + } + } + + for ( int y = height - 1; y > topLeft.y(); y-- ) { + for ( int x = width - 1; x > topLeft.x(); x-- ) { + if ( image.pixel( x, y ) != trans ) { + bottomRight.setY( y + 1 ); + y = 0; + break; + } + } + } + + for ( int x = width - 1; x > topLeft.x(); x-- ) { + for ( int y = height - 1; y > topLeft.y(); y-- ) { + if ( image.pixel( x, y ) != trans ) { + bottomRight.setX( x + 1 ); + x = 0; + break; + } + } + } + + return QRect( + topLeft.x(), + topLeft.y(), + bottomRight.x() - topLeft.x(), + bottomRight.y() - topLeft.y() ); +} + +//========================================================= + +//! @internal +class KexiDBLabel::Private +{ + public: + Private() + : timer(0) +// , autonumberDisplayParameters(0) + , pixmapDirty( true ) + , shadowEnabled( false ) + , resizeEvent( false ) + { + } + ~Private() {} + KPixmap shadowPixmap; + QPoint shadowPosition; + KexiDBInternalLabel* internalLabel; + QTimer* timer; + QColor frameColor; + bool pixmapDirty : 1; + bool shadowEnabled : 1; + bool resizeEvent : 1; +}; + +//========================================================= + +KexiDBLabel::KexiDBLabel( QWidget *parent, const char *name, WFlags f ) + : QLabel( parent, name, f ) + , KexiDBTextWidgetInterface() + , KexiFormDataItemInterface() + , d( new Private() ) +{ + init(); +} + +KexiDBLabel::KexiDBLabel( const QString& text, QWidget *parent, const char *name, WFlags f ) + : QLabel( parent, name, f ) + , KexiDBTextWidgetInterface() + , KexiFormDataItemInterface() + , d( new Private() ) +{ + init(); + setText( text ); +} + +KexiDBLabel::~KexiDBLabel() +{ + delete d; +} + +void KexiDBLabel::init() +{ + m_hasFocusableWidget = false; + d->internalLabel = new KexiDBInternalLabel( this ); + d->internalLabel->hide(); + d->frameColor = palette().active().foreground(); + + setAlignment( d->internalLabel->alignment() ); +} + +void KexiDBLabel::updatePixmapLater() { + if (d->resizeEvent) { + if (!d->timer) { + d->timer = new QTimer(this, "KexiDBLabelTimer"); + connect(d->timer, SIGNAL(timeout()), this, SLOT(updatePixmap())); + } + d->timer->start(100, true); + d->resizeEvent = false; + return; + } + if (d->timer && d->timer->isActive()) + return; + updatePixmap(); +} + +void KexiDBLabel::updatePixmap() { + /*! + Whatever has changed in KexiDBLabel, + every parameter is set to our private-label. + Just in case... + */ + d->internalLabel->setText( text() ); + d->internalLabel->setFixedSize( size() ); + d->internalLabel->setPalette( palette() ); + d->internalLabel->setAlignment( alignment() ); +// d->shadowPixmap = KPixmap(); //parallel repaints won't hurt us cause incomplete pixmap + KPixmap shadowPixmap = d->internalLabel->getShadowPixmap(); + if (shadowPixmap.isNull()) + return; + d->shadowPixmap = shadowPixmap; + d->shadowPosition = d->internalLabel->m_shadowRect.topLeft(); + d->pixmapDirty = false; + repaint(); +} + +void KexiDBLabel::paintEvent( QPaintEvent* e ) +{ + QPainter p( this ); + if ( d->shadowEnabled ) { + /*! + If required, update the pixmap-cache. + */ + if ( d->pixmapDirty ) { + updatePixmapLater(); + } + + /*! + If the part that should be redrawn intersects with our shadow, + redraw the shadow where it intersects with e->rect(). + Have to move the clipping rect around a bit because + the shadow has to be drawn using an offset relative to + the widgets border. + */ + if ( !d->pixmapDirty && e->rect().contains( d->shadowPosition ) && !d->shadowPixmap.isNull()) { + QRect clipRect = QRect( + QMAX( e->rect().x() - d->shadowPosition.x(), 0 ), + QMAX( e->rect().y() - d->shadowPosition.y(), 0 ), + QMIN( e->rect().width() + d->shadowPosition.x(), d->shadowPixmap.width() ), + QMIN( e->rect().height() + d->shadowPosition.y(), d->shadowPixmap.height() ) ); + p.drawPixmap( d->internalLabel->m_shadowRect.topLeft(), d->shadowPixmap, clipRect ); + } + } + KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), false ); + QLabel::paintEvent( e ); +} + +void KexiDBLabel::setValueInternal( const QVariant& add, bool removeOld ) { + if (removeOld) + setText(add.toString()); + else + setText( m_origValue.toString() + add.toString() ); +} + +QVariant KexiDBLabel::value() { + return text(); +} + +void KexiDBLabel::setInvalidState( const QString& displayText ) +{ + setText( displayText ); +} + +bool KexiDBLabel::valueIsNull() +{ + return text().isNull(); +} + +bool KexiDBLabel::valueIsEmpty() +{ + return text().isEmpty(); +} + +bool KexiDBLabel::isReadOnly() const +{ + return true; +} + +void KexiDBLabel::setReadOnly( bool readOnly ) +{ + Q_UNUSED(readOnly); +} + +QWidget* KexiDBLabel::widget() +{ + return this; +} + +bool KexiDBLabel::cursorAtStart() +{ + return false; +} + +bool KexiDBLabel::cursorAtEnd() +{ + return false; +} + +void KexiDBLabel::clear() +{ + setText(QString::null); +} + +bool KexiDBLabel::setProperty( const char * name, const QVariant & value ) +{ + const bool ret = QLabel::setProperty(name, value); + if (d->shadowEnabled) { + if (0==qstrcmp("indent", name) || 0==qstrcmp("font", name) || 0==qstrcmp("margin", name) + || 0==qstrcmp("frameShadow", name) || 0==qstrcmp("frameShape", name) + || 0==qstrcmp("frameStyle", name) || 0==qstrcmp("midLineWidth", name) + || 0==qstrcmp("lineWidth", name)) { + d->internalLabel->setProperty(name, value); + updatePixmap(); + } + } + return ret; +} + +void KexiDBLabel::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) +{ + KexiFormDataItemInterface::setColumnInfo(cinfo); + KexiDBTextWidgetInterface::setColumnInfo(cinfo, this); +} + +void KexiDBLabel::setShadowEnabled( bool state ) { + d->shadowEnabled = state; + d->pixmapDirty = true; + if (state) + d->internalLabel->updateFrame(); + repaint(); +} + +void KexiDBLabel::resizeEvent( QResizeEvent* e ) { + if (isVisible()) + d->resizeEvent = true; + d->pixmapDirty = true; + QLabel::resizeEvent( e ); +} + +void KexiDBLabel::fontChange( const QFont& font ) { + d->pixmapDirty = true; + d->internalLabel->setFont( font ); + QLabel::fontChange( font ); +} + +void KexiDBLabel::styleChange( QStyle& style ) { + d->pixmapDirty = true; + QLabel::styleChange( style ); +} + +void KexiDBLabel::enabledChange( bool enabled ) { + d->pixmapDirty = true; + d->internalLabel->setEnabled( enabled ); + QLabel::enabledChange( enabled ); +} + +void KexiDBLabel::paletteChange( const QPalette& oldPal ) { + Q_UNUSED(oldPal); + d->pixmapDirty = true; + d->internalLabel->setPalette( palette() ); +} + +/*const QColor & KexiDBLabel::paletteForegroundColor () const +{ + return d->foregroundColor; +} + +void KexiDBLabel::setPaletteForegroundColor ( const QColor& color ) +{ + d->foregroundColor = color; +}*/ + +void KexiDBLabel::frameChanged() { + d->pixmapDirty = true; + d->internalLabel->updateFrame(); + QFrame::frameChanged(); +} + +void KexiDBLabel::showEvent( QShowEvent* e ) { + d->pixmapDirty = true; + QLabel::showEvent( e ); +} + +void KexiDBLabel::setText( const QString& text ) { + d->pixmapDirty = true; + QLabel::setText( text ); + //This is necessary for KexiFormDataItemInterface + valueChanged(); + repaint(); +} + +bool KexiDBLabel::shadowEnabled() const +{ + return d->shadowEnabled; +} + +#define ClassName KexiDBLabel +#define SuperClassName QLabel +#include "kexiframeutils_p.cpp" +#include "kexidblabel.moc" diff --git a/kexi/plugins/forms/widgets/kexidblabel.h b/kexi/plugins/forms/widgets/kexidblabel.h new file mode 100644 index 00000000..ec4e626a --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidblabel.h @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Christian Nitschkowski <segfault_ii@web.de> + Copyright (C) 2005 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. +*/ + +#ifndef KEXIDBLABEL_H +#define KEXIDBLABEL_H + +#include <qimage.h> +#include <qlabel.h> + +#include <kpixmap.h> + +#include "../kexiformdataiteminterface.h" +#include "../kexidbtextwidgetinterface.h" +#include <widget/utils/kexidisplayutils.h> + +class QPainter; +class QTimer; +class KexiDBInternalLabel; + +//! @short An extended, data-aware, read-only text label. +/*! It's text may have a drop-shadow. + + @author Christian Nitschkowski, Jaroslaw Staniek +*/ +class KEXIFORMUTILS_EXPORT KexiDBLabel : public QLabel, protected KexiDBTextWidgetInterface, public KexiFormDataItemInterface { + Q_OBJECT + Q_PROPERTY( QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true ) + Q_PROPERTY( QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true ) + Q_PROPERTY( bool shadowEnabled READ shadowEnabled WRITE setShadowEnabled DESIGNABLE true ) + Q_OVERRIDE( QPixmap pixmap DESIGNABLE false ) + Q_OVERRIDE( bool scaledContents DESIGNABLE false ) +// Q_OVERRIDE( QColor paletteForegroundColor READ paletteForegroundColor WRITE setPaletteForegroundColor DESIGNABLE true ) + Q_PROPERTY( QColor frameColor READ frameColor WRITE setFrameColor DESIGNABLE true ) + + public: + KexiDBLabel( QWidget *parent, const char *name = 0, WFlags f = 0 ); + KexiDBLabel( const QString& text, QWidget *parent, const char *name = 0, WFlags f = 0 ); + virtual ~KexiDBLabel(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + + virtual QVariant value(); + + bool shadowEnabled() const; + + virtual void setInvalidState( const QString& displayText ); + + virtual bool valueIsNull(); + + virtual bool valueIsEmpty(); + + //! always true + virtual bool isReadOnly() const; + + virtual QWidget* widget(); + + //! always false + virtual bool cursorAtStart(); + + //! always false + virtual bool cursorAtEnd(); + + virtual void clear(); + + //! used to catch setIndent(), etc. + virtual bool setProperty ( const char * name, const QVariant & value ); + + virtual const QColor& frameColor() const; + +// const QColor & paletteForegroundColor() const; + + public slots: + //! Sets the datasource to \a ds + inline void setDataSource( const QString &ds ) { KexiFormDataItemInterface::setDataSource( ds ); } + + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + + virtual void setText( const QString& text ); + + /*! Enable/Disable the shadow effect. + KexiDBLabel acts just like a normal QLabel when shadow is disabled. */ + void setShadowEnabled( bool state ); + + virtual void setPalette( const QPalette &pal ); + + virtual void setFrameColor(const QColor& color); + +// void setPaletteForegroundColor( const QColor& color ); + + protected slots: + //! empty + virtual void setReadOnly( bool readOnly ); + void updatePixmap(); + + protected: + void init(); + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo); + virtual void paintEvent( QPaintEvent* ); + virtual void resizeEvent( QResizeEvent* e ); + + //! Sets value \a value for a widget. + virtual void setValueInternal( const QVariant& add, bool removeOld ); + + virtual void fontChange( const QFont& font ); + virtual void styleChange( QStyle& style ); + virtual void enabledChange( bool enabled ); + + virtual void paletteChange( const QPalette& oldPal ); + virtual void frameChanged(); + virtual void showEvent( QShowEvent* e ); + + //! Reimplemented to paint using real frame color instead of froeground. + //! Also allows to paint more types of frame. + virtual void drawFrame( QPainter * ); + + void updatePixmapLater(); + + class Private; + Private *d; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidblineedit.cpp b/kexi/plugins/forms/widgets/kexidblineedit.cpp new file mode 100644 index 00000000..3897a8cb --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidblineedit.cpp @@ -0,0 +1,417 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2007 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 "kexidblineedit.h" +#include "kexidbautofield.h" + +#include <kdebug.h> +#include <knumvalidator.h> +#include <kdatetbl.h> + +#include <qpopupmenu.h> +#include <qpainter.h> + +#include <kexiutils/utils.h> +#include <kexidb/queryschema.h> +#include <kexidb/fieldvalidator.h> +#include <kexiutils/utils.h> + +//! @todo reenable as an app aption +//#define USE_KLineEdit_setReadOnly + +//! @internal A validator used for read only flag to disable editing +class KexiDBLineEdit_ReadOnlyValidator : public QValidator +{ + public: + KexiDBLineEdit_ReadOnlyValidator( QObject * parent ) + : QValidator(parent) + { + } + ~KexiDBLineEdit_ReadOnlyValidator() {} + virtual State validate( QString &, int & ) const { return Invalid; } +}; + +//----- + +KexiDBLineEdit::KexiDBLineEdit(QWidget *parent, const char *name) + : KLineEdit(parent, name) + , KexiDBTextWidgetInterface() + , KexiFormDataItemInterface() +//moved , m_dateFormatter(0) +//moved , m_timeFormatter(0) + , m_menuExtender(this, this) + , m_internalReadOnly(false) + , m_slotTextChanged_enabled(true) +{ +#ifdef USE_KLineEdit_setReadOnly +//! @todo reenable as an app aption + QPalette p(widget->palette()); + p.setColor( lighterGrayBackgroundColor(palette()) ); + widget->setPalette(p); +#endif + + connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged(const QString&))); +} + +KexiDBLineEdit::~KexiDBLineEdit() +{ +//moved delete m_dateFormatter; +//moved delete m_timeFormatter; +} + +void KexiDBLineEdit::setInvalidState( const QString& displayText ) +{ + KLineEdit::setReadOnly(true); +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); + setText(displayText); +} + +void KexiDBLineEdit::setValueInternal(const QVariant& add, bool removeOld) +{ +#if 0 //moved to KexiTextFormatter + QVariant value; + if (removeOld) + value = add; + else { + if (add.toString().isEmpty()) + value = m_origValue; + else + value = m_origValue.toString() + add.toString(); + } + + if (m_columnInfo) { + const KexiDB::Field::Type t = m_columnInfo->field->type(); + if (t == KexiDB::Field::Boolean) { + //! @todo temporary solution for booleans! + setText( value.toBool() ? "1" : "0" ); + return; + } + else if (t == KexiDB::Field::Date) { + setText( dateFormatter()->dateToString( value.toString().isEmpty() ? QDate() : value.toDate() ) ); + setCursorPosition(0); //ok? + return; + } + else if (t == KexiDB::Field::Time) { + setText( + timeFormatter()->timeToString( + //hack to avoid converting null variant to valid QTime(0,0,0) + value.toString().isEmpty() ? value.toTime() : QTime(99,0,0) + ) + ); + setCursorPosition(0); //ok? + return; + } + else if (t == KexiDB::Field::DateTime) { + if (value.toString().isEmpty() ) { + setText( QString::null ); + } + else { + setText( + dateFormatter()->dateToString( value.toDateTime().date() ) + " " + + timeFormatter()->timeToString( value.toDateTime().time() ) + ); + } + setCursorPosition(0); //ok? + return; + } + } +#endif + m_slotTextChanged_enabled = false; + setText( m_textFormatter.valueToText(removeOld ? QVariant() : m_origValue, add.toString()) ); +// setText( value.toString() ); + setCursorPosition(0); //ok? + m_slotTextChanged_enabled = true; +} + +QVariant KexiDBLineEdit::value() +{ + return m_textFormatter.textToValue( text() ); +#if 0 // moved to KexiTextFormatter + if (! m_columnInfo) + return QVariant(); + const KexiDB::Field::Type t = m_columnInfo->field->type(); + switch (t) { + case KexiDB::Field::Text: + case KexiDB::Field::LongText: + return text(); + case KexiDB::Field::Byte: + case KexiDB::Field::ShortInteger: + return text().toShort(); +//! @todo uint, etc? + case KexiDB::Field::Integer: + return text().toInt(); + case KexiDB::Field::BigInteger: + return text().toLongLong(); + case KexiDB::Field::Boolean: + //! @todo temporary solution for booleans! + return text() == "1" ? QVariant(true,1) : QVariant(false,0); + case KexiDB::Field::Date: + return dateFormatter()->stringToVariant( text() ); + case KexiDB::Field::Time: + return timeFormatter()->stringToVariant( text() ); + case KexiDB::Field::DateTime: + return stringToDateTime(*dateFormatter(), *timeFormatter(), text()); + case KexiDB::Field::Float: + return text().toFloat(); + case KexiDB::Field::Double: + return text().toDouble(); + default: + return QVariant(); + } +//! @todo more data types! + return text(); +#endif +} + +void KexiDBLineEdit::slotTextChanged(const QString&) +{ + if (!m_slotTextChanged_enabled) + return; + signalValueChanged(); +} + +bool KexiDBLineEdit::valueIsNull() +{ + return valueIsEmpty(); //ok??? text().isNull(); +} + +bool KexiDBLineEdit::valueIsEmpty() +{ + return m_textFormatter.valueIsEmpty( text() ); +#if 0 // moved to KexiTextFormatter + if (text().isEmpty()) + return true; + + if (m_columnInfo) { + const KexiDB::Field::Type t = m_columnInfo->field->type(); + if (t == KexiDB::Field::Date || ) + return dateFormatter()->isEmpty( text() ); + else if (t == KexiDB::Field::Time) + return timeFormatter()->isEmpty( text() ); + else if (t == KexiDB::Field::Time) + return dateTimeIsEmpty( *dateFormatter(), *timeFormatter(), text() ); + } + +//! @todo + return text().isEmpty(); +#endif +} + +bool KexiDBLineEdit::valueIsValid() +{ + return m_textFormatter.valueIsValid( text() ); +#if 0 // moved to KexiTextFormatter + if (!m_columnInfo) + return true; +//! @todo fix for fields with "required" property = true + if (valueIsEmpty()/*ok?*/) + return true; + + const KexiDB::Field::Type t = m_columnInfo->field->type(); + if (t == KexiDB::Field::Date) + return dateFormatter()->stringToVariant( text() ).isValid(); + else if (t == KexiDB::Field::Time) + return timeFormatter()->stringToVariant( text() ).isValid(); + else if (t == KexiDB::Field::DateTime) + return dateTimeIsValid( *dateFormatter(), *timeFormatter(), text() ); + +//! @todo + return true; +#endif +} + +bool KexiDBLineEdit::isReadOnly() const +{ + return m_internalReadOnly; +} + +void KexiDBLineEdit::setReadOnly( bool readOnly ) +{ +#ifdef USE_KLineEdit_setReadOnly +//! @todo reenable as an app aption + return KLineEdit::setReadOnly( readOnly ); +#else + m_internalReadOnly = readOnly; + if (m_internalReadOnly) { + m_readWriteValidator = validator(); + if (!m_readOnlyValidator) + m_readOnlyValidator = new KexiDBLineEdit_ReadOnlyValidator(this); + setValidator( m_readOnlyValidator ); + } + else { + //revert to r/w validator + setValidator( m_readWriteValidator ); + } + m_menuExtender.updatePopupMenuActions(); +#endif +} + +QPopupMenu * KexiDBLineEdit::createPopupMenu() +{ + QPopupMenu *contextMenu = KLineEdit::createPopupMenu(); + m_menuExtender.createTitle(contextMenu); + return contextMenu; +} + + +QWidget* KexiDBLineEdit::widget() +{ + return this; +} + +bool KexiDBLineEdit::cursorAtStart() +{ + return cursorPosition()==0; +} + +bool KexiDBLineEdit::cursorAtEnd() +{ + return cursorPosition()==(int)text().length(); +} + +void KexiDBLineEdit::clear() +{ + if (!m_internalReadOnly) + KLineEdit::clear(); +} + + +void KexiDBLineEdit::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) +{ + KexiFormDataItemInterface::setColumnInfo(cinfo); + m_textFormatter.setField( cinfo ? cinfo->field : 0 ); + + if (!cinfo) + return; + +//! @todo handle input mask (via QLineEdit::setInputMask()) using a special KexiDB::FieldInputMask class + setValidator( new KexiDB::FieldValidator(*cinfo->field, this) ); + +#if 0 // moved to KexiTextFormatter + if (t==KexiDB::Field::Date) { +//! @todo use KDateWidget? + setInputMask( dateFormatter()->inputMask() ); + } + else if (t==KexiDB::Field::Time) { +//! @todo use KTimeWidget +// setInputMask("00:00:00"); + setInputMask( timeFormatter()->inputMask() ); + } + else if (t==KexiDB::Field::DateTime) { + setInputMask( + dateTimeInputMask( *dateFormatter(), *timeFormatter() ) ); + } +#endif + const QString inputMask( m_textFormatter.inputMask() ); + if (!inputMask.isEmpty()) + setInputMask( inputMask ); + + KexiDBTextWidgetInterface::setColumnInfo(cinfo, this); +} + +/*todo +void KexiDBLineEdit::paint( QPainter *p ) +{ + KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), hasFocus() ); +}*/ + +void KexiDBLineEdit::paintEvent ( QPaintEvent *pe ) +{ + KLineEdit::paintEvent( pe ); + QPainter p(this); + KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), hasFocus() ); +} + +bool KexiDBLineEdit::event( QEvent * e ) +{ + const bool ret = KLineEdit::event( e ); + KexiDBTextWidgetInterface::event(e, this, text().isEmpty()); + if (e->type()==QEvent::FocusOut) { + QFocusEvent *fe = static_cast<QFocusEvent *>(e); +// if (fe->reason()!=QFocusEvent::ActiveWindow && fe->reason()!=QFocusEvent::Popup) { + if (fe->reason()==QFocusEvent::Tab || fe->reason()==QFocusEvent::Backtab) { + //display aligned to left after loosing the focus (only if this is tab/backtab event) +//! @todo add option to set cursor at the beginning + setCursorPosition(0); //ok? + } + } + return ret; +} + +bool KexiDBLineEdit::appendStretchRequired(KexiDBAutoField* autoField) const +{ + return KexiDBAutoField::Top == autoField->labelPosition(); +} + +void KexiDBLineEdit::handleAction(const QString& actionName) +{ + if (actionName=="edit_copy") { + copy(); + } + else if (actionName=="edit_paste") { + paste(); + } + else if (actionName=="edit_cut") { + cut(); + } + //! @todo ? +} + +void KexiDBLineEdit::setDisplayDefaultValue(QWidget *widget, bool displayDefaultValue) +{ + KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue); + // initialize display parameters for default / entered value + KexiDisplayUtils::DisplayParameters * const params + = displayDefaultValue ? m_displayParametersForDefaultValue : m_displayParametersForEnteredValue; + setFont(params->font); + QPalette pal(palette()); + pal.setColor(QPalette::Active, QColorGroup::Text, params->textColor); + setPalette(pal); +} + +void KexiDBLineEdit::undo() +{ + cancelEditor(); +} + +void KexiDBLineEdit::moveCursorToEnd() +{ + KLineEdit::end(false/*!mark*/); +} + +void KexiDBLineEdit::moveCursorToStart() +{ + KLineEdit::home(false/*!mark*/); +} + +void KexiDBLineEdit::selectAll() +{ + KLineEdit::selectAll(); +} + +bool KexiDBLineEdit::keyPressed(QKeyEvent *ke) +{ + Q_UNUSED(ke); + return false; +} + +#include "kexidblineedit.moc" diff --git a/kexi/plugins/forms/widgets/kexidblineedit.h b/kexi/plugins/forms/widgets/kexidblineedit.h new file mode 100644 index 00000000..5f0262b2 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidblineedit.h @@ -0,0 +1,170 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2007 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. +*/ + +#ifndef KexiDBLineEdit_H +#define KexiDBLineEdit_H + +#include <klineedit.h> +#include <qvalidator.h> + +#include "kexiformdataiteminterface.h" +#include "kexidbtextwidgetinterface.h" +#include "kexidbutils.h" +#include <widget/tableview/kexitextformatter.h> +#include <widget/utils/kexidatetimeformatter.h> + +class KexiDBWidgetContextMenuExtender; + +/*! @internal Utility: alter background color to be a blended color + of the background and base (usually lighter gray). Used for read-only mode. */ +void setLighterGrayBackgroundColor(QWidget* widget); + +//! @short Line edit widget for Kexi forms +/*! Handles many data types. User input is validated by using validators + and/or input masks. +*/ +class KEXIFORMUTILS_EXPORT KexiDBLineEdit : + public KLineEdit, + protected KexiDBTextWidgetInterface, + public KexiFormDataItemInterface, + public KexiSubwidgetInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + Q_OVERRIDE(bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true) + + public: + KexiDBLineEdit(QWidget *parent, const char *name=0); + virtual ~KexiDBLineEdit(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return true if the value is valid */ + virtual bool valueIsValid(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! If \a displayDefaultValue is true, the value set by KexiDataItemInterface::setValue() + is displayed in a special way. Used by KexiFormDataProvider::fillDataItems(). + \a widget is equal to 'this'. + Reimplemented after KexiFormDataItemInterface. */ + virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue); + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo); + + /*! Handles action having standard name \a actionName. + Action could be: "edit_copy", "edit_paste", etc. + Reimplemented after KexiDataItemChangesListener. */ + virtual void handleAction(const QString& actionName); + + /*! Called by top-level form on key press event to consume widget-specific shortcuts. */ + virtual bool keyPressed(QKeyEvent *ke); + + public slots: + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + virtual void setReadOnly( bool readOnly ); + + //! Reimplemented, so "undo" means the same as "cancelEditor" action + virtual void undo(); + + //! Implemented for KexiDataItemInterface + virtual void moveCursorToEnd(); + + //! Implemented for KexiDataItemInterface + virtual void moveCursorToStart(); + + //! Implemented for KexiDataItemInterface + virtual void selectAll(); + + protected slots: + void slotTextChanged(const QString&); + + protected: + virtual void paintEvent ( QPaintEvent * ); + virtual void setValueInternal(const QVariant& add, bool removeOld); + virtual bool event ( QEvent * ); + +#if 0 +//moved to KexiTextFormatter + inline KexiDateFormatter* dateFormatter() { + return m_dateFormatter ? m_dateFormatter : m_dateFormatter = new KexiDateFormatter(); + } + + inline KexiTimeFormatter* timeFormatter() { + return m_timeFormatter ? m_timeFormatter : m_timeFormatter = new KexiTimeFormatter(); + } +#endif + + virtual QPopupMenu * createPopupMenu(); + + //! Implemented for KexiSubwidgetInterface + virtual bool appendStretchRequired(KexiDBAutoField* autoField) const; + +#if 0 +//moved to KexiTextFormatter + //! Used for date and date/time types + KexiDateFormatter* m_dateFormatter; + //! Used for time and date/time types + KexiTimeFormatter* m_timeFormatter; +#endif + //! Used to format text + KexiTextFormatter m_textFormatter; + + //! Used for read only flag to disable editing + QGuardedPtr<const QValidator> m_readOnlyValidator; + + //! Used to remember the previous validator used forf r/w mode, after setting the read only flag + QGuardedPtr<const QValidator> m_readWriteValidator; + + //! Used for extending context menu + KexiDBWidgetContextMenuExtender m_menuExtender; + + //! Used in isReadOnly, as sometimes we want to have the flag set tot true when KLineEdit::isReadOnly + //! is still false. + bool m_internalReadOnly : 1; + + //! Used in slotTextChanged() + bool m_slotTextChanged_enabled : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbsubform.cpp b/kexi/plugins/forms/widgets/kexidbsubform.cpp new file mode 100644 index 00000000..8d1971a9 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbsubform.cpp @@ -0,0 +1,131 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl> + + This library 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 library 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 library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexidbsubform.h" + +#include "kexidbform.h" +#include "../kexiformview.h" +#include <kexidb/utils.h> +#include <formeditor/formIO.h> +#include <formeditor/objecttree.h> +#include <formeditor/utils.h> +#include <formeditor/container.h> +#include <formeditor/formmanager.h> + +KexiDBSubForm::KexiDBSubForm(KFormDesigner::Form *parentForm, QWidget *parent, const char *name) +: QScrollView(parent, name), m_parentForm(parentForm), m_form(0), m_widget(0) +{ + setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + viewport()->setPaletteBackgroundColor(colorGroup().mid()); +} +/* +void +KexiDBSubForm::paintEvent(QPaintEvent *ev) +{ + QScrollView::paintEvent(ev); + QPainter p; + + setWFlags(WPaintUnclipped); + + QString txt("Subform"); + QFont f = font(); + f.setPointSize(f.pointSize() * 3); + QFontMetrics fm(f); + const int txtw = fm.width(txt), txth = fm.height(); + + p.begin(this, true); + p.setPen(black); + p.setFont(f); + p.drawText(width()/2, height()/2, txt, Qt::AlignCenter|Qt::AlignVCenter); + p.end(); + + clearWFlags( WPaintUnclipped ); +} +*/ +void +KexiDBSubForm::setFormName(const QString &name) +{ + if(m_formName==name) + return; + + m_formName = name; //assign, even if the name points to nowhere + + if(name.isEmpty()) { + delete m_widget; + m_widget = 0; + updateScrollBars(); + return; + } + + QWidget *pw = parentWidget(); + KexiFormView *view = 0; + QStringList list; + while(pw) { + if(pw->isA("KexiDBSubForm")) { + if(list.contains(pw->name())) { +//! @todo error message + return; // Be sure to don't run into a endless-loop cause of recursive subforms. + } + list.append(pw->name()); + } + else if(! view && pw->isA("KexiFormView")) + view = static_cast<KexiFormView*>(pw); // we need a KexiFormView* + pw = pw->parentWidget(); + } + + if (!view || !view->parentDialog() || !view->parentDialog()->mainWin() + || !view->parentDialog()->mainWin()->project()->dbConnection()) + return; + + KexiDB::Connection *conn = view->parentDialog()->mainWin()->project()->dbConnection(); + + // we check if there is a form with this name + int id = KexiDB::idForObjectName(*conn, name, KexiPart::FormObjectType); + if((id == 0) || (id == view->parentDialog()->id())) // == our form + return; // because of recursion when loading + + // we create the container widget + delete m_widget; + m_widget = new KexiDBFormBase(viewport(), "KexiDBSubForm_widget"); + m_widget->show(); + addChild(m_widget); + m_form = new KFormDesigner::Form(KexiFormPart::library(), this->name()); + m_form->createToplevel(m_widget); + + // and load the sub form + QString data; + tristate res = conn->loadDataBlock(id, data, QString::null); + if (res == true) + res = KFormDesigner::FormIO::loadFormFromString(m_form, m_widget, data); + if(res != true) { + delete m_widget; + m_widget = 0; + updateScrollBars(); + m_formName = QString::null; + return; + } + m_form->setDesignMode(false); + + // Install event filters on the whole newly created form + KFormDesigner::ObjectTreeItem *tree = m_parentForm->objectTree()->lookup(QObject::name()); + KFormDesigner::installRecursiveEventFilter(this, tree->eventEater()); +} + +#include "kexidbsubform.moc" diff --git a/kexi/plugins/forms/widgets/kexidbsubform.h b/kexi/plugins/forms/widgets/kexidbsubform.h new file mode 100644 index 00000000..5b73f860 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbsubform.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 Jaroslaw Staniek <js@iidea.pl> + + This library 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 library 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 library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KexiDBSubForm_H +#define KexiDBSubForm_H + +#include <qscrollview.h> +#include <formeditor/form.h> + +//! @short A form embedded as a widget inside other form +class KEXIFORMUTILS_EXPORT KexiDBSubForm : public QScrollView +{ + Q_OBJECT + Q_PROPERTY(QString formName READ formName WRITE setFormName DESIGNABLE true) + + public: + KexiDBSubForm(KFormDesigner::Form *parentForm, QWidget *parent, const char *name); + ~KexiDBSubForm() {} + + //! \return the name of the subform to display inside this widget + QString formName() const { return m_formName; } + + //! Sets the name of the subform to display inside this widget + void setFormName(const QString &name); + + //void paintEvent(QPaintEvent *ev); + + private: + KFormDesigner::Form *m_parentForm; + KFormDesigner::Form *m_form; + QWidget *m_widget; + QString m_formName; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbtextedit.cpp b/kexi/plugins/forms/widgets/kexidbtextedit.cpp new file mode 100644 index 00000000..8541fc01 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbtextedit.cpp @@ -0,0 +1,209 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-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 "kexidbtextedit.h" +#include "kexidblineedit.h" +#include <kexidb/queryschema.h> + +#include <kapplication.h> +#include <kstdaccel.h> +#include <kdebug.h> + +#include <qpainter.h> + +KexiDBTextEdit::KexiDBTextEdit(QWidget *parent, const char *name) + : KTextEdit(parent, name) + , KexiDBTextWidgetInterface() + , KexiFormDataItemInterface() + , m_menuExtender(this, this) + , m_slotTextChanged_enabled(true) +{ + connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged())); + installEventFilter(this); +} + +KexiDBTextEdit::~KexiDBTextEdit() +{ +} + +void KexiDBTextEdit::setInvalidState( const QString& displayText ) +{ + setReadOnly(true); +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); + KTextEdit::setText(displayText); +} + +void KexiDBTextEdit::setValueInternal(const QVariant& add, bool removeOld) +{ + if (m_columnInfo && m_columnInfo->field->type()==KexiDB::Field::Boolean) { +//! @todo temporary solution for booleans! + KTextEdit::setText( add.toBool() ? "1" : "0" ); + } + else { + if (removeOld) + KTextEdit::setText( add.toString() ); + else + KTextEdit::setText( m_origValue.toString() + add.toString() ); + } +} + +QVariant KexiDBTextEdit::value() +{ + return text(); +} + +void KexiDBTextEdit::slotTextChanged() +{ + if (!m_slotTextChanged_enabled) + return; + signalValueChanged(); +} + +bool KexiDBTextEdit::valueIsNull() +{ + return text().isNull(); +} + +bool KexiDBTextEdit::valueIsEmpty() +{ + return text().isEmpty(); +} + +bool KexiDBTextEdit::isReadOnly() const +{ + return KTextEdit::isReadOnly(); +} + +void KexiDBTextEdit::setReadOnly( bool readOnly ) +{ + KTextEdit::setReadOnly( readOnly ); + QPalette p = palette(); + QColor c(readOnly ? lighterGrayBackgroundColor(kapp->palette()) : p.color(QPalette::Normal, QColorGroup::Base)); + setPaper( c ); + p.setColor(QColorGroup::Base, c); + p.setColor(QColorGroup::Background, c); + setPalette( p ); +} + +void KexiDBTextEdit::setText( const QString & text, const QString & context ) +{ + KTextEdit::setText(text, context); +} + +QWidget* KexiDBTextEdit::widget() +{ + return this; +} + +bool KexiDBTextEdit::cursorAtStart() +{ + int para, index; + getCursorPosition ( ¶, &index ); + return para==0 && index==0; +} + +bool KexiDBTextEdit::cursorAtEnd() +{ + int para, index; + getCursorPosition ( ¶, &index ); + return (paragraphs()-1)==para && (paragraphLength(paragraphs()-1)-1)==index; +} + +void KexiDBTextEdit::clear() +{ + setText(QString::null, QString::null); +} + +void KexiDBTextEdit::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) +{ + KexiFormDataItemInterface::setColumnInfo(cinfo); + if (!cinfo) + return; + KexiDBTextWidgetInterface::setColumnInfo(m_columnInfo, this); +} + +void KexiDBTextEdit::paintEvent ( QPaintEvent *pe ) +{ + KTextEdit::paintEvent( pe ); + QPainter p(this); + KexiDBTextWidgetInterface::paint( this, &p, text().isEmpty(), alignment(), hasFocus() ); +} + +QPopupMenu * KexiDBTextEdit::createPopupMenu(const QPoint & pos) +{ + QPopupMenu *contextMenu = KTextEdit::createPopupMenu(pos); + m_menuExtender.createTitle(contextMenu); + return contextMenu; +} + +void KexiDBTextEdit::undo() +{ + cancelEditor(); +} + +void KexiDBTextEdit::setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue) +{ + KexiFormDataItemInterface::setDisplayDefaultValue(widget, displayDefaultValue); + // initialize display parameters for default / entered value + KexiDisplayUtils::DisplayParameters * const params + = displayDefaultValue ? m_displayParametersForDefaultValue : m_displayParametersForEnteredValue; + QPalette pal(palette()); + pal.setColor(QPalette::Active, QColorGroup::Text, params->textColor); + setPalette(pal); + setFont(params->font); +//! @todo support rich text... +/* m_slotTextChanged_enabled = false; + //for rich text... + const QString origText( text() ); + KTextEdit::setText(QString::null); + setCurrentFont(params->font); + setColor(params->textColor); + KTextEdit::setText(origText); + m_slotTextChanged_enabled = true;*/ +} + +void KexiDBTextEdit::moveCursorToEnd() +{ + KTextEdit::setCursorPosition(paragraphs()-1, paragraphLength( paragraphs()-1 )); +} + +void KexiDBTextEdit::moveCursorToStart() +{ + KTextEdit::setCursorPosition(0 /*para*/, 0 /*index*/); +} + +void KexiDBTextEdit::selectAll() +{ + KTextEdit::selectAll(); +} + +void KexiDBTextEdit::keyPressEvent( QKeyEvent *ke ) +{ + // for instance, Windows uses Ctrl+Tab for moving between tabs, so do not steal this shortcut + if (KStdAccel::tabNext().contains( KKey(ke) ) || KStdAccel::tabPrev().contains( KKey(ke) )) { + ke->ignore(); + return; + } + KTextEdit::keyPressEvent(ke); +} + +#include "kexidbtextedit.moc" diff --git a/kexi/plugins/forms/widgets/kexidbtextedit.h b/kexi/plugins/forms/widgets/kexidbtextedit.h new file mode 100644 index 00000000..a380b070 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbtextedit.h @@ -0,0 +1,113 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2007 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. +*/ + +#ifndef KexiDBTextEdit_H +#define KexiDBTextEdit_H + +#include "kexiformdataiteminterface.h" +#include "kexidbtextwidgetinterface.h" +#include "kexidbutils.h" +#include <ktextedit.h> + +//! @short Multiline edit widget for Kexi forms +class KEXIFORMUTILS_EXPORT KexiDBTextEdit : + public KTextEdit, + protected KexiDBTextWidgetInterface, + public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + + public: + KexiDBTextEdit(QWidget *parent, const char *name=0); + virtual ~KexiDBTextEdit(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo); + + /*! If \a displayDefaultValue is true, the value set by KexiDataItemInterface::setValue() + is displayed in a special way. Used by KexiFormDataProvider::fillDataItems(). + \a widget is equal to 'this'. + Reimplemented after KexiFormDataItemInterface. */ + virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue); + + //! Windows uses Ctrl+Tab for moving between tabs, so do not steal this shortcut + virtual void keyPressEvent( QKeyEvent *ke ); + + public slots: + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + virtual void setReadOnly( bool readOnly ); + virtual void setText( const QString & text, const QString & context ); + + //! Reimplemented, so "undo" means the same as "cancelEditor" action +//! @todo enable "real" undo internally so user can use ctrl+z while editing + virtual void undo(); + + //! Implemented for KexiDataItemInterface + virtual void moveCursorToEnd(); + + //! Implemented for KexiDataItemInterface + virtual void moveCursorToStart(); + + //! Implemented for KexiDataItemInterface + virtual void selectAll(); + + protected slots: + void slotTextChanged(); + + protected: + virtual void paintEvent ( QPaintEvent * ); + virtual void setValueInternal(const QVariant& add, bool removeOld); + QPopupMenu * createPopupMenu(const QPoint & pos); + + //! Used for extending context menu + KexiDBWidgetContextMenuExtender m_menuExtender; + + //! Used to disable slotTextChanged() + bool m_slotTextChanged_enabled : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbtimeedit.cpp b/kexi/plugins/forms/widgets/kexidbtimeedit.cpp new file mode 100644 index 00000000..82e61b83 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbtimeedit.cpp @@ -0,0 +1,156 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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 "kexidbtimeedit.h" + +#include <qtoolbutton.h> +#include <qlayout.h> +#include <qpainter.h> + +#include <kpopupmenu.h> +#include <kdatepicker.h> +#include <kdatetbl.h> +#include <kexiutils/utils.h> + +KexiDBTimeEdit::KexiDBTimeEdit(const QTime &time, QWidget *parent, const char *name) + : QTimeEdit(time, parent, name), KexiFormDataItemInterface() +{ + m_invalidState = false; + setAutoAdvance(true); + m_cleared = false; + +#ifdef QDateTimeEditor_HACK + m_dte_time = KexiUtils::findFirstChild<QDateTimeEditor>(this, "QDateTimeEditor"); +#else + m_dte_time = 0; +#endif + + connect(this, SIGNAL(valueChanged(const QTime&)), this, SLOT(slotValueChanged(const QTime&))); +} + +KexiDBTimeEdit::~KexiDBTimeEdit() +{ +} + +void KexiDBTimeEdit::setInvalidState( const QString&) +{ + setEnabled(false); + setReadOnly(true); + m_invalidState = true; +//! @todo move this to KexiDataItemInterface::setInvalidStateInternal() ? + if (focusPolicy() & TabFocus) + setFocusPolicy(QWidget::ClickFocus); +} + +void +KexiDBTimeEdit::setEnabled(bool enabled) +{ + // prevent the user from reenabling the widget when it is in invalid state + if(enabled && m_invalidState) + return; + QTimeEdit::setEnabled(enabled); +} + +void KexiDBTimeEdit::setValueInternal(const QVariant &add, bool removeOld) +{ + m_cleared = !m_origValue.isValid(); + + int setNumberOnFocus = -1; + QTime t; + QString addString(add.toString()); + if (removeOld) { + if (!addString.isEmpty() && addString[0].latin1()>='0' && addString[0].latin1() <='9') { + setNumberOnFocus = addString[0].latin1()-'0'; + t = QTime(setNumberOnFocus, 0, 0); + } + } + else + t = m_origValue.toTime(); + + setTime(t); +} + +QVariant +KexiDBTimeEdit::value() +{ + //QDateTime - a hack needed because QVariant(QTime) has broken isNull() + return QVariant(QDateTime( m_cleared ? QDate() : QDate(0,1,2)/*nevermind*/, time())); +} + +bool KexiDBTimeEdit::valueIsNull() +{ + return !time().isValid() || time().isNull(); +} + +bool KexiDBTimeEdit::valueIsEmpty() +{ + return m_cleared; +} + +bool KexiDBTimeEdit::isReadOnly() const +{ + //! @todo: data/time edit API has no readonly flag, + //! so use event filter to avoid changes made by keyboard or mouse when m_readOnly==true + return m_readOnly; //!isEnabled(); +} + +void KexiDBTimeEdit::setReadOnly(bool set) +{ + m_readOnly = set; +} + +QWidget* +KexiDBTimeEdit::widget() +{ + return this; +} + +bool KexiDBTimeEdit::cursorAtStart() +{ +#ifdef QDateTimeEditor_HACK + return m_dte_time && hasFocus() && m_dte_time->focusSection()==0; +#else + return false; +#endif +} + +bool KexiDBTimeEdit::cursorAtEnd() +{ +#ifdef QDateTimeEditor_HACK + return m_dte_time && hasFocus() + && m_dte_time->focusSection()==int(m_dte_time->sectionCount()-1); +#else + return false; +#endif +} + +void KexiDBTimeEdit::clear() +{ + setTime(QTime()); + m_cleared = true; +} + +void +KexiDBTimeEdit::slotValueChanged(const QTime&) +{ + m_cleared = false; +} + +#include "kexidbtimeedit.moc" diff --git a/kexi/plugins/forms/widgets/kexidbtimeedit.h b/kexi/plugins/forms/widgets/kexidbtimeedit.h new file mode 100644 index 00000000..9665b1f9 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbtimeedit.h @@ -0,0 +1,87 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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. +*/ + +#ifndef KexiDBTimeEdit_H +#define KexiDBTimeEdit_H + +#include "kexiformdataiteminterface.h" +#include "kexidbtextwidgetinterface.h" +#include <qdatetimeedit.h> + +class QDateTimeEditor; + +//! @short A db-aware time editor +class KEXIFORMUTILS_EXPORT KexiDBTimeEdit : public QTimeEdit, public KexiFormDataItemInterface +{ + Q_OBJECT + Q_PROPERTY(QString dataSource READ dataSource WRITE setDataSource DESIGNABLE true) + Q_PROPERTY(QCString dataSourceMimeType READ dataSourceMimeType WRITE setDataSourceMimeType DESIGNABLE true) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true ) + + public: + KexiDBTimeEdit(const QTime &time, QWidget *parent, const char *name=0); + virtual ~KexiDBTimeEdit(); + + inline QString dataSource() const { return KexiFormDataItemInterface::dataSource(); } + inline QCString dataSourceMimeType() const { return KexiFormDataItemInterface::dataSourceMimeType(); } + virtual QVariant value(); + virtual void setInvalidState( const QString& displayText ); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsNull(); + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsEmpty(); + + /*! \return 'readOnly' flag for this widget. */ + virtual bool isReadOnly() const; + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget(); + + virtual bool cursorAtStart(); + virtual bool cursorAtEnd(); + virtual void clear(); + + virtual void setEnabled(bool enabled); + + public slots: + inline void setDataSource(const QString &ds) { KexiFormDataItemInterface::setDataSource(ds); } + inline void setDataSourceMimeType(const QCString &ds) { KexiFormDataItemInterface::setDataSourceMimeType(ds); } + virtual void setReadOnly(bool set); + + protected slots: + void slotValueChanged(const QTime&); + + protected: + virtual void setValueInternal(const QVariant& add, bool removeOld); + + private: + QDateTimeEditor* m_dte_time; + bool m_invalidState : 1; + bool m_cleared : 1; + bool m_readOnly : 1; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexidbutils.cpp b/kexi/plugins/forms/widgets/kexidbutils.cpp new file mode 100644 index 00000000..0c08d64c --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbutils.cpp @@ -0,0 +1,99 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl> + + This library 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 library 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 library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexidbutils.h" + +#include <kpopupmenu.h> +#include <kiconloader.h> + +#include <kexidb/queryschema.h> +#include <kexidb/utils.h> +#include <formeditor/widgetlibrary.h> +#include <kexiutils/utils.h> +#include "../kexiformpart.h" +#include <widget/utils/kexicontextmenuutils.h> + + +QColor lighterGrayBackgroundColor(const QPalette& palette) +{ + return KexiUtils::blendedColors(palette.active().background(), palette.active().base(), 1, 2); +} + +//------- + +KexiDBWidgetContextMenuExtender::KexiDBWidgetContextMenuExtender( QObject* parent, KexiDataItemInterface* iface ) + : QObject(parent) + , m_iface(iface) + , m_contextMenuHasTitle(false) +{ +} + +KexiDBWidgetContextMenuExtender::~KexiDBWidgetContextMenuExtender() +{ +} + +void KexiDBWidgetContextMenuExtender::createTitle(QPopupMenu *menu) +{ + if (!menu) + return; + m_contextMenu = menu; + KPopupTitle *titleItem = new KPopupTitle(); + const int id = m_contextMenu->insertItem(titleItem, -1, 0); + m_contextMenu->setItemEnabled(id, false); + QString icon; + if (dynamic_cast<QWidget*>(m_iface)) + icon = KexiFormPart::library()->iconName(dynamic_cast<QWidget*>(m_iface)->className()); + + m_contextMenuHasTitle = m_iface->columnInfo() ? + KexiContextMenuUtils::updateTitle(m_contextMenu, + m_iface->columnInfo()->captionOrAliasOrName(), + KexiDB::simplifiedTypeName(*m_iface->columnInfo()->field), icon) + : false; + + if (!m_contextMenuHasTitle) + m_contextMenu->removeItem(id); + updatePopupMenuActions(); +} + +void KexiDBWidgetContextMenuExtender::updatePopupMenuActions() +{ + if (m_contextMenu) { + enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll }; //from qlineedit.h + const bool readOnly = m_iface->isReadOnly(); + const int id = m_contextMenu->idAt(m_contextMenuHasTitle ? 1 : 0); + +//! @todo maybe redo will be enabled one day? + m_contextMenu->removeItem(id-(int)IdRedo); + + // update cut/copy/paste + m_contextMenu->setItemEnabled(id-(int)IdCut, !readOnly); + m_contextMenu->setItemEnabled(id-(int)IdPaste, !readOnly); + m_contextMenu->setItemEnabled(id-(int)IdClear, !readOnly); + } +} + +//------------------ + +KexiSubwidgetInterface::KexiSubwidgetInterface() +{ +} + +KexiSubwidgetInterface::~KexiSubwidgetInterface() +{ +} diff --git a/kexi/plugins/forms/widgets/kexidbutils.h b/kexi/plugins/forms/widgets/kexidbutils.h new file mode 100644 index 00000000..386f1ee5 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexidbutils.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Jaroslaw Staniek <js@iidea.pl> + + This library 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 library 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 library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KDBWIDGETS_UTILS_H +#define KDBWIDGETS_UTILS_H + +#include <qpopupmenu.h> +#include <kexidataiteminterface.h> + +QColor lighterGrayBackgroundColor(const QPalette& palette); + +//! @short Used for extending editor widgets' context menu. +/*! @internal This is performed by adding a title and disabling editing + actions when "read only" flag is true. */ +class KexiDBWidgetContextMenuExtender : public QObject +{ + public: + KexiDBWidgetContextMenuExtender( QObject* parent, KexiDataItemInterface* iface ); + ~KexiDBWidgetContextMenuExtender(); + + //! Creates title for context menu \a menu + void createTitle(QPopupMenu *menu); + + //! Enables or disables context menu actions that can modify the value. + //! The menu has to be previously provided by createTitle(). + void updatePopupMenuActions(); + + /*! Updates title for context menu based on data item \a iface caption or name + Used in createTitle(QPopupMenu *menu) and KexiDBImageBox. + \return true is the title has been added. */ + static bool updateContextMenuTitleForDataItem(QPopupMenu *menu, KexiDataItemInterface* iface, + const QString& icon = QString::null); + + protected: + KexiDataItemInterface* m_iface; + QGuardedPtr<QPopupMenu> m_contextMenu; + bool m_contextMenuHasTitle; //!< true if KPopupTitle has been added to the context menu. +}; + +class KexiDBAutoField; + +//! An interface allowing to define custom behaviour for subwidget of the KexiDBAutoField +class KexiSubwidgetInterface +{ + public: + KexiSubwidgetInterface(); + virtual ~KexiSubwidgetInterface(); + + virtual bool appendStretchRequired(KexiDBAutoField* autoField) const + { Q_UNUSED(autoField); return false; } + virtual bool subwidgetStretchRequired(KexiDBAutoField* autoField) const + { Q_UNUSED(autoField); return false; } +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexiframe.cpp b/kexi/plugins/forms/widgets/kexiframe.cpp new file mode 100644 index 00000000..b49386da --- /dev/null +++ b/kexi/plugins/forms/widgets/kexiframe.cpp @@ -0,0 +1,77 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2007 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 "kexiframe.h" + +#include <qpainter.h> +#include <qdrawutil.h> +#include <kexiutils/utils.h> + +//! @internal +class KexiFrame::Private +{ + public: + Private() + { + } + ~Private() + { + } + QColor frameColor; +#if 0 +//todo + KexiFrame::Shape frameShape; + KexiFrame::Shadow frameShadow; +#endif +}; + +//========================================================= + +KexiFrame::KexiFrame( QWidget * parent, const char * name, WFlags f ) + : QFrame(parent, name, f) + , d( new Private() ) +{ + //defaults + d->frameColor = palette().active().foreground(); +//! @todo obtain these defaults from current template's style... + setLineWidth(2); + setFrameStyle(QFrame::StyledPanel|QFrame::Raised); +} + +KexiFrame::~KexiFrame() +{ + delete d; +} + +void KexiFrame::dragMoveEvent( QDragMoveEvent *e ) +{ + QFrame::dragMoveEvent(e); + emit handleDragMoveEvent(e); +} + +void KexiFrame::dropEvent( QDropEvent *e ) +{ + QFrame::dropEvent(e); + emit handleDropEvent(e); +} + +#define ClassName KexiFrame +#define SuperClassName QFrame +#include "kexiframeutils_p.cpp" +#include "kexiframe.moc" diff --git a/kexi/plugins/forms/widgets/kexiframe.h b/kexi/plugins/forms/widgets/kexiframe.h new file mode 100644 index 00000000..8d60d597 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexiframe.h @@ -0,0 +1,84 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2007 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. +*/ + +#ifndef KexiFrame_H +#define KexiFrame_H + +#include <qframe.h> + +//! @short Frame widget for Kexi forms +class KEXIFORMUTILS_EXPORT KexiFrame : public QFrame +{ + Q_OBJECT +//todo Q_ENUMS( Shape Shadow ) + Q_PROPERTY( QColor frameColor READ frameColor WRITE setFrameColor DESIGNABLE true ) +//todo Q_OVERRIDE( Shape frameShape READ frameShape WRITE setFrameShape ) +//todo Q_OVERRIDE( Shadow frameShadow READ frameShadow WRITE setFrameShadow ) + + public: + KexiFrame( QWidget * parent, const char * name = 0, WFlags f = 0 ); + virtual ~KexiFrame(); + + virtual const QColor& frameColor() const; + +#if 0 +//! @todo more options + enum Shadow { + NoShadow = QFrame::Plain, + Raised = QFrame::Raised, + Sunken = QFrame::Sunken + }; +//! @todo more options + enum Shape { NoFrame = QFrame::NoFrame, //!< no frame + Box = QFrame::Box, //!< rectangular box + Panel = QFrame::Panel, //!< rectangular panel + StyledPanel = QFrame::StyledPanel, //!< rectangular panel depending on the GUI style + GroupBoxPanel = QFrame::GroupBoxPanel //!< rectangular group-box-like panel depending on the GUI style + }; + Shape frameShape() const; + void setFrameShape( KexiFrame::Shape shape ); + Shadow frameShadow() const; + void setFrameShadow( KexiFrame::Shadow shadow ); +#endif + + //! Used to emit handleDragMoveEvent() signal needed to control dragging over the container's surface + virtual void dragMoveEvent( QDragMoveEvent *e ); + + //! Used to emit handleDropEvent() signal needed to control dropping on the container's surface + virtual void dropEvent( QDropEvent *e ); + + public slots: + virtual void setPalette( const QPalette &pal ); + virtual void setFrameColor(const QColor& color); + + signals: + //! Needed to control dragging over the container's surface + void handleDragMoveEvent(QDragMoveEvent *e); + + //! Needed to control dropping on the container's surface + void handleDropEvent(QDropEvent *e); + + protected: + virtual void drawFrame( QPainter * ); + + class Private; + Private *d; +}; + +#endif diff --git a/kexi/plugins/forms/widgets/kexiframeutils_p.cpp b/kexi/plugins/forms/widgets/kexiframeutils_p.cpp new file mode 100644 index 00000000..11b8650a --- /dev/null +++ b/kexi/plugins/forms/widgets/kexiframeutils_p.cpp @@ -0,0 +1,232 @@ +/* This file is part of the KDE project + Copyright (C) 2005-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. +*/ + +/* This file is included by KexiDBLabel and KexiFrame */ + +//! @todo add more frame types +void ClassName::drawFrame( QPainter *p ) +{ + if (frameShape() == QFrame::Box) { + if ( frameShadow() == Plain ) + qDrawPlainRect( p, frameRect(), d->frameColor, lineWidth() ); + else + qDrawShadeRect( p, frameRect(), colorGroup(), frameShadow() == QFrame::Sunken, + lineWidth(), midLineWidth() ); + } + else { + SuperClassName::drawFrame(p); + } +} + +void ClassName::setPalette( const QPalette &pal ) +{ + QPalette pal2(pal); + QColorGroup cg( pal2.active() ); + cg.setColor(QColorGroup::Light, KexiUtils::bleachedColor( d->frameColor, 150 )); + cg.setColor(QColorGroup::Mid, d->frameColor); + cg.setColor(QColorGroup::Dark, d->frameColor.dark(150)); + pal2.setActive(cg); + QColorGroup cg2( pal2.inactive() ); + cg2.setColor(QColorGroup::Light, cg.light() ); + cg2.setColor(QColorGroup::Mid, cg.mid()); + cg2.setColor(QColorGroup::Dark, cg.dark()); + pal2.setInactive(cg2); + SuperClassName::setPalette(pal2); +} + +const QColor& ClassName::frameColor() const +{ + return d->frameColor; +} + +void ClassName::setFrameColor(const QColor& color) +{ + d->frameColor = color; + //update light and dark colors + setPalette( palette() ); +} + +#if 0 +//todo +ClassName::Shape ClassName::frameShape() const +{ + return d->frameShape; +} + +void ClassName::setFrameShape( ClassName::Shape shape ) +{ + d->frameShape = shape; + update(); +} + +ClassName::Shadow ClassName::frameShadow() const +{ + return d->frameShadow; +} + +void ClassName::setFrameShadow( ClassName::Shadow shadow ) +{ + d->frameShadow = shadow; + update(); +} +#endif + +#if 0 +void QFrame::drawFrame( QPainter *p ) +{ + QPoint p1, p2; + QRect r = frameRect(); + int type = fstyle & MShape; + int cstyle = fstyle & MShadow; +#ifdef QT_NO_DRAWUTIL + p->setPen( black ); // #### + p->drawRect( r ); //### a bit too simple +#else + const QColorGroup & g = colorGroup(); + +#ifndef QT_NO_STYLE + QStyleOption opt(lineWidth(),midLineWidth()); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (cstyle == Sunken) + flags |= QStyle::Style_Sunken; + else if (cstyle == Raised) + flags |= QStyle::Style_Raised; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (hasMouse()) + flags |= QStyle::Style_MouseOver; +#endif // QT_NO_STYLE + + switch ( type ) { + + case Box: + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + qDrawShadeRect( p, r, g, cstyle == Sunken, lwidth, + midLineWidth() ); + break; + + case LineEditPanel: + style().drawPrimitive( QStyle::PE_PanelLineEdit, p, r, g, flags, opt ); + break; + + case GroupBoxPanel: + style().drawPrimitive( QStyle::PE_PanelGroupBox, p, r, g, flags, opt ); + break; + + case TabWidgetPanel: + style().drawPrimitive( QStyle::PE_PanelTabWidget, p, r, g, flags, opt ); + break; + + case MenuBarPanel: +#ifndef QT_NO_STYLE + style().drawPrimitive(QStyle::PE_PanelMenuBar, p, r, g, flags, opt); + break; +#endif // fall through to Panel if QT_NO_STYLE + + case ToolBarPanel: +#ifndef QT_NO_STYLE + style().drawPrimitive( QStyle::PE_PanelDockWindow, p, rect(), g, flags, opt); + break; +#endif // fall through to Panel if QT_NO_STYLE + + case StyledPanel: +#ifndef QT_NO_STYLE + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + style().drawPrimitive(QStyle::PE_Panel, p, r, g, flags, opt); + break; +#endif // fall through to Panel if QT_NO_STYLE + + case PopupPanel: +#ifndef QT_NO_STYLE + { + int vextra = style().pixelMetric(QStyle::PM_PopupMenuFrameVerticalExtra, this), + hextra = style().pixelMetric(QStyle::PM_PopupMenuFrameHorizontalExtra, this); + if(vextra > 0 || hextra > 0) { + QRect fr = frameRect(); + int fw = frameWidth(); + if(vextra > 0) { + style().drawControl(QStyle::CE_PopupMenuVerticalExtra, p, this, + QRect(fr.x() + fw, fr.y() + fw, fr.width() - (fw*2), vextra), + g, flags, opt); + style().drawControl(QStyle::CE_PopupMenuVerticalExtra, p, this, + QRect(fr.x() + fw, fr.bottom() - fw - vextra, fr.width() - (fw*2), vextra), + g, flags, opt); + } + if(hextra > 0) { + style().drawControl(QStyle::CE_PopupMenuHorizontalExtra, p, this, + QRect(fr.x() + fw, fr.y() + fw + vextra, hextra, fr.height() - (fw*2) - vextra), + g, flags, opt); + style().drawControl(QStyle::CE_PopupMenuHorizontalExtra, p, this, + QRect(fr.right() - fw - hextra, fr.y() + fw + vextra, hextra, fr.height() - (fw*2) - vextra), + g, flags, opt); + } + } + + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + style().drawPrimitive(QStyle::PE_PanelPopup, p, r, g, flags, opt); + break; + } +#endif // fall through to Panel if QT_NO_STYLE + + case Panel: + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + qDrawShadePanel( p, r, g, cstyle == Sunken, lwidth ); + break; + + case WinPanel: + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), wpwidth ); + else + qDrawWinPanel( p, r, g, cstyle == Sunken ); + break; + case HLine: + case VLine: + if ( type == HLine ) { + p1 = QPoint( r.x(), r.height()/2 ); + p2 = QPoint( r.x()+r.width(), p1.y() ); + } + else { + p1 = QPoint( r.x()+r.width()/2, 0 ); + p2 = QPoint( p1.x(), r.height() ); + } + if ( cstyle == Plain ) { + QPen oldPen = p->pen(); + p->setPen( QPen(g.foreground(),lwidth) ); + p->drawLine( p1, p2 ); + p->setPen( oldPen ); + } + else + qDrawShadeLine( p, p1, p2, g, cstyle == Sunken, + lwidth, midLineWidth() ); + break; + } +#endif // QT_NO_DRAWUTIL + +#endif diff --git a/kexi/plugins/forms/widgets/kexipushbutton.cpp b/kexi/plugins/forms/widgets/kexipushbutton.cpp new file mode 100644 index 00000000..acfda0a4 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexipushbutton.cpp @@ -0,0 +1,32 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2005 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 "kexipushbutton.h" + +KexiPushButton::KexiPushButton( const QString & text, QWidget * parent, const char * name ) +: KPushButton(text, parent, name) +{ +} + +KexiPushButton::~KexiPushButton() +{ +} + +#include "kexipushbutton.moc" diff --git a/kexi/plugins/forms/widgets/kexipushbutton.h b/kexi/plugins/forms/widgets/kexipushbutton.h new file mode 100644 index 00000000..12c01631 --- /dev/null +++ b/kexi/plugins/forms/widgets/kexipushbutton.h @@ -0,0 +1,55 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-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. +*/ + +#ifndef KexiPushButton_H +#define KexiPushButton_H + +#include <kpushbutton.h> +#include "../kexiformeventhandler.h" + +//! @short Push Button widget for Kexi forms +class KEXIFORMUTILS_EXPORT KexiPushButton : public KPushButton +{ + Q_OBJECT + Q_PROPERTY(QString onClickAction READ onClickAction WRITE setOnClickAction DESIGNABLE true) + Q_PROPERTY(QString onClickActionOption READ onClickActionOption WRITE setOnClickActionOption DESIGNABLE true) + + public: + KexiPushButton( const QString & text, QWidget * parent, const char * name = 0 ); + ~KexiPushButton(); + + public slots: + //! action string for "on click" event + //! @see KexiFormPart::slotAssignAction() + //! @see KexiFormEventAction::ActionData + QString onClickAction() const { return m_onClickActionData.string; } + void setOnClickAction(const QString& actionString) { m_onClickActionData.string = actionString; } + + //! action option allowing to select whether the object should be opened in data view mode or printed, etc. + //! @see KexiFormPart::slotAssignAction() + //! @see KexiFormEventAction::ActionData + QString onClickActionOption() const { return m_onClickActionData.option; } + void setOnClickActionOption(const QString& option) { m_onClickActionData.option = option; } + + protected: + KexiFormEventAction::ActionData m_onClickActionData; +}; + +#endif |