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 | |
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')
67 files changed, 14995 insertions, 0 deletions
diff --git a/kexi/plugins/forms/Makefile.am b/kexi/plugins/forms/Makefile.am new file mode 100644 index 00000000..e01b4f6c --- /dev/null +++ b/kexi/plugins/forms/Makefile.am @@ -0,0 +1,56 @@ +include $(top_srcdir)/kexi/Makefile.global + +kde_module_LTLIBRARIES = kexihandler_form.la kformdesigner_kexidbwidgets.la + +kexihandler_form_la_SOURCES = kexiforms.cpp + +kexihandler_form_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined +kexihandler_form_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la \ + $(top_builddir)/kexi/widget/utils/libkexiguiutils.la \ + $(top_builddir)/kexi/widget/tableview/libkexidatatable.la \ + $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \ + $(top_builddir)/kexi/formeditor/libkformdesigner.la \ + $(top_builddir)/lib/koproperty/libkoproperty.la \ + ./libkexiformutils.la + +kformdesigner_kexidbwidgets_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined +kformdesigner_kexidbwidgets_la_SOURCES = kexidbfactory.cpp +kformdesigner_kexidbwidgets_la_LIBADD = $(top_builddir)/kexi/formeditor/libkformdesigner.la \ + $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \ + ./libkexiformutils.la + +lib_LTLIBRARIES = libkexiformutils.la +libkexiformutils_la_SOURCES = kexiformdataiteminterface.cpp kexidataawarewidgetinfo.cpp \ + kexidataprovider.cpp kexiformscrollview.cpp kexiformeventhandler.cpp \ + kexidbtextwidgetinterface.cpp kexiactionselectiondialog.cpp kexiformmanager.cpp \ + kexidatasourcepage.cpp kexiformpart.cpp kexiformview.cpp +libkexiformutils_la_LDFLAGS = $(all_libraries) $(VER_INFO) -no-undefined +libkexiformutils_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la \ + $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \ + $(top_builddir)/kexi/formeditor/libkformdesigner.la \ + $(top_builddir)/kexi/plugins/forms/widgets/libkexiformutilswidgets.la + +kformdesignerservicesdir=$(kde_servicesdir)/kformdesigner +kformdesignerservices_DATA=kformdesigner_kexidbfactory.desktop + +servicesdir=$(kde_servicesdir)/kexi +services_DATA=kexiformhandler.desktop + +rcdir = $(kde_datadir)/kexi +rc_DATA = kexiformpartui.rc kexiformpartinstui.rc + +SUBDIRS = widgets . + +INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \ + -I$(top_srcdir)/kexi/widget/utils \ + -I$(top_srcdir)/kexi/widget \ + -I$(top_srcdir)/kexi/formeditor \ + -I$(top_srcdir)/lib -I$(top_srcdir)/lib/koproperty -I$(top_srcdir)/lib/kofficecore \ + -I$(top_srcdir)/kexi/widget/tableview/private \ + -I$(top_srcdir)/kexi/widget/tableview $(all_includes) + +METASOURCES = AUTO + +include ../Makefile.common +noinst_HEADERS = kexidataprovider.h kexidbfactory.h \ + kexiformpart.h kexiformscrollview.h kexiformview.h
\ No newline at end of file diff --git a/kexi/plugins/forms/kexiactionselectiondialog.cpp b/kexi/plugins/forms/kexiactionselectiondialog.cpp new file mode 100644 index 00000000..26b4a9a6 --- /dev/null +++ b/kexi/plugins/forms/kexiactionselectiondialog.cpp @@ -0,0 +1,724 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 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 "kexiactionselectiondialog.h" +#include "kexiactionselectiondialog_p.h" + +#include <keximainwindow.h> +#include <kexipartitem.h> +#include <kexiproject.h> +#include <kexipartinfo.h> +#include <kexipart.h> +#include <kexiactioncategories.h> + +#include <klistview.h> +#include <kaction.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <kstdguiitem.h> +#include <kpushbutton.h> + +#include <qbitmap.h> +#include <qlabel.h> +#include <qheader.h> +#include <qvbox.h> +#include <qtooltip.h> +#include <qwidgetstack.h> + +#include <widget/utils/klistviewitemtemplate.h> +#include <widget/kexibrowser.h> +#include <widget/kexibrowseritem.h> +#include <kexiutils/utils.h> + +typedef KListViewItemTemplate<QString> ActionSelectorDialogListItemBase; + +class ActionSelectorDialogListItem : public ActionSelectorDialogListItemBase +{ +public: + ActionSelectorDialogListItem(const QString& data, QListView *parent, QString label1) + : ActionSelectorDialogListItemBase(data, parent, label1) + , fifoSorting(true) + { + m_sortKey.sprintf("%2.2d", parent->childCount()); + } + + ActionSelectorDialogListItem(const QString& data, QListViewItem *parent, QString label1) + : ActionSelectorDialogListItemBase(data, parent, label1) + , fifoSorting(true) + { + m_sortKey.sprintf("%2.2d", parent->childCount()); + } + + virtual QString key( int column, bool ascending ) const + { + return fifoSorting ? m_sortKey : ActionSelectorDialogListItemBase::key(column, ascending); + } + + bool fifoSorting : 1; + +protected: + QString m_sortKey; +}; + +//--------------------------------------- + +ActionsListViewBase::ActionsListViewBase(QWidget* parent) + : KListView(parent) +{ + setResizeMode(QListView::AllColumns); + addColumn(""); + header()->hide(); + setColumnWidthMode(0, QListView::Maximum); + setAllColumnsShowFocus(true); + setTooltipColumn(0); +} + +ActionsListViewBase::~ActionsListViewBase() +{ +} + +QListViewItem *ActionsListViewBase::itemForAction(const QString& actionName) +{ + for (QListViewItemIterator it(this); it.current(); ++it) { + ActionSelectorDialogListItem* item = dynamic_cast<ActionSelectorDialogListItem*>(it.current()); + if (item && item->data == actionName) + return item; + } + return 0; +} + +void ActionsListViewBase::selectAction(const QString& actionName) +{ + QListViewItem *item = itemForAction(actionName); + if (item) { + setSelected(item, true); + ensureItemVisible(firstChild()); + ensureItemVisible(selectedItem()); + } +} + +//--------------------------------------- + +KActionsListViewBase::KActionsListViewBase(QWidget* parent, KexiMainWindow* mainWin) + : ActionsListViewBase(parent) + , m_mainWin(mainWin) +{ +} + +KActionsListViewBase::~KActionsListViewBase() {} + +void KActionsListViewBase::init() +{ + setSorting(0); + const QPixmap noIcon( KexiUtils::emptyIcon(KIcon::Small) ); + KActionPtrList sharedActions( m_mainWin->allActions() ); + const Kexi::ActionCategories *acat = Kexi::actionCategories(); + foreach (KActionPtrList::ConstIterator, it, sharedActions) { +// kdDebug() << (*it)->name() << " " << (*it)->text() << endl; + //! @todo group actions + //! @todo: store KAction* here? + const int actionCategories = acat->actionCategories((*it)->name()); + if (actionCategories==-1) { + kexipluginswarn << "KActionsListViewBase(): no category declared for action \"" + << (*it)->name() << "\"! Fix this!" << endl; + continue; + } + if (!isActionVisible((*it)->name(), actionCategories)) + continue; + ActionSelectorDialogListItem *pitem = new ActionSelectorDialogListItem((*it)->name(), + this, (*it)->toolTip().isEmpty() ? (*it)->text().replace("&", "") : (*it)->toolTip() ); + pitem->fifoSorting = false; //alpha sort + pitem->setPixmap( 0, (*it)->iconSet( KIcon::Small, 16 ).pixmap( QIconSet::Small, QIconSet::Active ) ); + if (!pitem->pixmap(0) || pitem->pixmap(0)->isNull()) + pitem->setPixmap( 0, noIcon ); + } +} + +//--------------------------------------- + +//! @internal Used to display KActions (in column 2) +class KActionsListView : public KActionsListViewBase +{ +public: + KActionsListView(QWidget* parent, KexiMainWindow* mainWin) + : KActionsListViewBase(parent, mainWin) + { + } + virtual ~KActionsListView() {} + + virtual bool isActionVisible(const char* actionName, int actionCategories) const { + Q_UNUSED(actionName); + return actionCategories & Kexi::GlobalActionCategory; + } +}; + +//! @internal Used to display KActions (in column 2) +class CurrentFormActionsListView : public KActionsListViewBase +{ +public: + CurrentFormActionsListView(QWidget* parent, KexiMainWindow* mainWin) + : KActionsListViewBase(parent, mainWin) + { + } + virtual ~CurrentFormActionsListView() {} + + virtual bool isActionVisible(const char* actionName, int actionCategories) const { + return actionCategories & Kexi::WindowActionCategory + && Kexi::actionCategories()->actionSupportsObjectType(actionName, KexiPart::FormObjectType); + } +}; + +//! @internal a list view displaying action categories user can select from (column 1) +class ActionCategoriesListView : public ActionsListViewBase +{ +public: + ActionCategoriesListView(QWidget* parent) //, KexiProject& project) + : ActionsListViewBase(parent) + { + QListViewItem *item = new ActionSelectorDialogListItem("noaction", this, i18n("No action") ); + const QPixmap noIcon( KexiUtils::emptyIcon(KIcon::Small) ); + item->setPixmap(0, noIcon); + item = new ActionSelectorDialogListItem("kaction", this, i18n("Application actions") ); + item->setPixmap(0, SmallIcon("form_action")); + + KexiPart::PartInfoList *pl = Kexi::partManager().partInfoList(); + for (KexiPart::Info *info = pl->first(); info; info = pl->next()) { + KexiPart::Part *part = Kexi::partManager().part(info); + if (!info->isVisibleInNavigator() || !part) + continue; + item = new KexiBrowserItem(this, info); + item->setText(0, part->instanceCaption()); + } + QListViewItem *formItem = itemForAction("form"); + if (formItem) { + item = new ActionSelectorDialogListItem("currentForm", formItem, + i18n("Current form's actions", "Current")); + } + adjustColumn(0); + setMinimumWidth( columnWidth(0) + 6 ); + } + + ~ActionCategoriesListView() + { + } + + //! \return item for action \a actionName, reimplemented to support KexiBrowserItem items + virtual QListViewItem *itemForAction(const QString& actionName) + { + for (QListViewItemIterator it(this); it.current(); ++it) { + //simple case + ActionSelectorDialogListItem* item = dynamic_cast<ActionSelectorDialogListItem*>(it.current()); + if (item) { + if (item->data == actionName) + return it.current(); + continue; + } + KexiBrowserItem* bitem = dynamic_cast<KexiBrowserItem*>(it.current()); + if (bitem) { + if (bitem->info()->objectName() == actionName) + return it.current(); + } + } + return 0; + } +}; + +//! @internal Used to display list of actions available to executing (column 3) +class ActionToExecuteListView : public ActionsListViewBase +{ + public: + ActionToExecuteListView(QWidget* parent) + : ActionsListViewBase(parent) + { + } + + ~ActionToExecuteListView() + { + } + + //! Updates actions + void showActionsForMimeType(const QString& mimeType) { + if (m_currentMimeType == mimeType) + return; + m_currentMimeType = mimeType; + clear(); + KexiPart::Part *part = Kexi::partManager().partForMimeType( m_currentMimeType ); + if (!part) + return; + int supportedViewModes = part->supportedViewModes(); + ActionSelectorDialogListItem *item; + const QPixmap noIcon( KexiUtils::emptyIcon(KIcon::Small) ); + if (supportedViewModes & Kexi::DataViewMode) { + item = new ActionSelectorDialogListItem("open", this, i18n("Open in Data View")); + item->setPixmap(0, SmallIcon("fileopen")); + } + if (part->info()->isExecuteSupported()) { + item = new ActionSelectorDialogListItem("execute", this, i18n("Execute")); + item->setPixmap(0, SmallIcon("player_play")); + } + if (part->info()->isPrintingSupported()) { + ActionSelectorDialogListItem *printItem = new ActionSelectorDialogListItem( + "print", this, i18n("Print")); + printItem->setPixmap(0, SmallIcon("fileprint")); + KAction *a = KStdAction::printPreview(0, 0, 0); + item = new ActionSelectorDialogListItem("printPreview", printItem, + a->text().replace("&", "").replace("...", "")); + item->setPixmap(0, SmallIcon(a->icon())); + delete a; + item = new ActionSelectorDialogListItem("pageSetup", printItem, i18n("Show Page Setup")); + item->setPixmap(0, noIcon); + setOpen(printItem, true); + printItem->setExpandable(false); + } + if (part->info()->isDataExportSupported()) { + ActionSelectorDialogListItem *exportItem = new ActionSelectorDialogListItem( + "exportToCSV", this, + i18n("Note: use multiple rows if needed", "Export to File\nAs Data Table")); + exportItem->setMultiLinesEnabled(true); + exportItem->setPixmap(0, SmallIcon("table")); + item = new ActionSelectorDialogListItem("copyToClipboardAsCSV", + exportItem, + i18n("Note: use multiple rows if needed", "Copy to Clipboard\nAs Data Table")); + item->setPixmap(0, SmallIcon("table")); + item->setMultiLinesEnabled(true); + setOpen(exportItem, true); + exportItem->setExpandable(false); + } + item = new ActionSelectorDialogListItem("new", this, i18n("Create New Object")); + item->setPixmap(0, SmallIcon("filenew")); + if (supportedViewModes & Kexi::DesignViewMode) { + item = new ActionSelectorDialogListItem("design", this, i18n("Open in Design View")); + item->setPixmap(0, SmallIcon("edit")); + } + if (supportedViewModes & Kexi::TextViewMode) { + item = new ActionSelectorDialogListItem("editText", this, i18n("Open in Text View")); + item->setPixmap(0, noIcon); + } + item = new ActionSelectorDialogListItem("close", this, i18n("Close View")); + item->setPixmap(0, SmallIcon("fileclose")); + updateWidth(); + } + + void updateWidth() + { + adjustColumn(0); + setMinimumWidth( columnWidth(0) ); + } + + QString m_currentMimeType; +}; + +//------------------------------------- + +//! @internal +class KexiActionSelectionDialog::KexiActionSelectionDialogPrivate +{ +public: + KexiActionSelectionDialogPrivate() + : kactionPageWidget(0), kactionListView(0), objectsListView(0) + , currentFormActionsPageWidget(0) + , currentFormActionsListView(0) + , secondAnd3rdColumnMainWidget(0) + , hideActionToExecuteListView(false) + { + } + + void raiseWidget(QWidget *w) + { + secondAnd3rdColumnStack->raiseWidget( w ); + selectActionToBeExecutedLbl->setBuddy(w); + } + + void updateSelectActionToBeExecutedMessage(const QString& actionType) + { + QString msg; + if (actionType=="noaction") + msg = QString::null; + // hardcoded, but it's not that bad + else if (actionType=="macro") + msg = i18n("&Select macro to be executed after clicking \"%1\" button:").arg(actionWidgetName); + else if (actionType=="script") + msg = i18n("&Select script to be executed after clicking \"%1\" button:").arg(actionWidgetName); + //default: table/query/form/report... + else + msg = i18n("&Select object to be opened after clicking \"%1\" button:").arg(actionWidgetName); + selectActionToBeExecutedLbl->setText(msg); + } + + // changes 3rd column visibility + void setActionToExecuteSectionVisible(bool visible, bool force = false) + { + if (!force && hideActionToExecuteListView != visible) + return; + hideActionToExecuteListView = !visible; + actionToExecuteListView->hide(); + actionToExecuteLbl->hide(); + actionToExecuteListView->show(); + actionToExecuteLbl->show(); + } + + KexiMainWindow* mainWin; + QString actionWidgetName; + ActionCategoriesListView* actionCategoriesListView; //!< for column #1 + QWidget *kactionPageWidget; + KActionsListView* kactionListView; //!< for column #2 + KexiBrowser* objectsListView; //!< for column #2 + QWidget *currentFormActionsPageWidget; //!< for column #2 + CurrentFormActionsListView* currentFormActionsListView; //!< for column #2 + QWidget *emptyWidget; + QLabel* selectActionToBeExecutedLbl; + ActionToExecuteListView* actionToExecuteListView; + QLabel *actionToExecuteLbl; + QWidget *secondAnd3rdColumnMainWidget; + QGridLayout *glyr; + QGridLayout *secondAnd3rdColumnGrLyr; + QWidgetStack *secondAnd3rdColumnStack, *secondColumnStack; + bool hideActionToExecuteListView; +}; + +//------------------------------------- + +KexiActionSelectionDialog::KexiActionSelectionDialog(KexiMainWindow* mainWin, QWidget *parent, + const KexiFormEventAction::ActionData& action, const QCString& actionWidgetName) + : KDialogBase(parent, "actionSelectorDialog", true, i18n("Assigning Action to Command Button"), + KDialogBase::Ok | KDialogBase::Cancel ) + , d( new KexiActionSelectionDialogPrivate() ) +{ + d->mainWin = mainWin; + d->actionWidgetName = actionWidgetName; + setButtonOK( KGuiItem(i18n("Assign action", "&Assign"), "button_ok", i18n("Assign action")) ); + + QWidget *mainWidget = new QWidget( this ); + mainWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setMainWidget(mainWidget); + +/* lbl 1 + +------------+ +-------------------------------+ + | | | [a] | + | 1st column | | +----------- + +------------+ | + | | | | 2nd column | | 3rd column | | + | | | + + + + | + | | | +------------+ +------------+ | + +------------+ +-------------------------------+ + \______________________________________________/ + glyr + [a]- QWidgetStack *secondAnd3rdColumnStack, + - for displaying KActions, the stack contains d->kactionPageWidget QWidget + - for displaying objects, the stack contains secondAnd3rdColumnMainWidget QWidget and QGridLayout *secondAnd3rdColumnGrLyr + - kactionPageWidget contains only a QVBoxLayout and label+kactionListView +*/ + d->glyr = new QGridLayout(mainWidget, 2, 2, KDialog::marginHint(), KDialog::spacingHint()); + d->glyr->setRowStretch(1, 1); + + // 1st column: action types + d->actionCategoriesListView = new ActionCategoriesListView(mainWidget); + d->actionCategoriesListView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + d->glyr->addWidget(d->actionCategoriesListView, 1, 0); + connect( d->actionCategoriesListView, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(slotActionCategorySelected(QListViewItem*))); + + QLabel *lbl = new QLabel(d->actionCategoriesListView, i18n("Action category:"), mainWidget); + lbl->setMinimumHeight(lbl->fontMetrics().height()*2); + lbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + lbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak); + d->glyr->addWidget(lbl, 0, 0, Qt::AlignTop|Qt::AlignLeft); + + // widget stack for 2nd and 3rd column + d->secondAnd3rdColumnStack = new QWidgetStack(mainWidget); + d->secondAnd3rdColumnStack->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + d->glyr->addMultiCellWidget(d->secondAnd3rdColumnStack, 0, 1, 1, 1);//, Qt::AlignTop|Qt::AlignLeft); + + d->secondAnd3rdColumnMainWidget = new QWidget(d->secondAnd3rdColumnStack); + d->secondAnd3rdColumnMainWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + d->secondAnd3rdColumnGrLyr = new QGridLayout(d->secondAnd3rdColumnMainWidget, 2, 2, 0, KDialog::spacingHint()); + d->secondAnd3rdColumnGrLyr->setRowStretch(1, 2); + d->secondAnd3rdColumnStack->addWidget(d->secondAnd3rdColumnMainWidget); + + // 2nd column: list of actions/objects + d->objectsListView = new KexiBrowser(d->secondAnd3rdColumnMainWidget, d->mainWin, 0/*features*/); + d->secondAnd3rdColumnGrLyr->addWidget(d->objectsListView, 1, 0); + connect(d->objectsListView, SIGNAL(selectionChanged(KexiPart::Item*)), + this, SLOT(slotItemForOpeningOrExecutingSelected(KexiPart::Item*))); + + d->selectActionToBeExecutedLbl = new QLabel(d->secondAnd3rdColumnMainWidget); + d->selectActionToBeExecutedLbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + d->selectActionToBeExecutedLbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak); + d->selectActionToBeExecutedLbl->setMinimumHeight(d->selectActionToBeExecutedLbl->fontMetrics().height()*2); + d->secondAnd3rdColumnGrLyr->addWidget(d->selectActionToBeExecutedLbl, 0, 0, Qt::AlignTop|Qt::AlignLeft); + + d->emptyWidget = new QWidget(d->secondAnd3rdColumnStack); + d->secondAnd3rdColumnStack->addWidget(d->emptyWidget); + + // 3rd column: actions to execute + d->actionToExecuteListView = new ActionToExecuteListView(d->secondAnd3rdColumnMainWidget); + d->actionToExecuteListView->installEventFilter(this); //to be able to disable painting + d->actionToExecuteListView->viewport()->installEventFilter(this); //to be able to disable painting + d->actionToExecuteListView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + connect(d->actionToExecuteListView, SIGNAL(executed(QListViewItem*)), + this, SLOT(slotActionToExecuteItemExecuted(QListViewItem*))); + connect(d->actionToExecuteListView, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(slotActionToExecuteItemSelected(QListViewItem*))); + d->secondAnd3rdColumnGrLyr->addWidget(d->actionToExecuteListView, 1, 1); + + d->actionToExecuteLbl = new QLabel(d->actionToExecuteListView, + i18n("Action to execute:"), d->secondAnd3rdColumnMainWidget); + d->actionToExecuteLbl->installEventFilter(this); //to be able to disable painting + d->actionToExecuteLbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + d->actionToExecuteLbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak); + d->secondAnd3rdColumnGrLyr->addWidget(d->actionToExecuteLbl, 0, 1, Qt::AlignTop|Qt::AlignLeft); + + // temporary show all sections to avoid resizing the dialog in the future + d->actionCategoriesListView->selectAction("table"); + d->setActionToExecuteSectionVisible(true); + adjustSize(); + resize(QMAX(700, width()), QMAX(450, height())); + d->actionToExecuteListView->updateWidth(); + + bool ok; + QString actionType, actionArg; + KexiPart::Info* partInfo = action.decodeString(actionType, actionArg, ok); + if (ok) { + d->actionCategoriesListView->selectAction(actionType); + if (actionType=="kaction") { + d->kactionListView->selectAction(actionArg); + d->kactionListView->setFocus(); + } + else if (actionType=="currentForm") { + d->currentFormActionsListView->selectAction(actionArg); + d->currentFormActionsListView->setFocus(); + } + else if (partInfo + && Kexi::partManager().part(partInfo)) // We use the Part Manager + // to determine whether the Kexi-plugin is installed and whether we like to show + // it in our list of actions. + { + KexiPart::Item *item = d->mainWin->project()->item(partInfo, actionArg); + if (d->objectsListView && item) { + d->objectsListView->selectItem(*item); + QString actionOption( action.option ); + if (actionOption.isEmpty()) + actionOption = "open"; // for backward compatibility + d->actionToExecuteListView->selectAction(actionOption); + d->objectsListView->setFocus(); + } + } + } + else {//invalid assignment or 'noaction' + d->actionCategoriesListView->selectAction("noaction"); + d->actionCategoriesListView->setFocus(); + } +} + +KexiActionSelectionDialog::~KexiActionSelectionDialog() +{ + delete d; +} + +void KexiActionSelectionDialog::slotKActionItemExecuted(QListViewItem*) +{ + accept(); +} + +void KexiActionSelectionDialog::slotKActionItemSelected(QListViewItem*) +{ + d->setActionToExecuteSectionVisible(false); + updateOKButtonStatus(); +} + +void KexiActionSelectionDialog::slotCurrentFormActionItemExecuted(QListViewItem*) +{ + accept(); +} + +void KexiActionSelectionDialog::slotCurrentFormActionItemSelected(QListViewItem*) +{ + d->setActionToExecuteSectionVisible(false); + updateOKButtonStatus(); +} + +void KexiActionSelectionDialog::slotItemForOpeningOrExecutingSelected(KexiPart::Item* item) +{ + d->setActionToExecuteSectionVisible(item); +} + +void KexiActionSelectionDialog::slotActionToExecuteItemExecuted(QListViewItem* item) +{ + if (!item) + return; + ActionSelectorDialogListItemBase *listItem = dynamic_cast<ActionSelectorDialogListItemBase*>(item); + if (listItem && !listItem->data.isEmpty()) + accept(); +} + +void KexiActionSelectionDialog::slotActionToExecuteItemSelected(QListViewItem*) +{ + updateOKButtonStatus(); +} + +void KexiActionSelectionDialog::slotActionCategorySelected(QListViewItem* item) +{ + ActionSelectorDialogListItem *simpleItem = dynamic_cast<ActionSelectorDialogListItem*>(item); + // simple case: part-less item, e.g. kaction: + if (simpleItem) { + d->updateSelectActionToBeExecutedMessage(simpleItem->data); + QString selectActionToBeExecutedMsg( + i18n("&Select action to be executed after clicking \"%1\" button:")); // msg for a label + if (simpleItem->data == "kaction") { + if (!d->kactionPageWidget) { + //create lbl+list view with a vlayout + d->kactionPageWidget = new QWidget(); + d->kactionPageWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + QVBoxLayout *vlyr = new QVBoxLayout(d->kactionPageWidget, 0, KDialog::spacingHint()); + d->kactionListView = new KActionsListView(d->kactionPageWidget, d->mainWin); + d->kactionListView->init(); + QLabel *lbl = new QLabel(d->kactionListView, selectActionToBeExecutedMsg.arg(d->actionWidgetName), + d->kactionPageWidget); + lbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak); + lbl->setMinimumHeight(lbl->fontMetrics().height()*2); + vlyr->addWidget(lbl); + vlyr->addWidget(d->kactionListView); + d->secondAnd3rdColumnStack->addWidget(d->kactionPageWidget); + connect(d->kactionListView, SIGNAL(executed(QListViewItem*)), + this, SLOT(slotKActionItemExecuted(QListViewItem*))); + connect( d->kactionListView, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(slotKActionItemSelected(QListViewItem*))); + } + d->setActionToExecuteSectionVisible(false); + d->raiseWidget(d->kactionPageWidget); + slotKActionItemSelected(d->kactionListView->selectedItem()); //to refresh column #3 + } + else if (simpleItem->data == "currentForm") { + if (!d->currentFormActionsPageWidget) { + //create lbl+list view with a vlayout + d->currentFormActionsPageWidget = new QWidget(); + d->currentFormActionsPageWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + QVBoxLayout *vlyr = new QVBoxLayout(d->currentFormActionsPageWidget, 0, KDialog::spacingHint()); + d->currentFormActionsListView = new CurrentFormActionsListView( + d->currentFormActionsPageWidget, d->mainWin); + d->currentFormActionsListView->init(); + QLabel *lbl = new QLabel(d->currentFormActionsListView, + selectActionToBeExecutedMsg.arg(d->actionWidgetName), d->currentFormActionsPageWidget); + lbl->setAlignment(Qt::AlignTop|Qt::AlignLeft|Qt::WordBreak); + lbl->setMinimumHeight(lbl->fontMetrics().height()*2); + vlyr->addWidget(lbl); + vlyr->addWidget(d->currentFormActionsListView); + d->secondAnd3rdColumnStack->addWidget(d->currentFormActionsPageWidget); + connect(d->currentFormActionsListView, SIGNAL(executed(QListViewItem*)), + this, SLOT(slotCurrentFormActionItemExecuted(QListViewItem*))); + connect( d->currentFormActionsListView, SIGNAL(selectionChanged(QListViewItem*)), + this, SLOT(slotCurrentFormActionItemSelected(QListViewItem*))); + } + d->setActionToExecuteSectionVisible(false); + d->raiseWidget(d->currentFormActionsPageWidget); + slotCurrentFormActionItemSelected(d->currentFormActionsListView->selectedItem()); //to refresh column #3 + } + else if (simpleItem->data == "noaction") { + d->raiseWidget(d->emptyWidget); + d->objectsListView->clearSelection(); + //hide column #3 + d->setActionToExecuteSectionVisible(false); + } + d->actionCategoriesListView->update(); + updateOKButtonStatus(); + return; + } + // other case + KexiBrowserItem* browserItem = dynamic_cast<KexiBrowserItem*>(item); + if (browserItem) { + d->updateSelectActionToBeExecutedMessage(browserItem->info()->objectName()); + if (d->objectsListView->itemsMimeType().latin1()!=browserItem->info()->mimeType()) { + d->objectsListView->setProject(d->mainWin->project(), browserItem->info()->mimeType()); + d->actionToExecuteListView->showActionsForMimeType( browserItem->info()->mimeType() ); + d->setActionToExecuteSectionVisible(false); + } + if (d->secondAnd3rdColumnStack->visibleWidget()!=d->secondAnd3rdColumnMainWidget) { + d->raiseWidget( d->secondAnd3rdColumnMainWidget ); + d->objectsListView->clearSelection(); + d->setActionToExecuteSectionVisible(false, true); + } + else + d->raiseWidget( d->secondAnd3rdColumnMainWidget ); + } + d->actionCategoriesListView->update(); + updateOKButtonStatus(); +} + +KexiMainWindow* KexiActionSelectionDialog::mainWin() const +{ + return d->mainWin; +} + +KexiFormEventAction::ActionData KexiActionSelectionDialog::currentAction() const +{ + KexiFormEventAction::ActionData data; + ActionSelectorDialogListItem *simpleItem = dynamic_cast<ActionSelectorDialogListItem*>( + d->actionCategoriesListView->selectedItem()); + // simple case: part-less item, e.g. kaction: + if (simpleItem) { + if (simpleItem->data == "kaction") { + if (d->kactionListView->selectedItem()) { + data.string = QString("kaction:") + + dynamic_cast<ActionSelectorDialogListItem*>( d->kactionListView->selectedItem() )->data; + return data; + } + } + else if (simpleItem->data == "currentForm") { + if (d->currentFormActionsListView->selectedItem()) { + data.string = QString("currentForm:") + + dynamic_cast<ActionSelectorDialogListItem*>( + d->currentFormActionsListView->selectedItem() )->data; + return data; + } + } + } + KexiBrowserItem* browserItem = dynamic_cast<KexiBrowserItem*>( d->actionCategoriesListView->selectedItem() ); + if (browserItem) { + ActionSelectorDialogListItem *actionToExecute = dynamic_cast<ActionSelectorDialogListItem*>( + d->actionToExecuteListView->selectedItem()); + if (d->objectsListView && actionToExecute && !actionToExecute->data.isEmpty()) { + KexiPart::Item* partItem = d->objectsListView->selectedPartItem(); + KexiPart::Info* partInfo = partItem ? Kexi::partManager().infoForMimeType( partItem->mimeType() ) : 0; + if (partInfo) { + // opening or executing: table:name, query:name, form:name, macro:name, script:name, etc. + data.string = QString("%1:%2").arg(partInfo->objectName()).arg(partItem->name()); + data.option = actionToExecute->data; + return data; + } + } + } + return data; // No Action +} + +void KexiActionSelectionDialog::updateOKButtonStatus() +{ + QPushButton *btn = actionButton(Ok); + ActionSelectorDialogListItem *simpleItem = dynamic_cast<ActionSelectorDialogListItem*>( + d->actionCategoriesListView->selectedItem()); + btn->setEnabled( (simpleItem && simpleItem->data == "noaction") || !currentAction().isEmpty() ); +} + +bool KexiActionSelectionDialog::eventFilter(QObject *o, QEvent *e) +{ + if (d->hideActionToExecuteListView) + return true; + return KDialogBase::eventFilter(o, e); +} + +#include "kexiactionselectiondialog.moc" +#include "kexiactionselectiondialog_p.moc" diff --git a/kexi/plugins/forms/kexiactionselectiondialog.h b/kexi/plugins/forms/kexiactionselectiondialog.h new file mode 100644 index 00000000..6b6a896b --- /dev/null +++ b/kexi/plugins/forms/kexiactionselectiondialog.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 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 KEXIACTIONSELECTIONDIALOG_H +#define KEXIACTIONSELECTIONDIALOG_H + +#include <kdialogbase.h> +#include "kexiformeventhandler.h" + +class KexiMainWindow; +class KListView; +namespace KexiPart { + class Item; +} + +//! @short A dialog for selecting an action to be executed for a form's command button +/*! Available actions are: + - application's global actions like "edit->copy" (KAction-based) + - opening/printing/executing of selected object (table/query/form/script/macrto, etc.) +*/ +class KEXIFORMUTILS_EXPORT KexiActionSelectionDialog : public KDialogBase +{ + Q_OBJECT + public: + KexiActionSelectionDialog(KexiMainWindow* mainWin, QWidget *parent, + const KexiFormEventAction::ActionData& action, const QCString& actionWidgetName); + ~KexiActionSelectionDialog(); + + /*! \return selected action data or empty action if dialog has been rejected + or "No action" has been selected. */ + KexiFormEventAction::ActionData currentAction() const; + + //! \return the \a KexiMainWindow instance. + KexiMainWindow* mainWin() const; + + virtual bool eventFilter(QObject *o, QEvent *e); + + protected slots: + void slotActionCategorySelected(QListViewItem* item); + void slotKActionItemExecuted(QListViewItem*); + void slotKActionItemSelected(QListViewItem*); + void slotActionToExecuteItemExecuted(QListViewItem* item); + void slotActionToExecuteItemSelected(QListViewItem*); + void slotCurrentFormActionItemExecuted(QListViewItem*); + void slotCurrentFormActionItemSelected(QListViewItem*); + void slotItemForOpeningOrExecutingSelected(KexiPart::Item* item); + + protected: + void updateOKButtonStatus(); + + class KexiActionSelectionDialogPrivate; + KexiActionSelectionDialogPrivate* d; +}; + +#endif diff --git a/kexi/plugins/forms/kexiactionselectiondialog_p.h b/kexi/plugins/forms/kexiactionselectiondialog_p.h new file mode 100644 index 00000000..51f5c369 --- /dev/null +++ b/kexi/plugins/forms/kexiactionselectiondialog_p.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 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 KEXIACTIONSELECTIONDIALOG_P_H +#define KEXIACTIONSELECTIONDIALOG_P_H + +#include <klistview.h> + +//! @internal +class ActionsListViewBase : public KListView +{ + public: + ActionsListViewBase(QWidget* parent); + virtual ~ActionsListViewBase(); + + //! \return item for action \a actionName + virtual QListViewItem *itemForAction(const QString& actionName); + void selectAction(const QString& actionName); +}; + +//! @internal Used by KActionsListView and CurrentFormActionsListView (in column 2) +class KActionsListViewBase : public ActionsListViewBase +{ + Q_OBJECT + public: + KActionsListViewBase(QWidget* parent, KexiMainWindow* mainWin); + virtual ~KActionsListViewBase(); + void init(); + virtual bool isActionVisible(const char* actionName, int actionCategories) const = 0; + + protected: + KexiMainWindow* m_mainWin; +}; + +#endif diff --git a/kexi/plugins/forms/kexidataawarewidgetinfo.cpp b/kexi/plugins/forms/kexidataawarewidgetinfo.cpp new file mode 100644 index 00000000..a6033c70 --- /dev/null +++ b/kexi/plugins/forms/kexidataawarewidgetinfo.cpp @@ -0,0 +1,43 @@ +/* This file is part of the KDE project + Copyright (C) 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 "kexidataawarewidgetinfo.h" + +KexiDataAwareWidgetInfo::KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f) + : KFormDesigner::WidgetInfo(f) +{ + init(); +} + +KexiDataAwareWidgetInfo::KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f, + const char* parentFactoryName, const char* inheritedClassName) + : KFormDesigner::WidgetInfo(f, parentFactoryName, inheritedClassName) +{ + init(); +} + +KexiDataAwareWidgetInfo::~KexiDataAwareWidgetInfo() +{ +} + +void KexiDataAwareWidgetInfo::init() +{ + setAutoSyncForProperty( "dataSource", false ); + setAutoSyncForProperty( "dataSourceMimeType", false ); +} diff --git a/kexi/plugins/forms/kexidataawarewidgetinfo.h b/kexi/plugins/forms/kexidataawarewidgetinfo.h new file mode 100644 index 00000000..41e67d85 --- /dev/null +++ b/kexi/plugins/forms/kexidataawarewidgetinfo.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright (C) 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 KEXIDATAAWAREWIDGETINFO_H +#define KEXIDATAAWAREWIDGETINFO_H + +#include <formeditor/widgetfactory.h> + +//! A widget info for data-aware widgets +/*! Used within factories just like KFormDesigner::WidgetInfo, + but also predefines specific behaviour, + e.g. sets autoSync flag to false for "dataSource" property. +*/ +class KEXIFORMUTILS_EXPORT KexiDataAwareWidgetInfo : public KFormDesigner::WidgetInfo +{ + public: + KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f); + + KexiDataAwareWidgetInfo(KFormDesigner::WidgetFactory *f, + const char* parentFactoryName, const char* inheritedClassName = 0); + + virtual ~KexiDataAwareWidgetInfo(); + + protected: + void init(); +}; + +#endif diff --git a/kexi/plugins/forms/kexidataprovider.cpp b/kexi/plugins/forms/kexidataprovider.cpp new file mode 100644 index 00000000..6706f838 --- /dev/null +++ b/kexi/plugins/forms/kexidataprovider.cpp @@ -0,0 +1,315 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 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 "kexidataprovider.h" + +#include <qwidget.h> +#include <qobjectlist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <widget/tableview/kexitableitem.h> +#include <widget/tableview/kexitableviewdata.h> +#include <widget/tableview/kexicomboboxbase.h> +#include <kexidb/queryschema.h> +#include <kexiutils/utils.h> + +#include "widgets/kexidbform.h" + +KexiFormDataProvider::KexiFormDataProvider() + : KexiDataItemChangesListener() + , m_mainWidget(0) + , m_duplicatedItems(0) + , m_disableFillDuplicatedDataItems(false) +{ +} + +KexiFormDataProvider::~KexiFormDataProvider() +{ + delete m_duplicatedItems; +} + +void KexiFormDataProvider::setMainDataSourceWidget(QWidget* mainWidget) +{ + m_mainWidget = mainWidget; + m_dataItems.clear(); + m_usedDataSources.clear(); + m_fieldNumbersForDataItems.clear(); + if (!m_mainWidget) + return; + + //find widgets whose will work as data items + QObjectList *l = m_mainWidget->queryList( "QWidget" ); + QObjectListIt it( *l ); + QObject *obj; + QDict<char> tmpSources; + for ( ; (obj = it.current()) != 0; ++it ) { + KexiFormDataItemInterface* const formDataItem = dynamic_cast<KexiFormDataItemInterface*>(obj); + if (!formDataItem) + continue; + if (formDataItem->parentInterface()) //item with parent interface: collect parent instead... + continue; +#if 0 //! @todo reenable when subform is moved to KexiDBForm + KexiDBForm *dbForm = KexiUtils::findParent<KexiDBForm>(obj, "KexiDBForm"); //form's surface... + if (dbForm!=m_mainWidget) //only set data for this form's data items + continue; +#else + //tmp: reject widgets within subforms + if (KexiUtils::findParent<KexiDBForm>(obj, "KexiDBSubForm")) + continue; +#endif + QString dataSource( formDataItem->dataSource().lower() ); + if (dataSource.isEmpty()) + continue; + kexipluginsdbg << obj->name() << endl; + m_dataItems.append( formDataItem ); + formDataItem->installListener( this ); + tmpSources.replace( dataSource, (char*)1 ); + } + delete l; + //now we've got a set (unique list) of field names in tmpSources + //remember it in m_usedDataSources + for (QDictIterator<char> it(tmpSources); it.current(); ++it) { + m_usedDataSources += it.currentKey(); + } +} + +void KexiFormDataProvider::fillDataItems(KexiTableItem& row, bool cursorAtNewRow) +{ + kexipluginsdbg << "KexiFormDataProvider::fillDataItems() cnt=" << row.count() << endl; + for (KexiFormDataItemInterfaceToIntMap::ConstIterator it = m_fieldNumbersForDataItems.constBegin(); + it!=m_fieldNumbersForDataItems.constEnd(); ++it) + { + KexiFormDataItemInterface *itemIface = it.key(); + if (!itemIface->columnInfo()) { + kexipluginsdbg << "KexiFormDataProvider::fillDataItems(): itemIface->columnInfo() == 0" << endl; + continue; + } + //1. Is this a value with a combo box (lookup)? + int indexForVisibleLookupValue = itemIface->columnInfo()->indexForVisibleLookupValue(); + if (indexForVisibleLookupValue<0 && indexForVisibleLookupValue>=(int)row.count()) //sanity + indexForVisibleLookupValue = -1; //no + const QVariant value(row.at(it.data())); + QVariant visibleLookupValue; + if (indexForVisibleLookupValue!=-1 && (int)row.size()>indexForVisibleLookupValue) + visibleLookupValue = row.at(indexForVisibleLookupValue); + kexipluginsdbg << "fill data of '" << itemIface->dataSource() << "' at idx=" << it.data() + << " data=" << value << (indexForVisibleLookupValue!=-1 + ? QString(" SPECIAL: indexForVisibleLookupValue=%1 visibleValue=%2") + .arg(indexForVisibleLookupValue).arg(visibleLookupValue.toString()) + : QString::null) + << endl; + const bool displayDefaultValue = cursorAtNewRow && (value.isNull() && visibleLookupValue.isNull()) + && !itemIface->columnInfo()->field->defaultValue().isNull() + && !itemIface->columnInfo()->field->isAutoIncrement(); //no value to set but there is default value defined + itemIface->setValue( + displayDefaultValue ? itemIface->columnInfo()->field->defaultValue() : value, + QVariant(), /*add*/ + /*!remove old*/false, + indexForVisibleLookupValue==-1 ? 0 : &visibleLookupValue //pass visible value if available + ); + // now disable/enable "display default value" if needed (do it after setValue(), before setValue() turns it off) + if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue) + itemIface->setDisplayDefaultValue( dynamic_cast<QWidget*>(itemIface), displayDefaultValue ); + } +} + +void KexiFormDataProvider::fillDuplicatedDataItems( + KexiFormDataItemInterface* item, const QVariant& value) +{ + if (m_disableFillDuplicatedDataItems) + return; + if (!m_duplicatedItems) { + //build (once) a set of duplicated data items (having the same fields assigned) + //so we can later check if an item is duplicated with a cost of o(1) + QMap<KexiDB::Field*,int> tmpDuplicatedItems; + QMapIterator<KexiDB::Field*,int> it_dup; + for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) { + if (!it.current()->columnInfo() || !it.current()->columnInfo()->field) + continue; + kdDebug() << " ** " << it.current()->columnInfo()->field->name() << endl; + it_dup = tmpDuplicatedItems.find( it.current()->columnInfo()->field ); + uint count; + if (it_dup==tmpDuplicatedItems.end()) + count = 0; + else + count = it_dup.data(); + tmpDuplicatedItems.insert( it.current()->columnInfo()->field, ++count ); + } + m_duplicatedItems = new QPtrDict<char>(101); + for (it_dup = tmpDuplicatedItems.begin(); it_dup!=tmpDuplicatedItems.end(); ++it_dup) { + if (it_dup.data() > 1) { + m_duplicatedItems->insert( it_dup.key(), (char*)1 ); + kexipluginsdbg << "duplicated item: " << static_cast<KexiDB::Field*>(it_dup.key())->name() + << " (" << it_dup.data() << " times)" << endl; + } + } + } + if (item->columnInfo() && m_duplicatedItems->find( item->columnInfo()->field )) { + for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) { + if (it.current()!=item && item->columnInfo()->field == it.current()->columnInfo()->field) { + kexipluginsdbg << "- setting a copy of value for item '" + << dynamic_cast<QObject*>(it.current())->name() << "' == " << value << endl; + it.current()->setValue( value ); + } + } + } +} + +void KexiFormDataProvider::valueChanged(KexiDataItemInterface* item) +{ + Q_UNUSED( item ); +} + +bool KexiFormDataProvider::cursorAtNewRow() const +{ + return false; +} + +void KexiFormDataProvider::invalidateDataSources( const QDict<char>& invalidSources, + KexiDB::QuerySchema* query) +{ + //fill m_fieldNumbersForDataItems mapping from data item to field number + //(needed for fillDataItems) + KexiDB::QueryColumnInfo::Vector fieldsExpanded; +// uint dataFieldsCount; // == fieldsExpanded.count() if query is available or else == m_dataItems.count() + + if (query) { + fieldsExpanded = query->fieldsExpanded( KexiDB::QuerySchema::WithInternalFields ); +// dataFieldsCount = fieldsExpanded.count(); + QMap<KexiDB::QueryColumnInfo*,int> columnsOrder( query->columnsOrder() ); + for (QMapConstIterator<KexiDB::QueryColumnInfo*,int> it = columnsOrder.constBegin(); it!=columnsOrder.constEnd(); ++it) { + kexipluginsdbg << "query->columnsOrder()[ " << it.key()->field->name() << " ] = " << it.data() << endl; + } + for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) { + KexiFormDataItemInterface *item = it.current(); + KexiDB::QueryColumnInfo* ci = query->columnInfo( it.current()->dataSource() ); + int index = ci ? columnsOrder[ ci ] : -1; + kexipluginsdbg << "query->columnsOrder()[ " << (ci ? ci->field->name() : "") << " ] = " << index + << " (dataSource: " << item->dataSource() << ", name=" << dynamic_cast<QObject*>(item)->name() << ")" << endl; + if (index!=-1 && !m_fieldNumbersForDataItems[ item ]) + m_fieldNumbersForDataItems.insert( item, index ); + //todo + //WRONG: not only used data sources can be fetched! + // m_fieldNumbersForDataItems.insert( it.current(), + // m_usedDataSources.findIndex(it.current()->dataSource().lower()) ); + } + } + else {//!query +// dataFieldsCount = m_dataItems.count(); + } + +#if 0 //moved down + //in 'newIndices' let's collect new indices for every data source + foreach(QValueList<uint>::ConstIterator, it, invalidSources) { + //all previous indices have corresponding data source +// for (; i < (*it); i++) { +// newIndices[i] = number++; + //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl; +// } + //this index have no corresponding data source +// newIndices[i]=-1; + KexiFormDataItemInterface *item = m_dataItems.at( *it ); + if (item) + item->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") ); + m_dataItems.remove(*it); + kexipluginsdbg << "invalidateDataSources(): " << (*it) << " -> " << -1 << endl; +// i++; + } +#endif + //fill remaining part of the vector +// for (; i < dataFieldsCount; i++) { //m_dataItems.count(); i++) { + //newIndices[i] = number++; + //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl; + //} + +#if 0 + //recreate m_fieldNumbersForDataItems and mark widgets with invalid data sources + KexiFormDataItemInterfaceToIntMap newFieldNumbersForDataItems; + foreach(KexiFormDataItemInterfaceToIntMap::ConstIterator, it, m_fieldNumbersForDataItems) { + bool ok; + const int newIndex = newIndices.at( it.data(), &ok ); + if (ok && newIndex!=-1) { + kexipluginsdbg << "invalidateDataSources(): " << it.key()->dataSource() << ": " << it.data() << " -> " << newIndex << endl; + newFieldNumbersForDataItems.replace(it.key(), newIndex); + } + else { + kexipluginsdbg << "invalidateDataSources(): removing " << it.key()->dataSource() << endl; + m_dataItems.remove(it.key()); + it.key()->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") ); + } + } +#endif +// m_fieldNumbersForDataItems = newFieldNumbersForDataItems; + + //update data sources set (some of them may be removed) + QDict<char> tmpUsedDataSources(1013); + + if (query) + query->debug(); + + //if (query && m_dataItems.count()!=query->fieldCount()) { + // kdWarning() << "KexiFormDataProvider::invalidateDataSources(): m_dataItems.count()!=query->fieldCount() (" + // << m_dataItems.count() << "," << query->fieldCount() << ")" << endl; + //} + //i = 0; + m_disableFillDuplicatedDataItems = true; // temporary disable fillDuplicatedDataItems() + // because setColumnInfo() can activate it + for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current();) { + KexiFormDataItemInterface * item = it.current(); + if (invalidSources[ item->dataSource().lower() ]) { + item->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") ); + m_dataItems.remove(item); + continue; + } + uint fieldNumber = m_fieldNumbersForDataItems[ item ]; + if (query) { + KexiDB::QueryColumnInfo *ci = fieldsExpanded[fieldNumber]; + item->setColumnInfo(ci); + kexipluginsdbg << "- item=" << dynamic_cast<QObject*>(item)->name() + << " dataSource=" << item->dataSource() + << " field=" << ci->field->name() << endl; + const int indexForVisibleLookupValue = ci->indexForVisibleLookupValue(); + if (-1 != indexForVisibleLookupValue && indexForVisibleLookupValue < (int)fieldsExpanded.count()) { + //there's lookup column defined: set visible column as well + KexiDB::QueryColumnInfo *visibleColumnInfo = fieldsExpanded[ indexForVisibleLookupValue ]; + if (visibleColumnInfo) { + item->setVisibleColumnInfo( visibleColumnInfo ); + if (dynamic_cast<KexiComboBoxBase*>(item) && m_mainWidget + && dynamic_cast<KexiComboBoxBase*>(item)->internalEditor()) + { + // m_mainWidget (dbform) should filter the (just created using setVisibleColumnInfo()) + // combo box' internal editor (actually, only if the combo is in 'editable' mode) + dynamic_cast<KexiComboBoxBase*>(item)->internalEditor()->installEventFilter(m_mainWidget); + } + kexipluginsdbg << " ALSO SET visibleColumn=" << visibleColumnInfo->debugString() + << "\n at position " << indexForVisibleLookupValue << endl; + } + } + } + tmpUsedDataSources.replace( item->dataSource().lower(), (char*)1 ); + ++it; + } + m_disableFillDuplicatedDataItems = false; + m_usedDataSources.clear(); + foreach_list(QDictIterator<char>, it, tmpUsedDataSources) { + m_usedDataSources += it.currentKey(); + } +} diff --git a/kexi/plugins/forms/kexidataprovider.h b/kexi/plugins/forms/kexidataprovider.h new file mode 100644 index 00000000..64019842 --- /dev/null +++ b/kexi/plugins/forms/kexidataprovider.h @@ -0,0 +1,95 @@ +/* This file is part of the KDE project + Copyright (C) 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 KEXIFORMDATAPROVIDER_H +#define KEXIFORMDATAPROVIDER_H + +#include "kexiformdataiteminterface.h" +#include <qptrdict.h> +#include <qdict.h> + +class KexiTableItem; +namespace KexiDB { + class QuerySchema; +} + +//! @short The KexiFormDataProvider class is a data provider for Kexi Forms +/*! This provider collects data-aware widgets using setMainWidget(). + Then, usedDataSources() unique list of required field names is available. + On every call of fillDataItems() method, the provider will fill data items + with appropriate data from a database cursor. + + Field names are collected effectively, so eg. having widgets using data sources: + ("name", "surname", "surname", "name") - "name" and "surname" repeated - will only + return ("name", "surname") list, so the cursor's query can be simplified + and thus more effective. +*/ +class KEXIFORMUTILS_EXPORT KexiFormDataProvider : public KexiDataItemChangesListener +{ + public: + KexiFormDataProvider(); + virtual ~KexiFormDataProvider(); + + /*! sets \a mainWidget to be a main widget for this data provider. + Also find widgets whose will work as data items + (all of them must implement KexiFormDataItemInterface), so these could be + filled with data on demand. */ + void setMainDataSourceWidget(QWidget* mainWidget); + + QStringList usedDataSources() const { return m_usedDataSources; } + + //unused QPtrList<KexiFormDataItemInterface>& dataItems() { return m_dataItems; } + + /*! Fills data items with appropriate data fetched from \a cursor. + \a newRowEditing == true means that we are at new (not yet inserted) database row. */ + void fillDataItems(KexiTableItem& row, bool cursorAtNewRow); + + /*! Implementation for KexiDataItemChangesListener. + Reaction for change of \a item. Does nothing here. */ + virtual void valueChanged(KexiDataItemInterface* item); + + /*! Implementation for KexiDataItemChangesListener. + Implement this to return information whether we're currently at new row or now. + This can be used e.g. by data-aware widgets to determine if "(autonumber)" + label should be displayed. Returns false here. */ + virtual bool cursorAtNewRow() const; + + /*! Invalidates data sources collected by this provided. + \a invalidSources is the set of data sources that should + be omitted for fillDataItems(). + Used by KexiFormView::initDataSource(). */ + void invalidateDataSources( const QDict<char>& invalidSources, + KexiDB::QuerySchema* query = 0 ); + + /*! Fills the same data provided by \a value to every data item (other than \a item) + having the same data source as \a item. This method is called immediately when + \a value is changed, so duplicated data items are quickly updated. */ + void fillDuplicatedDataItems(KexiFormDataItemInterface* item, const QVariant& value); + + protected: + QWidget *m_mainWidget; + QPtrDict<char> *m_duplicatedItems; + typedef QMap<KexiFormDataItemInterface*,uint> KexiFormDataItemInterfaceToIntMap; + QPtrList<KexiFormDataItemInterface> m_dataItems; + QStringList m_usedDataSources; + KexiFormDataItemInterfaceToIntMap m_fieldNumbersForDataItems; + bool m_disableFillDuplicatedDataItems : 1; +}; + +#endif diff --git a/kexi/plugins/forms/kexidatasourcepage.cpp b/kexi/plugins/forms/kexidatasourcepage.cpp new file mode 100644 index 00000000..6c0de830 --- /dev/null +++ b/kexi/plugins/forms/kexidatasourcepage.cpp @@ -0,0 +1,471 @@ +/* This file is part of the KDE project + Copyright (C) 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 "kexidatasourcepage.h" + +#include <qlabel.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qheader.h> + +#include <kiconloader.h> +#include <klocale.h> +#include <ktoolbarbutton.h> +#include <kdebug.h> +#include <kpopupmenu.h> + +#include <widget/kexipropertyeditorview.h> +#include <widget/kexidatasourcecombobox.h> +#include <widget/kexifieldlistview.h> +#include <widget/kexifieldcombobox.h> +#include <widget/kexismalltoolbutton.h> +#include <kexidb/connection.h> +#include <kexiproject.h> + +#include <formeditor/commands.h> + +#include <koproperty/property.h> +#include <koproperty/utils.h> + +KexiDataSourcePage::KexiDataSourcePage(QWidget *parent, const char *name) + : QWidget(parent, name) + , m_insideClearDataSourceSelection(false) +{ + QVBoxLayout *vlyr = new QVBoxLayout(this); + m_objectInfoLabel = new KexiObjectInfoLabel(this, "KexiObjectInfoLabel"); + vlyr->addWidget(m_objectInfoLabel); + + m_noDataSourceAvailableSingleText = i18n("No data source could be assigned for this widget."); + m_noDataSourceAvailableMultiText = i18n("No data source could be assigned for multiple widgets."); + + vlyr->addSpacing(8); + + //Section 1: Form's/Widget's Data Source + KoProperty::GroupContainer *container = new KoProperty::GroupContainer(i18n("Data Source"), this); + vlyr->addWidget(container); + + QWidget *contents = new QWidget(container); + container->setContents(contents); + QVBoxLayout *contentsVlyr = new QVBoxLayout(contents); + + m_noDataSourceAvailableLabel = new QLabel(m_noDataSourceAvailableSingleText, contents); + m_noDataSourceAvailableLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + m_noDataSourceAvailableLabel->setMargin(2); + m_noDataSourceAvailableLabel->setAlignment(Qt::WordBreak | Qt::AlignBottom | Qt::AlignLeft); + contentsVlyr->addWidget(m_noDataSourceAvailableLabel); + + //-Widget's Data Source + QHBoxLayout *hlyr = new QHBoxLayout(contentsVlyr); +#if 0 +//! @todo unhide this when expression work +// m_widgetDSLabel = new QLabel(i18n("Table Field, Query Field or Expression", "Source field or expression:"), this); +#else + m_widgetDSLabel = new QLabel(i18n("Table Field or Query Field", "Widget's data source:"), contents); +#endif + m_widgetDSLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_widgetDSLabel->setMargin(2); + m_widgetDSLabel->setMinimumHeight(IconSize(KIcon::Small)+4); + m_widgetDSLabel->setAlignment(AlignLeft|AlignBottom); + hlyr->addWidget(m_widgetDSLabel); + + m_clearWidgetDSButton = new KexiSmallToolButton(contents, QString::null, "clear_left", "clearWidgetDSButton"); + m_clearWidgetDSButton->setMinimumHeight(m_widgetDSLabel->minimumHeight()); + QToolTip::add(m_clearWidgetDSButton, i18n("Clear widget's data source")); + hlyr->addWidget(m_clearWidgetDSButton); + connect(m_clearWidgetDSButton, SIGNAL(clicked()), this, SLOT(clearWidgetDataSourceSelection())); + + m_sourceFieldCombo = new KexiFieldComboBox(contents, "sourceFieldCombo"); + m_widgetDSLabel->setBuddy(m_sourceFieldCombo); + contentsVlyr->addWidget(m_sourceFieldCombo); + +/* m_dataSourceSeparator = new QFrame(contents); + m_dataSourceSeparator->setFrameShape(QFrame::HLine); + m_dataSourceSeparator->setFrameShadow(QFrame::Sunken); + contentsVlyr->addWidget(m_dataSourceSeparator);*/ + + contentsVlyr->addSpacing(8); + + //- Form's Data Source + hlyr = new QHBoxLayout(contentsVlyr); + m_dataSourceLabel = new QLabel(i18n("Form's data source:"), contents); + m_dataSourceLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_dataSourceLabel->setMargin(2); + m_dataSourceLabel->setMinimumHeight(IconSize(KIcon::Small)+4); + m_dataSourceLabel->setAlignment(AlignLeft|AlignBottom); + hlyr->addWidget(m_dataSourceLabel); + + m_gotoButton = new KexiSmallToolButton(contents, QString::null, "goto", "gotoButton"); + m_gotoButton->setMinimumHeight(m_dataSourceLabel->minimumHeight()); + QToolTip::add(m_gotoButton, i18n("Go to selected form's data source")); + hlyr->addWidget(m_gotoButton); + connect(m_gotoButton, SIGNAL(clicked()), this, SLOT(slotGotoSelected())); + + m_clearDSButton = new KexiSmallToolButton(contents, QString::null, "clear_left", "clearDSButton"); + m_clearDSButton->setMinimumHeight(m_dataSourceLabel->minimumHeight()); + QToolTip::add(m_clearDSButton, i18n("Clear form's data source")); + hlyr->addWidget(m_clearDSButton); + connect(m_clearDSButton, SIGNAL(clicked()), this, SLOT(clearDataSourceSelection())); + + m_dataSourceCombo = new KexiDataSourceComboBox(contents, "dataSourceCombo"); + m_dataSourceLabel->setBuddy(m_dataSourceCombo); + contentsVlyr->addWidget(m_dataSourceCombo); + +#ifdef KEXI_NO_AUTOFIELD_WIDGET + m_availableFieldsLabel = 0; + m_addField = 0; +// m_fieldListView = 0; + vlyr->addStretch(); +#else + vlyr->addSpacing(fontMetrics().height()); +/* QFrame *separator = new QFrame(this); + separator->setFrameShape(QFrame::HLine); + separator->setFrameShadow(QFrame::Sunken); + vlyr->addWidget(separator);*/ +/* + KPopupTitle *title = new KPopupTitle(this); + title->setTitle(i18n("Inserting fields")); + vlyr->addWidget(title); + vlyr->addSpacing(4);*/ + + + //2. Inserting fields + container = new KoProperty::GroupContainer(i18n("Inserting Fields"), this); + vlyr->addWidget(container, 1); + + //helper info +//! @todo allow to hide such helpers by adding global option + contents = new QWidget(container); + container->setContents(contents); + contentsVlyr = new QVBoxLayout(contents); + hlyr = new QHBoxLayout(contentsVlyr); + m_mousePointerLabel = new QLabel(contents); + hlyr->addWidget(m_mousePointerLabel); + m_mousePointerLabel->setPixmap( SmallIcon("mouse_pointer") ); + m_mousePointerLabel->setFixedWidth( m_mousePointerLabel->pixmap() ? m_mousePointerLabel->pixmap()->width() : 0); + m_availableFieldsDescriptionLabel = new QLabel( + i18n("Select fields from the list below and drag them onto a form or click the \"Insert\" button"), contents); + m_availableFieldsDescriptionLabel->setAlignment( Qt::AlignAuto | Qt::WordBreak ); + hlyr->addWidget(m_availableFieldsDescriptionLabel); + + //Available Fields + contentsVlyr->addSpacing(4); + hlyr = new QHBoxLayout(contentsVlyr); + m_availableFieldsLabel = new QLabel(i18n("Available fields:"), contents); + m_availableFieldsLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + m_availableFieldsLabel->setMargin(2); + m_availableFieldsLabel->setMinimumHeight(IconSize(KIcon::Small)); + hlyr->addWidget(m_availableFieldsLabel); + + m_addField = new KexiSmallToolButton(contents, i18n("Insert selected field into form", "Insert"), + "add_field", "addFieldButton"); + m_addField->setMinimumHeight(m_availableFieldsLabel->minimumHeight()); +// m_addField->setTextPosition(QToolButton::Right); + m_addField->setFocusPolicy(StrongFocus); + QToolTip::add(m_addField, i18n("Insert selected fields into form")); + hlyr->addWidget(m_addField); + connect(m_addField, SIGNAL(clicked()), this, SLOT(slotInsertSelectedFields())); + + m_fieldListView = new KexiFieldListView(contents, "fieldListView", + KexiFieldListView::ShowDataTypes | KexiFieldListView::AllowMultiSelection ); + m_fieldListView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + m_availableFieldsLabel->setBuddy(m_fieldListView); + contentsVlyr->addWidget(m_fieldListView, 1); + connect(m_fieldListView, SIGNAL(selectionChanged()), this, SLOT(slotFieldListViewSelectionChanged())); + connect(m_fieldListView, SIGNAL(fieldDoubleClicked(const QString&, const QString&, const QString&)), + this, SLOT(slotFieldDoubleClicked(const QString&, const QString&, const QString&))); +#endif + + vlyr->addStretch(1); + + connect(m_dataSourceCombo, SIGNAL(textChanged(const QString &)), this, SLOT(slotDataSourceTextChanged(const QString &))); + connect(m_dataSourceCombo, SIGNAL(dataSourceChanged()), this, SLOT(slotDataSourceChanged())); + connect(m_sourceFieldCombo, SIGNAL(selected()), this, SLOT(slotFieldSelected())); + + clearDataSourceSelection(); + slotFieldListViewSelectionChanged(); +} + +KexiDataSourcePage::~KexiDataSourcePage() +{ +} + +void KexiDataSourcePage::setProject(KexiProject *prj) +{ + m_sourceFieldCombo->setProject(prj); + m_dataSourceCombo->setProject(prj); +} + +void KexiDataSourcePage::clearDataSourceSelection(bool alsoClearComboBox) +{ + if (m_insideClearDataSourceSelection) + return; + m_insideClearDataSourceSelection = true; + if (alsoClearComboBox && !m_dataSourceCombo->selectedName().isEmpty()) + m_dataSourceCombo->setDataSource("", ""); +// if (!m_dataSourceCombo->currentText().isEmpty()) { +// m_dataSourceCombo->setCurrentText(""); +// emit m_dataSourceCombo->dataSourceSelected(); +// } + m_clearDSButton->setEnabled(false); + m_gotoButton->setEnabled(false); +#ifndef KEXI_NO_AUTOFIELD_WIDGET + m_addField->setEnabled(false); + m_fieldListView->clear(); +#endif + m_insideClearDataSourceSelection = false; +} + +void KexiDataSourcePage::clearWidgetDataSourceSelection() +{ + if (!m_sourceFieldCombo->currentText().isEmpty()) { + m_sourceFieldCombo->setCurrentText(""); + m_sourceFieldCombo->setFieldOrExpression(QString::null); + slotFieldSelected(); + } + m_clearWidgetDSButton->setEnabled(false); +} + +void KexiDataSourcePage::slotGotoSelected() +{ + QCString mime = m_dataSourceCombo->selectedMimeType().latin1(); + if (mime=="kexi/table" || mime=="kexi/query") { + if (m_dataSourceCombo->isSelectionValid()) + emit jumpToObjectRequested(mime, m_dataSourceCombo->selectedName().latin1()); + } +} + +void KexiDataSourcePage::slotInsertSelectedFields() +{ +#ifndef KEXI_NO_AUTOFIELD_WIDGET + QStringList selectedFieldNames(m_fieldListView->selectedFieldNames()); + if (selectedFieldNames.isEmpty()) + return; + + emit insertAutoFields(m_fieldListView->schema()->table() ? "kexi/table" : "kexi/query", + m_fieldListView->schema()->name(), selectedFieldNames); +#endif +} + +void KexiDataSourcePage::slotFieldDoubleClicked(const QString& sourceMimeType, const QString& sourceName, + const QString& fieldName) +{ +#ifndef KEXI_NO_AUTOFIELD_WIDGET + QStringList selectedFields; + selectedFields.append(fieldName); + emit insertAutoFields(sourceMimeType, sourceName, selectedFields); +#endif +} + +void KexiDataSourcePage::slotDataSourceTextChanged(const QString & string) +{ + Q_UNUSED(string); + const bool enable = m_dataSourceCombo->isSelectionValid(); //!string.isEmpty() && m_dataSourceCombo->selectedName() == string.latin1(); + if (!enable) { + clearDataSourceSelection( m_dataSourceCombo->selectedName().isEmpty()/*alsoClearComboBox*/ ); + } + updateSourceFieldWidgetsAvailability(); +/*#ifndef KEXI_NO_AUTOFIELD_WIDGET + m_fieldListView->setEnabled(enable); +// m_addField->setEnabled(enable); + m_availableFieldsLabel->setEnabled(enable); +#endif*/ +} + +void KexiDataSourcePage::slotDataSourceChanged() +{ + if (!m_dataSourceCombo->project()) + return; + QCString mime = m_dataSourceCombo->selectedMimeType().latin1(); + bool dataSourceFound = false; + QCString name = m_dataSourceCombo->selectedName().latin1(); + if ((mime=="kexi/table" || mime=="kexi/query") && m_dataSourceCombo->isSelectionValid()) { + KexiDB::TableOrQuerySchema *tableOrQuery = new KexiDB::TableOrQuerySchema( + m_dataSourceCombo->project()->dbConnection(), name, mime=="kexi/table"); + if (tableOrQuery->table() || tableOrQuery->query()) { +#ifdef KEXI_NO_AUTOFIELD_WIDGET + m_tableOrQuerySchema = tableOrQuery; +#else + m_fieldListView->setSchema( tableOrQuery ); +#endif + dataSourceFound = true; + m_sourceFieldCombo->setTableOrQuery(name, mime=="kexi/table"); + } + else { + delete tableOrQuery; + } + } + if (!dataSourceFound) { + m_sourceFieldCombo->setTableOrQuery("", true); + } + //if (m_sourceFieldCombo->hasFocus()) +// m_dataSourceCombo->setFocus(); + m_clearDSButton->setEnabled(dataSourceFound); + m_gotoButton->setEnabled(dataSourceFound); + if (dataSourceFound) { + slotFieldListViewSelectionChanged(); + } else { +#ifndef KEXI_NO_AUTOFIELD_WIDGET + m_addField->setEnabled(false); +#endif + } + updateSourceFieldWidgetsAvailability(); + emit formDataSourceChanged(mime, name); +} + +void KexiDataSourcePage::slotFieldSelected() +{ + KexiDB::Field::Type dataType = KexiDB::Field::InvalidType; +#ifdef KEXI_NO_AUTOFIELD_WIDGET + KexiDB::Field *field = m_tableOrQuerySchema->field( m_sourceFieldCombo->fieldOrExpression() ); //temp +#else +//! @todo this should also work for expressions + KexiDB::Field *field = m_fieldListView->schema()->field( m_sourceFieldCombo->fieldOrExpression() ); +#endif + if (field) + dataType = field->type(); + + m_clearWidgetDSButton->setEnabled( !m_sourceFieldCombo->fieldOrExpression().isEmpty() ); + + emit dataSourceFieldOrExpressionChanged( + m_sourceFieldCombo->fieldOrExpression(), + m_sourceFieldCombo->fieldOrExpressionCaption(), + dataType + ); +} + +void KexiDataSourcePage::setDataSource(const QCString& mimeType, const QCString& name) +{ + m_dataSourceCombo->setDataSource(mimeType, name); +} + +void KexiDataSourcePage::assignPropertySet(KoProperty::Set* propertySet) +{ + QCString objectName; + if (propertySet && propertySet->contains("name")) + objectName = (*propertySet)["name"].value().toCString(); + if (!objectName.isEmpty() && objectName == m_currentObjectName) + return; //the same object + m_currentObjectName = objectName; + + QCString objectClassName; + if (propertySet && propertySet->contains("this:className")) + objectClassName = (*propertySet)["this:className"].value().toCString(); +/*moved if (propertySet) { + QCString iconName; + QString objectClassString; + if (propertySet->contains("this:iconName")) + iconName = (*propertySet)["this:iconName"].value().toCString(); + if (propertySet->contains("this:classString")) + objectClassString = (*propertySet)["this:classString"].value().toString(); + m_objectInfoLabel->setObjectName(objectName); + m_objectInfoLabel->setObjectClassIcon(iconName); + m_objectInfoLabel->setObjectClassName(objectClassString); + if (propertySet->contains("this:className")) + objectClassName = (*propertySet)["this:className"].value().toCString(); + }*/ + KexiPropertyEditorView::updateInfoLabelForPropertySet( + m_objectInfoLabel, propertySet); + + const bool isForm = objectClassName=="KexiDBForm"; +// kdDebug() << "objectClassName=" << objectClassName << endl; +// { +/* //this is top level form's surface: data source means table or query + QCString dataSourceMimeType, dataSource; + if (buffer->hasProperty("dataSourceMimeType")) + dataSourceMimeType = (*buffer)["dataSourceMimeType"].value().toCString(); + if (buffer->hasProperty("dataSource")) + dataSource = (*buffer)["dataSource"].value().toCString(); + m_dataSourceCombo->setDataSource(dataSourceMimeType, dataSource);*/ +// } +// else { + + const bool multipleSelection = objectClassName=="special:multiple"; + const bool hasDataSourceProperty = propertySet && propertySet->contains("dataSource") && !multipleSelection; + + if (!isForm) { + //this is a widget + QCString dataSource; + if (hasDataSourceProperty) { + if (propertySet) + dataSource = (*propertySet)["dataSource"].value().toCString(); + m_noDataSourceAvailableLabel->hide(); + m_sourceFieldCombo->setFieldOrExpression(dataSource); + m_sourceFieldCombo->setEnabled(true); + m_clearWidgetDSButton->setEnabled(!m_sourceFieldCombo->currentText().isEmpty()); + m_widgetDSLabel->show(); + m_clearWidgetDSButton->show(); + m_sourceFieldCombo->show(); +// m_dataSourceSeparator->hide(); + updateSourceFieldWidgetsAvailability(); + } + } + + if (isForm) { + m_noDataSourceAvailableLabel->hide(); +// m_dataSourceSeparator->hide(); + } + else if (!hasDataSourceProperty) { + if (multipleSelection) + m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableMultiText); + else + m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableSingleText); + m_noDataSourceAvailableLabel->show(); +// m_dataSourceSeparator->show(); + //make 'No data source could be assigned' label's height the same as the 'source field' combo+label + m_noDataSourceAvailableLabel->setMinimumHeight(m_widgetDSLabel->height() + + m_sourceFieldCombo->height()/*-m_dataSourceSeparator->height()*/); + m_sourceFieldCombo->setCurrentText(""); + } + + if (isForm || !hasDataSourceProperty) { + //no source field can be set + m_widgetDSLabel->hide(); + m_clearWidgetDSButton->hide(); + m_sourceFieldCombo->hide(); + } +} + +void KexiDataSourcePage::slotFieldListViewSelectionChanged() +{ +#ifndef KEXI_NO_AUTOFIELD_WIDGET + //update "add field" button's state + for (QListViewItemIterator it(m_fieldListView); it.current(); ++it) { + if (it.current()->isSelected()) { + m_addField->setEnabled(true); + return; + } + } + m_addField->setEnabled(false); +#endif +} + +void KexiDataSourcePage::updateSourceFieldWidgetsAvailability() +{ + const bool hasDataSource = m_dataSourceCombo->isSelectionValid(); //!m_dataSourceCombo->selectedName().isEmpty(); + m_sourceFieldCombo->setEnabled( hasDataSource ); + m_widgetDSLabel->setEnabled( hasDataSource ); +#ifndef KEXI_NO_AUTOFIELD_WIDGET + m_fieldListView->setEnabled( hasDataSource ); + m_availableFieldsLabel->setEnabled( hasDataSource ); + m_mousePointerLabel->setEnabled( hasDataSource ); + m_availableFieldsDescriptionLabel->setEnabled( hasDataSource ); +#endif +} + +#include "kexidatasourcepage.moc" diff --git a/kexi/plugins/forms/kexidatasourcepage.h b/kexi/plugins/forms/kexidatasourcepage.h new file mode 100644 index 00000000..0f113aa7 --- /dev/null +++ b/kexi/plugins/forms/kexidatasourcepage.h @@ -0,0 +1,112 @@ +/* This file is part of the KDE project + Copyright (C) 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 KEXIDATASOURCEPAGE_H +#define KEXIDATASOURCEPAGE_H + +#include <qwidget.h> +#include <kexidb/field.h> +#include <kexidb/utils.h> +#include <koproperty/set.h> + +class KCommand; +class KexiObjectInfoLabel; +class KexiDataSourceComboBox; +class KexiFieldComboBox; +class KexiFieldListView; +class KexiProject; +class QToolButton; +class QLabel; +class QFrame; + +//! A page within form designer's property tabbed pane, providing data source editor +class KEXIFORMUTILS_EXPORT KexiDataSourcePage : public QWidget +{ + Q_OBJECT + + public: + KexiDataSourcePage(QWidget *parent, const char *name = 0); + virtual ~KexiDataSourcePage(); + + KexiDataSourceComboBox* dataSourceCombo() const { return m_dataSourceCombo; } + KexiObjectInfoLabel* objectInfoLabel() const { return m_objectInfoLabel; } + + public slots: + void setProject(KexiProject *prj); + void clearDataSourceSelection(bool alsoClearComboBox = true); + void clearWidgetDataSourceSelection(); + + //! Sets data source of a currently selected form. + //! This is performed on form initialization and on activating. + void setDataSource(const QCString& mimeType, const QCString& name); + + //! Receives a pointer to a new property \a set (from KexiFormView::managerPropertyChanged()) + void assignPropertySet(KoProperty::Set* propertySet); + + signals: + //! Signal emitted when helper button 'go to selected data source' is clicked. + void jumpToObjectRequested(const QCString& mime, const QCString& name); + + //! Signal emitted when form's data source has been changed. It's connected to the Form Manager. + void formDataSourceChanged(const QCString& mime, const QCString& name); + + /*! Signal emitted when current widget's data source (field/expression) + has been changed. It's connected to the Form Manager. + \a caption for this field is also provided (e.g. AutoField form widget use it) */ + void dataSourceFieldOrExpressionChanged(const QString& string, const QString& caption, + KexiDB::Field::Type type); + + /*! Signal emitted when 'insert fields' button has been clicked */ + void insertAutoFields(const QString& sourceMimeType, const QString& sourceName, + const QStringList& fields); + + protected slots: + void slotDataSourceTextChanged(const QString & string); + void slotDataSourceChanged(); + void slotFieldSelected(); + void slotGotoSelected(); + void slotInsertSelectedFields(); + void slotFieldListViewSelectionChanged(); + void slotFieldDoubleClicked(const QString& sourceMimeType, const QString& sourceName, + const QString& fieldName); + + protected: + void updateSourceFieldWidgetsAvailability(); + + KexiFieldComboBox *m_sourceFieldCombo; + KexiObjectInfoLabel *m_objectInfoLabel; + KexiDataSourceComboBox* m_dataSourceCombo; + QLabel *m_dataSourceLabel, *m_noDataSourceAvailableLabel, + *m_widgetDSLabel, *m_availableFieldsLabel, + *m_mousePointerLabel, *m_availableFieldsDescriptionLabel; + QToolButton *m_clearWidgetDSButton, *m_clearDSButton, *m_gotoButton, *m_addField; + QFrame *m_dataSourceSeparator; + QString m_noDataSourceAvailableSingleText, m_noDataSourceAvailableMultiText; + bool m_insideClearDataSourceSelection : 1; +#ifdef KEXI_NO_AUTOFIELD_WIDGET + KexiDB::TableOrQuerySchema *m_tableOrQuerySchema; //!< temp. +#else + KexiFieldListView* m_fieldListView; +#endif + + //! Used only in assignPropertySet() to check whether we already have the set assigned + QCString m_currentObjectName; + //QGuardedPtr<KoProperty::Set> m_propertySet; +}; + +#endif diff --git a/kexi/plugins/forms/kexidbfactory.cpp b/kexi/plugins/forms/kexidbfactory.cpp new file mode 100644 index 00000000..4ab05d76 --- /dev/null +++ b/kexi/plugins/forms/kexidbfactory.cpp @@ -0,0 +1,713 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2006 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 <qpopupmenu.h> +#include <qscrollview.h> +#include <qcursor.h> +#include <qpainter.h> +#include <qstyle.h> + +#include <kgenericfactory.h> +#include <klocale.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kactioncollection.h> +#include <kstdaction.h> + +#include <formeditor/container.h> +#include <formeditor/form.h> +#include <formeditor/formIO.h> +#include <formeditor/formmanager.h> +#include <formeditor/objecttree.h> +#include <formeditor/utils.h> +#include <kexidb/utils.h> +#include <kexidb/connection.h> +#include <kexipart.h> +#include <formeditor/widgetlibrary.h> +#include <kexigradientwidget.h> +#include <keximainwindow.h> +#include <kexiutils/utils.h> +#include <widget/kexicustompropertyfactory.h> +#include <widget/utils/kexicontextmenuutils.h> + +#include "kexiformview.h" +#include "widgets/kexidbautofield.h" +#include "widgets/kexidbcheckbox.h" +#include "widgets/kexidbimagebox.h" +//#include "widgets/kexidbdoublespinbox.h" +//#include "widgets/kexidbintspinbox.h" +#include "widgets/kexiframe.h" +#include "widgets/kexidblabel.h" +#include "widgets/kexidblineedit.h" +#include "widgets/kexidbtextedit.h" +#include "widgets/kexidbcombobox.h" +#include "widgets/kexipushbutton.h" +#include "widgets/kexidbform.h" +#include "widgets/kexidbsubform.h" +#include "kexidataawarewidgetinfo.h" + +#include "kexidbfactory.h" +#include <core/kexi.h> + + +////////////////////////////////////////// + +KexiDBFactory::KexiDBFactory(QObject *parent, const char *name, const QStringList &) + : KFormDesigner::WidgetFactory(parent, name) +{ + KFormDesigner::WidgetInfo *wi; + wi = new KexiDataAwareWidgetInfo(this); + wi->setPixmap("form"); + wi->setClassName("KexiDBForm"); + wi->setName(i18n("Form")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "form")); + wi->setDescription(i18n("A data-aware form widget")); + addClass(wi); + +#ifndef KEXI_NO_SUBFORM + wi = new KexiDataAwareWidgetInfo(this); + wi->setPixmap("subform"); + wi->setClassName("KexiDBSubForm"); + wi->addAlternateClassName("KexiSubForm", true/*override*/); //older + wi->setName(i18n("Sub Form")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "subForm")); + wi->setDescription(i18n("A form widget included in another Form")); + wi->setAutoSyncForProperty( "formName", false ); + addClass(wi); +#endif + + // inherited + wi = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KLineEdit"); + wi->setPixmap("lineedit"); + wi->setClassName("KexiDBLineEdit"); + wi->addAlternateClassName("QLineEdit", true/*override*/); + wi->addAlternateClassName("KLineEdit", true/*override*/); + wi->setIncludeFileName("klineedit.h"); + wi->setName(i18n("Text Box")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "textBox")); + wi->setDescription(i18n("A widget for entering and displaying text")); + addClass(wi); + + // inherited + wi = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KTextEdit"); + wi->setPixmap("textedit"); + wi->setClassName("KexiDBTextEdit"); + wi->addAlternateClassName("QTextEdit", true/*override*/); + wi->addAlternateClassName("KTextEdit", true/*override*/); + wi->setIncludeFileName("ktextedit.h"); + wi->setName(i18n("Text Editor")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "textEditor")); + wi->setDescription(i18n("A multiline text editor")); + addClass(wi); + + wi = new KFormDesigner::WidgetInfo( + this, "containers", "QFrame" /*we're inheriting to get i18n'd strings already translated there*/); + wi->setPixmap("frame"); + wi->setClassName("KexiFrame"); + wi->addAlternateClassName("QFrame", true/*override*/); + wi->setName(i18n("Frame")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "frame")); + wi->setDescription(i18n("A simple frame widget")); + addClass(wi); + + wi = new KexiDataAwareWidgetInfo( + this, "stdwidgets", "QLabel" /*we're inheriting to get i18n'd strings already translated there*/); + wi->setPixmap("label"); + wi->setClassName("KexiDBLabel"); + wi->addAlternateClassName("QLabel", true/*override*/); + wi->addAlternateClassName("KexiLabel", true/*override*/); //older + wi->setName(i18n("Text Label", "Label")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "label")); + wi->setDescription(i18n("A widget for displaying text")); + addClass(wi); + +#ifndef KEXI_NO_IMAGEBOX_WIDGET + wi = new KexiDataAwareWidgetInfo( + this, "stdwidgets", "KexiPictureLabel" /*we're inheriting to get i18n'd strings already translated there*/); + wi->setPixmap("pixmaplabel"); + wi->setClassName("KexiDBImageBox"); + wi->addAlternateClassName("KexiPictureLabel", true/*override*/); + wi->addAlternateClassName("KexiImageBox", true/*override*/); //older + wi->setName(i18n("Image Box")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "image")); + wi->setDescription(i18n("A widget for displaying images")); +// wi->setCustomTypeForProperty("pixmapData", KexiCustomPropertyFactory::PixmapData); + wi->setCustomTypeForProperty("pixmapId", KexiCustomPropertyFactory::PixmapId); + addClass(wi); + + setInternalProperty("KexiDBImageBox", "dontStartEditingOnInserting", "1"); +// setInternalProperty("KexiDBImageBox", "forceShowAdvancedProperty:pixmap", "1"); +#endif + +#ifdef KEXI_DB_COMBOBOX_WIDGET + wi = new KexiDataAwareWidgetInfo( + this, "stdwidgets", "KComboBox" /*we're inheriting to get i18n'd strings already translated there*/); + wi->setPixmap("combo"); + wi->setClassName("KexiDBComboBox"); + wi->addAlternateClassName("KComboBox", true/*override*/); + wi->setName(i18n("Combo Box")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "comboBox")); + wi->setDescription(i18n("A combo box widget")); + addClass(wi); +#endif + + wi = new KexiDataAwareWidgetInfo(this, "stdwidgets", "QCheckBox"); + wi->setPixmap("check"); + wi->setClassName("KexiDBCheckBox"); + wi->addAlternateClassName("QCheckBox", true/*override*/); + wi->setName(i18n("Check Box")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "checkBox")); + wi->setDescription(i18n("A check box with text label")); + addClass(wi); + +#ifndef KEXI_NO_AUTOFIELD_WIDGET + wi = new KexiDataAwareWidgetInfo(this); + wi->setPixmap("autofield"); + wi->setClassName("KexiDBAutoField"); + wi->addAlternateClassName("KexiDBFieldEdit", true/*override*/); //older + wi->setName(i18n("Auto Field")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters", "autoField")); + wi->setDescription(i18n("A widget containing an automatically selected editor " + "and a label to edit the value of a database field of any type.")); + addClass(wi); +#endif + +/* +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + KexiDataAwareWidgetInfo *wDate = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KDateWidget"); +#else + KexiDataAwareWidgetInfo *wDate = new KexiDataAwareWidgetInfo(this, "stdwidgets", "QDateEdit"); +#endif + wDate->setPixmap("dateedit"); + wDate->setClassName("KexiDBDateEdit"); + wDate->addAlternateClassName("QDateEdit", true);//override + wDate->addAlternateClassName("KDateWidget", true);//override + wDate->setName(i18n("Date Widget")); + wDate->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dateWidget")); + wDate->setDescription(i18n("A widget to input and display a date")); + addClass(wDate); + +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + KexiDataAwareWidgetInfo *wTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KTimeWidget"); +#else + KexiDataAwareWidgetInfo *wTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "QTimeEdit"); +#endif + wTime->setPixmap("timeedit"); + wTime->setClassName("KexiDBTimeEdit"); + wTime->addAlternateClassName("QTimeEdit", true);//override + wTime->addAlternateClassName("KTimeWidget", true);//override + wTime->setName(i18n("Time Widget")); + wTime->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "timeWidget")); + wTime->setDescription(i18n("A widget to input and display a time")); + addClass(wTime); + +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,9) + KexiDataAwareWidgetInfo *wDateTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KDateTimeWidget"); +#else + KexiDataAwareWidgetInfo *wDateTime = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KDateTimeWidget"); +#endif + wDateTime->setPixmap("datetimeedit"); + wDateTime->setClassName("KexiDBDateTimeEdit"); + wDateTime->addAlternateClassName("QDateTimeEdit", true);//override + wDateTime->addAlternateClassName("KDateTimeWidget", true);//override + wDateTime->setName(i18n("Date/Time Widget")); + wDateTime->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dateTimeWidget")); + wDateTime->setDescription(i18n("A widget to input and display a date and time")); + addClass(wDateTime); +*/ + +/* KexiDataAwareWidgetInfo *wIntSpinBox = new KexiDataAwareWidgetInfo(this, "stdwidgets", "KIntSpinBox"); + wIntSpinBox->setPixmap("spin"); + wIntSpinBox->setClassName("KexiDBIntSpinBox"); + wIntSpinBox->addAlternateClassName("QSpinBox", true); + wIntSpinBox->addAlternateClassName("KIntSpinBox", true); + wIntSpinBox->setName(i18n("Integer Number Spin Box")); + wIntSpinBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "intSpinBox")); + wIntSpinBox->setDescription(i18n("A spin box widget to input and display integer numbers")); + addClass(wIntSpinBox); + + KexiDataAwareWidgetInfo *wDoubleSpinBox = new KexiDataAwareWidgetInfo(this, "stdwidgets"); + wDoubleSpinBox->setPixmap("spin"); + wDoubleSpinBox->setClassName("KexiDBDoubleSpinBox"); + wDoubleSpinBox->addAlternateClassName("KDoubleSpinBox", true); + wDoubleSpinBox->setName(i18n("Floating-point Number Spin Box")); + wDoubleSpinBox->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. It must _not_ contain white spaces and non latin1 characters.", "dblSpinBox")); + wDoubleSpinBox->setDescription(i18n("A spin box widget to input and display floating-point numbers")); + addClass(wDoubleSpinBox);*/ + + // inherited + wi = new KFormDesigner::WidgetInfo( + this, "stdwidgets", "KPushButton"); + wi->addAlternateClassName("KexiPushButton"); + wi->setName(i18n("Command Button")); + wi->setNamePrefix( + i18n("Widget name. This string will be used to name widgets of this class. " + "It must _not_ contain white spaces and non latin1 characters.", "button")); + wi->setDescription(i18n("A command button to execute actions")); + addClass(wi); + + m_propDesc["dataSource"] = i18n("Data Source"); + m_propDesc["formName"] = i18n("Form Name"); + m_propDesc["onClickAction"] = i18n("On Click"); + m_propDesc["onClickActionOption"] = i18n("On Click Option"); + m_propDesc["autoTabStops"] = i18n("Auto Tab Order"); + m_propDesc["shadowEnabled"] = i18n("Shadow Enabled"); + m_propDesc["on"] = i18n("On: button", "On"); + + m_propDesc["widgetType"] = i18n("Editor Type"); + //for autofield's type: inherit i18n from KexiDB + m_propValDesc["Auto"] = i18n("AutoField editor's type", "Auto"); + m_propValDesc["Text"] = KexiDB::Field::typeName(KexiDB::Field::Text); + m_propValDesc["Integer"] = KexiDB::Field::typeName(KexiDB::Field::Integer); + m_propValDesc["Double"] = KexiDB::Field::typeName(KexiDB::Field::Double); + m_propValDesc["Boolean"] = KexiDB::Field::typeName(KexiDB::Field::Boolean); + m_propValDesc["Date"] = KexiDB::Field::typeName(KexiDB::Field::Date); + m_propValDesc["Time"] = KexiDB::Field::typeName(KexiDB::Field::Time); + m_propValDesc["DateTime"] = KexiDB::Field::typeName(KexiDB::Field::DateTime); + m_propValDesc["MultiLineText"] = i18n("AutoField editor's type", "Multiline Text"); + m_propValDesc["ComboBox"] = i18n("AutoField editor's type", "Drop-Down List"); + m_propValDesc["Image"] = i18n("AutoField editor's type", "Image"); + +// m_propDesc["labelCaption"] = i18n("Label Text"); + m_propDesc["autoCaption"] = i18n("Auto Label"); + m_propDesc["foregroundLabelColor"] = i18n("Label Text Color"); + m_propDesc["backgroundLabelColor"] = i18n("(a property name, keep the text narrow!)", + "Label Background\nColor"); + + m_propDesc["labelPosition"] = i18n("Label Position"); + m_propValDesc["Left"] = i18n("Label Position", "Left"); + m_propValDesc["Top"] = i18n("Label Position", "Top"); + m_propValDesc["NoLabel"] = i18n("Label Position", "No Label"); + + m_propDesc["sizeInternal"] = i18n("Size"); +// m_propDesc["pixmap"] = i18n("Image"); + m_propDesc["pixmapId"] = i18n("Image"); + m_propDesc["scaledContents"] = i18n("Scaled Contents"); + m_propDesc["keepAspectRatio"] = i18n("Keep Aspect Ratio (short)", "Keep Ratio"); + + //hide classes that are replaced by db-aware versions + hideClass("KexiPictureLabel"); + hideClass("KComboBox"); + + //used in labels, frames... + m_propDesc["frameColor"] = i18n("Frame Color"); + m_propDesc["dropDownButtonVisible"] = + i18n("Drop-Down Button for Image Box Visible (a property name, keep the text narrow!)", + "Drop-Down\nButton Visible"); + + //for checkbox + m_propValDesc["TristateDefault"] = i18n("Tristate checkbox, default", "Default"); + m_propValDesc["TristateOn"] = i18n("Tristate checkbox, yes", "Yes"); + m_propValDesc["TristateOff"] = i18n("Tristate checkbox, no", "No"); + + //for combobox + m_propDesc["editable"] = i18n("Editable combobox", "Editable"); +} + +KexiDBFactory::~KexiDBFactory() +{ +} + +QWidget* +KexiDBFactory::createWidget(const QCString &c, QWidget *p, const char *n, + KFormDesigner::Container *container, int options) +{ + kexipluginsdbg << "KexiDBFactory::createWidget() " << this << endl; + + QWidget *w=0; + QString text( container->form()->library()->textForWidgetName(n, c) ); + const bool designMode = options & KFormDesigner::WidgetFactory::DesignViewMode; + + if(c == "KexiDBSubForm") + w = new KexiDBSubForm(container->form(), p, n); + else if(c == "KexiDBLineEdit") + { + w = new KexiDBLineEdit(p, n); + if (designMode) + w->setCursor(QCursor(Qt::ArrowCursor)); + } + else if(c == "KexiDBTextEdit") + { + w = new KexiDBTextEdit(p, n); + if (designMode) + w->setCursor(QCursor(Qt::ArrowCursor)); + } + else if(c == "QFrame" || c == "KexiFrame") + { + w = new KexiFrame(p, n); + new KFormDesigner::Container(container, w, container); + } + else if(c == "KexiDBLabel") + w = new KexiDBLabel(text, p, n); +#ifndef KEXI_NO_IMAGEBOX_WIDGET + else if(c == "KexiDBImageBox") { + w = new KexiDBImageBox(designMode, p, n); + connect(w, SIGNAL(idChanged(long)), this, SLOT(slotImageBoxIdChanged(long))); + } +#endif +#ifndef KEXI_NO_AUTOFIELD_WIDGET + else if(c == "KexiDBAutoField") + w = new KexiDBAutoField(p, n, designMode); +#endif + else if(c == "KexiDBCheckBox") + w = new KexiDBCheckBox(text, p, n); + else if(c == "KexiDBComboBox") + w = new KexiDBComboBox(p, n, designMode); +/* else if(c == "KexiDBTimeEdit") + w = new KexiDBTimeEdit(QTime::currentTime(), p, n); + else if(c == "KexiDBDateEdit") + w = new KexiDBDateEdit(QDate::currentDate(), p, n); + else if(c == "KexiDBDateTimeEdit") + w = new KexiDBDateTimeEdit(QDateTime::currentDateTime(), p, n);*/ +// else if(c == "KexiDBIntSpinBox") +// w = new KexiDBIntSpinBox(p, n); +// else if(c == "KexiDBDoubleSpinBox") +// w = new KexiDBDoubleSpinBox(p, n); + else if(c == "KPushButton" || c == "KexiPushButton") + w = new KexiPushButton(text, p, n); + + return w; +} + +bool +KexiDBFactory::createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *) +{ + if(classname == "QPushButton" || classname == "KPushButton" || classname == "KexiPushButton") + { +/*! @todo also call createMenuActions() for inherited factory! */ + m_assignAction->plug( menu ); + return true; + } + else if(classname == "KexiDBImageBox") + { + KexiDBImageBox *imageBox = static_cast<KexiDBImageBox*>(w); + imageBox->contextMenu()->updateActionsAvailability(); + KActionCollection *ac = imageBox->contextMenu()->actionCollection(); + KPopupMenu *subMenu = new KPopupMenu(); +//! @todo make these actions undoable/redoable + menu->insertItem(i18n("&Image"), subMenu); + ac->action("insert")->plug(subMenu); + ac->action("file_save_as")->plug(subMenu); + subMenu->insertSeparator(); + ac->action("edit_cut")->plug(subMenu); + ac->action("edit_copy")->plug(subMenu); + ac->action("edit_paste")->plug(subMenu); + ac->action("delete")->plug(subMenu); + if (ac->action("properties")) { + subMenu->insertSeparator(); + ac->action("properties")->plug(subMenu); + } + } + return false; +} + +void +KexiDBFactory::createCustomActions(KActionCollection* col) +{ + //this will create shared instance action for design mode (special collection is provided) + m_assignAction = new KAction( i18n("&Assign Action..."), SmallIconSet("form_action"), + 0, 0, 0, col, "widget_assign_action"); +} + +bool +KexiDBFactory::startEditing(const QCString &classname, QWidget *w, KFormDesigner::Container *container) +{ + m_container = container; + if(classname == "KexiDBLineEdit") + { +//! @todo this code should not be copied here but +//! just inherited StdWidgetFactory::clearWidgetContent() should be called + KLineEdit *lineedit = static_cast<KLineEdit*>(w); + createEditor(classname, lineedit->text(), lineedit, container, + lineedit->geometry(), lineedit->alignment(), true); + return true; + } + if(classname == "KexiDBTextEdit") + { +//! @todo this code should not be copied here but +//! just inherited StdWidgetFactory::clearWidgetContent() should be called + KTextEdit *textedit = static_cast<KTextEdit*>(w); + createEditor(classname, textedit->text(), textedit, container, + textedit->geometry(), textedit->alignment(), true, true); + //copy a few properties + KTextEdit *ed = dynamic_cast<KTextEdit *>( editor(w) ); + ed->setWrapPolicy(textedit->wrapPolicy()); + ed->setWordWrap(textedit->wordWrap()); + ed->setTabStopWidth(textedit->tabStopWidth()); + ed->setWrapColumnOrWidth(textedit->wrapColumnOrWidth()); + ed->setLinkUnderline(textedit->linkUnderline()); + ed->setTextFormat(textedit->textFormat()); + ed->setHScrollBarMode(textedit->hScrollBarMode()); + ed->setVScrollBarMode(textedit->vScrollBarMode()); + return true; + } + else if ( classname == "KexiDBLabel" ) { + KexiDBLabel *label = static_cast<KexiDBLabel*>(w); + m_widget = w; + if(label->textFormat() == RichText) + { + QString text = label->text(); + if ( editRichText( label, text ) ) + { + changeProperty( "textFormat", "RichText", container->form() ); + changeProperty( "text", text, container->form() ); + } + + if ( classname == "KexiDBLabel" ) + w->resize(w->sizeHint()); + } + else + { + createEditor(classname, label->text(), label, container, + label->geometry(), label->alignment(), + false, label->alignment() & Qt::WordBreak /*multiline*/); + } + return true; + } + else if (classname == "KexiDBSubForm") { + // open the form in design mode + KexiMainWindow *mainWin = KexiUtils::findParent<KexiMainWindow>(w, "KexiMainWindow"); + KexiDBSubForm *subform = static_cast<KexiDBSubForm*>(w); + if(mainWin) { + bool openingCancelled; + mainWin->openObject("kexi/form", subform->formName(), Kexi::DesignViewMode, + openingCancelled); + } + return true; + } +#if 0 + else if( (classname == "KexiDBDateEdit") || (classname == "KexiDBDateTimeEdit") || (classname == "KexiDBTimeEdit") + /*|| (classname == "KexiDBIntSpinBox") || (classname == "KexiDBDoubleSpinBox")*/ ) { + disableFilter(w, container); + return true; + } +#endif + else if(classname == "KexiDBAutoField") { + if(static_cast<KexiDBAutoField*>(w)->hasAutoCaption()) + return false; // caption is auto, abort editing + QLabel *label = static_cast<KexiDBAutoField*>(w)->label(); + createEditor(classname, label->text(), label, container, label->geometry(), label->alignment()); + return true; + } + else if (classname == "KexiDBCheckBox") { + KexiDBCheckBox *cb = static_cast<KexiDBCheckBox*>(w); + QRect r( cb->geometry() ); + r.setLeft( r.left() + 2 + cb->style().subRect( QStyle::SR_CheckBoxIndicator, cb ).width() ); + createEditor(classname, cb->text(), cb, container, r, Qt::AlignAuto); + return true; + } + else if(classname == "KexiDBImageBox") { + KexiDBImageBox *image = static_cast<KexiDBImageBox*>(w); + image->insertFromFile(); + return true; + } + return false; +} + +bool +KexiDBFactory::previewWidget(const QCString &, QWidget *, KFormDesigner::Container *) +{ + return false; +} + +bool +KexiDBFactory::clearWidgetContent(const QCString & /*classname*/, QWidget *w) +{ +//! @todo this code should not be copied here but +//! just inherited StdWidgetFactory::clearWidgetContent() should be called + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>(w); + if(iface) + iface->clear(); + return true; +} + +QValueList<QCString> +KexiDBFactory::autoSaveProperties(const QCString & /*classname*/) +{ + QValueList<QCString> lst; +// if(classname == "KexiDBSubForm") + //lst << "formName"; +// if(classname == "KexiDBLineEdit") +// lst += "dataSource"; +// if(classname == "KexiDBAutoField") +// lst << "labelCaption"; + return lst; +} + +bool +KexiDBFactory::isPropertyVisibleInternal(const QCString& classname, QWidget *w, + const QCString& property, bool isTopLevel) +{ + //general + if (property=="dataSource" || property=="dataSourceMimeType") { + return false; //force + } + + bool ok = true; + + if(classname == "KexiPushButton") { + ok = property!="isDragEnabled" +#ifdef KEXI_NO_UNFINISHED + && property!="onClickAction" /*! @todo reenable */ + && property!="onClickActionOption" /*! @todo reenable */ + && property!="iconSet" /*! @todo reenable */ + && property!="stdItem" /*! @todo reenable stdItem */ +#endif + ; + } + else if(classname == "KexiDBLineEdit") + ok = property!="urlDropsEnabled" + && property!="vAlign" +#ifdef KEXI_NO_UNFINISHED + && property!="inputMask" + && property!="maxLength" //!< we may want to integrate this with db schema +#endif + ; + else if(classname == "KexiDBComboBox") + ok = property!="autoCaption" + && property!="labelPosition" + && property!="widgetType" + && property!="fieldTypeInternal" + && property!="fieldCaptionInternal"; //hide properties that come with KexiDBAutoField + else if(classname == "KexiDBTextEdit") + ok = property!="undoDepth" + && property!="undoRedoEnabled" //always true! + && property!="dragAutoScroll" //always true! + && property!="overwriteMode" //always false! + && property!="resizePolicy" + && property!="autoFormatting" //too complex +#ifdef KEXI_NO_UNFINISHED + && property!="paper" +#endif + ; + else if(classname == "KexiDBSubForm") + ok = property!="dragAutoScroll" + && property!="resizePolicy" + && property!="focusPolicy"; + else if(classname == "KexiDBForm") + ok = property!="iconText" + && property!="geometry" /*nonsense for toplevel widget; for size, "size" property is used*/; + else if(classname == "KexiDBLabel") + ok = property!="focusPolicy"; + else if(classname == "KexiDBAutoField") { + if (!isTopLevel && property=="caption") + return true; //force + if (property=="fieldTypeInternal" || property=="fieldCaptionInternal" +//! @todo unhide in 2.0 + || property=="widgetType") + return false; + ok = property!="text"; /* "text" is not needed as "caption" is used instead */ + } + else if (classname == "KexiDBImageBox") { + ok = property!="font" && property!="wordbreak"; + } + else if(classname == "KexiDBCheckBox") { + //hide text property if the widget is a child of an autofield beause there's already "caption" for this purpose + if (property=="text" && w && dynamic_cast<KFormDesigner::WidgetWithSubpropertiesInterface*>(w->parentWidget())) + return false; + ok = property!="autoRepeat"; + } + + return ok && WidgetFactory::isPropertyVisibleInternal(classname, w, property, isTopLevel); +} + +bool +KexiDBFactory::propertySetShouldBeReloadedAfterPropertyChange(const QCString& classname, + QWidget *w, const QCString& property) +{ + Q_UNUSED(classname); + Q_UNUSED(w); + if (property=="fieldTypeInternal" || property=="widgetType") + return true; + return false; +} + +bool +KexiDBFactory::changeText(const QString &text) +{ + KFormDesigner::Form *form = m_container ? m_container->form() : 0; + if (!form) + return false; + if (!form->selectedWidget()) + return false; + QCString n( form->selectedWidget()->className() ); +// QWidget *w = WidgetFactory::widget(); + if(n == "KexiDBAutoField") { + changeProperty("caption", text, form); + return true; + } + //! \todo check field's geometry + return false; +} + +void +KexiDBFactory::resizeEditor(QWidget *editor, QWidget *w, const QCString &classname) +{ + //QSize s = widget->size(); + //QPoint p = widget->pos(); + + if(classname == "KexiDBAutoField") + editor->setGeometry( static_cast<KexiDBAutoField*>(w)->label()->geometry() ); +} + +void +KexiDBFactory::slotImageBoxIdChanged(KexiBLOBBuffer::Id_t id) +{ +//old KexiFormView *formView = KexiUtils::findParent<KexiFormView>((QWidget*)m_widget, "KexiFormView"); + + // (js) heh, porting to KFormDesigner::FormManager::self() singleton took me entire day of work... + KFormDesigner::Form *form = KFormDesigner::FormManager::self()->activeForm(); + KexiFormView *formView = form ? KexiUtils::findParent<KexiFormView>((QWidget*)form->widget(), "KexiFormView") : 0; + if (formView) { + changeProperty("pixmapId", (uint)/*! @todo unsafe */id, form); +//old formView->setUnsavedLocalBLOB(m_widget, id); + formView->setUnsavedLocalBLOB(form->selectedWidget(), id); + } +} + +KFORMDESIGNER_WIDGET_FACTORY(KexiDBFactory, kexidbwidgets) + +#include "kexidbfactory.moc" diff --git a/kexi/plugins/forms/kexidbfactory.h b/kexi/plugins/forms/kexidbfactory.h new file mode 100644 index 00000000..6064a001 --- /dev/null +++ b/kexi/plugins/forms/kexidbfactory.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2006 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 KEXIDBFACTORY_H +#define KEXIDBFACTORY_H + +#include <formeditor/widgetfactory.h> + +class KAction; + +namespace KFormDesigner { + class Form; + class FormManager; +} + +//! Kexi Factory (DB widgets + subform) +class KexiDBFactory : public KFormDesigner::WidgetFactory +{ + Q_OBJECT + + public: + KexiDBFactory(QObject *parent, const char *name, const QStringList &args); + virtual ~KexiDBFactory(); + + virtual QWidget *createWidget(const QCString &classname, QWidget *parent, const char *name, + KFormDesigner::Container *container, int options = DefaultOptions ); + + virtual void createCustomActions(KActionCollection* col); + virtual bool createMenuActions(const QCString &classname, QWidget *w, QPopupMenu *menu, + KFormDesigner::Container *container); + virtual bool startEditing(const QCString &classname, QWidget *w, KFormDesigner::Container *container); + virtual bool previewWidget(const QCString &, QWidget *, KFormDesigner::Container *); + virtual bool clearWidgetContent(const QCString &classname, QWidget *w); + + //virtual void saveSpecialProperty(const QString &classname, const QString &name, const QVariant &value, QWidget *w, + //QDomElement &parentNode, QDomDocument &parent) {} + //virtual void readSpecialProperty(const QCString &classname, QDomElement &node, QWidget *w, KFormDesigner::ObjectTreeItem *item) {} + virtual QValueList<QCString> autoSaveProperties(const QCString &classname); + + protected slots: + void slotImageBoxIdChanged(long id); /*KexiBLOBBuffer::Id_t*/ + + protected: + virtual bool changeText(const QString &newText); + virtual void resizeEditor(QWidget *editor, QWidget *widget, const QCString &classname); + + virtual bool isPropertyVisibleInternal(const QCString& classname, QWidget *w, + const QCString& property, bool isTopLevel); + + //! Sometimes property sets should be reloaded when a given property value changed. + virtual bool propertySetShouldBeReloadedAfterPropertyChange(const QCString& classname, QWidget *w, + const QCString& property); + + KAction* m_assignAction; +}; + +#endif diff --git a/kexi/plugins/forms/kexidbtextwidgetinterface.cpp b/kexi/plugins/forms/kexidbtextwidgetinterface.cpp new file mode 100644 index 00000000..47eabe9d --- /dev/null +++ b/kexi/plugins/forms/kexidbtextwidgetinterface.cpp @@ -0,0 +1,71 @@ +/* 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. +*/ + +#include "kexidbtextwidgetinterface.h" +#include "kexiformdataiteminterface.h" +#include <kexidb/queryschema.h> +#include <kexiutils/utils.h> +#include <qframe.h> +#include <qpainter.h> + +KexiDBTextWidgetInterface::KexiDBTextWidgetInterface() + : m_autonumberDisplayParameters(0) +{ +} + +KexiDBTextWidgetInterface::~KexiDBTextWidgetInterface() +{ + delete m_autonumberDisplayParameters; +} + +void KexiDBTextWidgetInterface::setColumnInfo(KexiDB::QueryColumnInfo* cinfo, QWidget *w) +{ + if (cinfo->field->isAutoIncrement()) { + if (!m_autonumberDisplayParameters) + m_autonumberDisplayParameters = new KexiDisplayUtils::DisplayParameters(); + KexiDisplayUtils::initDisplayForAutonumberSign(*m_autonumberDisplayParameters, w); + } +} + +void KexiDBTextWidgetInterface::paint( QFrame *w, QPainter* p, bool textIsEmpty, int alignment, bool hasFocus ) +{ + KexiFormDataItemInterface *dataItemIface = dynamic_cast<KexiFormDataItemInterface*>(w); + KexiDB::QueryColumnInfo *columnInfo = dataItemIface ? dataItemIface->columnInfo() : 0; + if (columnInfo && columnInfo->field && dataItemIface->cursorAtNewRow() && textIsEmpty) { + const int margin = w->lineWidth() + w->midLineWidth(); + if (columnInfo->field->isAutoIncrement() && m_autonumberDisplayParameters) { + if (w->hasFocus()) { + p->setPen( + KexiUtils::blendedColors( + m_autonumberDisplayParameters->textColor, w->palette().active().base(), 1, 3)); + } + KexiDisplayUtils::paintAutonumberSign(*m_autonumberDisplayParameters, p, + 2 + margin + w->margin(), margin, w->width() - margin*2 -2-2, + w->height() - margin*2 -2, alignment, hasFocus); + } + } +} + +void KexiDBTextWidgetInterface::event( QEvent * e, QWidget *w, bool textIsEmpty ) +{ + if (e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut) { + if (m_autonumberDisplayParameters && textIsEmpty) + w->repaint(); + } +} diff --git a/kexi/plugins/forms/kexidbtextwidgetinterface.h b/kexi/plugins/forms/kexidbtextwidgetinterface.h new file mode 100644 index 00000000..ca10fc4e --- /dev/null +++ b/kexi/plugins/forms/kexidbtextwidgetinterface.h @@ -0,0 +1,53 @@ +/* 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. +*/ + +#ifndef KexiDBTextWidgetInterface_H +#define KexiDBTextWidgetInterface_H + +#include <widget/utils/kexidisplayutils.h> + +namespace KexiDB { + class QueryColumnInfo; +} +class QFrame; + +//! @short An interface providing common text editor's functionality +/*! Widgets (e.g. KexiDBLineEdit, KexiDBTextEdit) implementing KexiFormDataItemInterface + use this interface to customize painting and data handling. */ +class KEXIFORMUTILS_EXPORT KexiDBTextWidgetInterface +{ + public: + KexiDBTextWidgetInterface(); + ~KexiDBTextWidgetInterface(); + + //! Called from KexiFormDataItemInterface::setColumnInfo(KexiDB::QueryColumnInfo* cinfo) implementation. + void setColumnInfo(KexiDB::QueryColumnInfo* cinfo, QWidget *w); + + //! Called from paintEvent( QPaintEvent *pe ) method of the data aware widget. + void paint( QFrame *w, QPainter *p, bool textIsEmpty, int alignment, bool hasFocus ); + + //! Called from event( QEvent * e ) method of the data aware widget. + void event( QEvent * e, QWidget *w, bool textIsEmpty ); + + protected: + //! parameters for displaying autonumber sign + KexiDisplayUtils::DisplayParameters *m_autonumberDisplayParameters; +}; + +#endif diff --git a/kexi/plugins/forms/kexiformdataiteminterface.cpp b/kexi/plugins/forms/kexiformdataiteminterface.cpp new file mode 100644 index 00000000..c87a2dab --- /dev/null +++ b/kexi/plugins/forms/kexiformdataiteminterface.cpp @@ -0,0 +1,68 @@ +/* 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. + */ + +#include "kexiformdataiteminterface.h" +#include "kexiformscrollview.h" +#include <kexidb/queryschema.h> +#include <kexiutils/utils.h> + +KexiFormDataItemInterface::KexiFormDataItemInterface() + : KexiDataItemInterface() + , m_columnInfo(0) + , m_displayParametersForEnteredValue(0) + , m_displayParametersForDefaultValue(0) + , m_displayDefaultValue(false) +{ +} + +KexiFormDataItemInterface::~KexiFormDataItemInterface() +{ + delete m_displayParametersForEnteredValue; + delete m_displayParametersForDefaultValue; +} + +void KexiFormDataItemInterface::undoChanges() +{ +// m_disable_signalValueChanged = true; + setValueInternal(QString::null, false); +// m_disable_signalValueChanged = false; +} + +KexiDB::Field* KexiFormDataItemInterface::field() const +{ + return m_columnInfo ? m_columnInfo->field : 0; +} + +void KexiFormDataItemInterface::setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue) +{ + m_displayDefaultValue = displayDefaultValue; + if (!m_displayParametersForDefaultValue) { + m_displayParametersForEnteredValue = new KexiDisplayUtils::DisplayParameters(widget); + m_displayParametersForDefaultValue = new KexiDisplayUtils::DisplayParameters(); + KexiDisplayUtils::initDisplayForDefaultValue(*m_displayParametersForDefaultValue, widget); + } +} + +void KexiFormDataItemInterface::cancelEditor() +{ + QWidget *parentWidget = dynamic_cast<QWidget*>(this)->parentWidget(); + KexiFormScrollView* view = KexiUtils::findParent<KexiFormScrollView>(parentWidget, "KexiFormScrollView"); + if (view) + view->cancelEditor(); +} diff --git a/kexi/plugins/forms/kexiformdataiteminterface.h b/kexi/plugins/forms/kexiformdataiteminterface.h new file mode 100644 index 00000000..99d20db4 --- /dev/null +++ b/kexi/plugins/forms/kexiformdataiteminterface.h @@ -0,0 +1,145 @@ +/* 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. + */ + +#ifndef KEXIFORMDATAITEMINTERFACE_H +#define KEXIFORMDATAITEMINTERFACE_H + +#include <widget/utils/kexidisplayutils.h> +#include <kexidataiteminterface.h> +#include <qwidget.h> + +namespace KexiDB { + class Field; +} + +//! An interface for declaring form widgets to be data-aware. +class KEXIFORMUTILS_EXPORT KexiFormDataItemInterface : public KexiDataItemInterface +{ + public: + KexiFormDataItemInterface(); + virtual ~KexiFormDataItemInterface(); + + //! \return the name of the data source for this widget. + //! Data source usually means here a table or query, a field name or an expression. + inline QString dataSource() const { return m_dataSource; } + + //! Sets the name of the data source for this widget. + //! Data source usually means here a table or query or field name name. + inline void setDataSource(const QString &ds) { m_dataSource = ds; } + + /*! \return the mime type of the data source for this widget. + Data source mime type means here types like "kexi/table" or "kexi/query" + in.the data source is set to object (as within form or subform) or is empty + if the data source is set to table field or query column. */ + inline QCString dataSourceMimeType() const { return m_dataSourceMimeType; } + + /*! Sets the mime type of the data source for this widget. + Data source usually means here a "kexi/table" or "kexi/query". + @see dataSourceMimeType() */ + inline void setDataSourceMimeType(const QCString &ds) { m_dataSourceMimeType = ds; } + + /*! 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'. + You can reimplement this in the widget. Always call the superclass' implementation. + setDisplayDefaultValue(.., false) is called in KexiFormScrollView::valueChanged() + as a response on data change performed by user. */ + virtual void setDisplayDefaultValue(QWidget* widget, bool displayDefaultValue); + + /*! \return true if default value is displayed for this item. */ + virtual bool hasDisplayedDefaultValue() const { return m_displayDefaultValue; } + + /*! Convenience function: casts this item to a QWidget. + Can return 0 if the item is not a QWidget-derived object. */ + virtual QWidget* widget() { return dynamic_cast<QWidget*>(this); } + + /*! Sets 'invalid' state, e.g. a text editor widget should display + text \a displayText and become read only to prevent entering data, + because updating at the database backend is not available. + \a displayText is usually set to something i18n'd like "#NAME?". + Note: that even widgets that usualy do not display texts (e.g. pixmaps) + should display \a displayText too. + */ + virtual void setInvalidState( const QString& displayText ) = 0; + + /*! Changes 'read only' flag, for this widget. + Typically this flag can be passed to a widget itself, + e.g. KLineEdit::setReadOnly(bool). */ + virtual void setReadOnly( bool readOnly ) = 0; + + //! \return database column information for this item + virtual KexiDB::Field* field() const; + + //! \return database column information for this item + virtual KexiDB::QueryColumnInfo* columnInfo() const { return m_columnInfo; } + + /*! Used internally to set database column information. + Reimplement if you need to do additional actions, + e.g. set data validator based on field type. Don't forget about + calling superclass implementation. */ + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo) { m_columnInfo = cinfo; } + + /*! Used internally to set visible database column information. + Reimplemented in KexiDBComboBox: except for combo box, this does nothing. */ + virtual void setVisibleColumnInfo(KexiDB::QueryColumnInfo* cinfo) { Q_UNUSED(cinfo); } + + /*! \return visible database column information for this item. + Except for combo box, this is exactly the same as columnInfo(). */ + virtual KexiDB::QueryColumnInfo* visibleColumnInfo() const { return columnInfo(); } + + /*! Does nothing, because within forms, widgets are always visible. */ + virtual void hideWidget() { } + + /*! Does nothing, because within forms, widgets are always visible. */ + virtual void showWidget() { } + + /*! Undoes changes made to this item - just resets the widget to original value. + Note: This is internal method called by KexiFormScrollView::cancelEditor(). + To cancel editing of the widget's data from the widget's code, + use KexiFormDataItemInterface::cancelEditor(). + Reimplemented in KexiDBComboBox to also revert the visible value (i.e. text) to the original state. + */ + virtual void undoChanges(); + + /* Cancels editing of the widget's data. This method just looks for + the (grand)parent KexiFormScrollView object and calls + KexiFormScrollView::cancelEditor(). */ + void cancelEditor(); + + /*! @internal + Called by top-level form on key press event. + Default implementation does nothing. + Implement this if you want to handle key presses from within the editor widget item. + \return true if \a ke should be accepted by the widget item. + This method is used e.g. in KexiDBImageBox 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) { Q_UNUSED(ke); return false; }; + + protected: + QString m_dataSource; + QCString m_dataSourceMimeType; + KexiDB::QueryColumnInfo* m_columnInfo; + KexiDisplayUtils::DisplayParameters *m_displayParametersForEnteredValue; //!< used in setDisplayDefaultValue() + KexiDisplayUtils::DisplayParameters *m_displayParametersForDefaultValue; //!< used in setDisplayDefaultValue() + bool m_displayDefaultValue : 1; //!< used by setDisplayDefaultValue() + + friend class KexiDBAutoField; +}; + +#endif diff --git a/kexi/plugins/forms/kexiformeventhandler.cpp b/kexi/plugins/forms/kexiformeventhandler.cpp new file mode 100644 index 00000000..01bca201 --- /dev/null +++ b/kexi/plugins/forms/kexiformeventhandler.cpp @@ -0,0 +1,188 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 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 "kexiformeventhandler.h" + +#include <qwidget.h> +#include <qobjectlist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <tableview/kexitableitem.h> +#include <tableview/kexitableviewdata.h> +#include <kexidb/queryschema.h> +#include <keximainwindow.h> +#include <kexidialogbase.h> +#include <kexipart.h> +#include <kexipartinfo.h> +#include <kexipartitem.h> + +KexiFormEventAction::ActionData::ActionData() +{ +} + +bool KexiFormEventAction::ActionData::isEmpty() const +{ + return string.isEmpty(); +} + +KexiPart::Info* KexiFormEventAction::ActionData::decodeString( + QString& actionType, QString& actionArg, bool& ok) const +{ + const int idx = string.find(':'); + ok = false; + if (idx==-1) + return 0; + const QString _actionType = string.left(idx); + const QString _actionArg = string.mid(idx+1); + if (_actionType.isEmpty() || _actionArg.isEmpty()) + return 0; + KexiPart::Info *info = 0; + if (_actionType!="kaction" && _actionType!="currentForm") { + info = Kexi::partManager().infoForMimeType( QString("kexi/%1").arg(_actionType) ); + if (!info) + return 0; + } + actionType = _actionType; + actionArg = _actionArg; + ok = true; + return info; +} + +//------------------------------------- + +KexiFormEventAction::KexiFormEventAction(KexiMainWindow *mainWin, QObject* parent, + const QString& actionName, const QString& objectName, const QString& actionOption) + : KAction(parent), m_mainWin(mainWin), m_actionName(actionName), m_objectName(objectName) + , m_actionOption(actionOption) +{ +} + +KexiFormEventAction::~KexiFormEventAction() +{ +} + +void KexiFormEventAction::activate() +{ + KexiProject* project = m_mainWin->project(); + if (!project) + return; + KexiPart::Part* part = Kexi::partManager().partForMimeType( + QString("kexi/%1").arg(m_actionName) ); + if (!part) + return; + KexiPart::Item* item = project->item( part->info(), m_objectName ); + if (!item) + return; + bool actionCancelled = false; + if (m_actionOption.isEmpty()) { // backward compatibility (good defaults) + if (part->info()->isExecuteSupported()) + part->execute(item, parent()); + else + m_mainWin->openObject(item, Kexi::DataViewMode, actionCancelled); + } + else { +//! @todo react on failure... + if (m_actionOption == "open") + m_mainWin->openObject(item, Kexi::DataViewMode, actionCancelled); + else if (m_actionOption == "execute") + part->execute(item, parent()); + else if (m_actionOption == "print") { + if (part->info()->isPrintingSupported()) + m_mainWin->printItem(item); + } + else if (m_actionOption == "printPreview") { + if (part->info()->isPrintingSupported()) + m_mainWin->printPreviewForItem(item); + } + else if (m_actionOption == "pageSetup") { + if (part->info()->isPrintingSupported()) + m_mainWin->showPageSetupForItem(item); + } + else if (m_actionOption == "exportToCSV" + || m_actionOption == "copyToClipboardAsCSV") + { + if (part->info()->isDataExportSupported()) + m_mainWin->executeCustomActionForObject(item, m_actionOption); + } + else if (m_actionOption == "new") + m_mainWin->newObject( part->info(), actionCancelled ); + else if (m_actionOption == "design") + m_mainWin->openObject(item, Kexi::DesignViewMode, actionCancelled); + else if (m_actionOption == "editText") + m_mainWin->openObject(item, Kexi::TextViewMode, actionCancelled); + else if (m_actionOption == "close") { + tristate res = m_mainWin->closeObject(item); + if (~res) + actionCancelled = true; + } + } +} + +//------------------------------------------ + +KexiFormEventHandler::KexiFormEventHandler() + : m_mainWidget(0) +{ +} + +KexiFormEventHandler::~KexiFormEventHandler() +{ +} + +void KexiFormEventHandler::setMainWidgetForEventHandling(KexiMainWindow *mainWin, QWidget* mainWidget) +{ + m_mainWidget = mainWidget; + if (!m_mainWidget) + return; + + //find widgets whose will work as data items +//! @todo look for other widgets too + QObjectList *l = m_mainWidget->queryList( "KexiPushButton" ); + QObjectListIt it( *l ); + QObject *obj; + for ( ; (obj = it.current()) != 0; ++it ) { + bool ok; + KexiFormEventAction::ActionData data; + data.string = obj->property("onClickAction").toString(); + data.option = obj->property("onClickActionOption").toString(); + if (data.isEmpty()) + continue; + + QString actionType, actionArg; + KexiPart::Info* partInfo = data.decodeString(actionType, actionArg, ok); + if (!ok) + continue; + if (actionType=="kaction" || actionType=="currentForm") { + KAction *action = mainWin->actionCollection()->action( actionArg.latin1() ); + if (!action) + continue; + QObject::disconnect( obj, SIGNAL(clicked()), action, SLOT(activate()) ); //safety + QObject::connect( obj, SIGNAL(clicked()), action, SLOT(activate()) ); + } + else if (partInfo) { //'open or execute' action + KexiFormEventAction* action = new KexiFormEventAction(mainWin, obj, actionType, actionArg, + data.option); + QObject::disconnect( obj, SIGNAL(clicked()), action, SLOT(activate()) ); + QObject::connect( obj, SIGNAL(clicked()), action, SLOT(activate()) ); + } + } + delete l; +} diff --git a/kexi/plugins/forms/kexiformeventhandler.h b/kexi/plugins/forms/kexiformeventhandler.h new file mode 100644 index 00000000..e92e9ff9 --- /dev/null +++ b/kexi/plugins/forms/kexiformeventhandler.h @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 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 KEXIFORMEVENTHANDLER_H +#define KEXIFORMEVENTHANDLER_H + +#include <qwidget.h> +#include <kaction.h> + +class KexiMainWindow; +namespace KexiPart { + class Info; +} + +//! The KexiFormEventHandler class handles events defined within Kexi Forms +/*! For now only "onClickAction" property of Push Button widget is handled: + It's possible to connect this event to predefined global action. + + Note: This interface will be extended in the future! + + @see KexiFormPart::slotAssignAction() + */ +class KEXIFORMUTILS_EXPORT KexiFormEventHandler +{ + public: + KexiFormEventHandler(); + virtual ~KexiFormEventHandler(); + + /*! Sets \a mainWidget to be a main widget for this handler. + Also find widgets having action assigned and connects them + to appropriate actions. + For now, all of them must be KexiPushButton). + \a mainWin is used to get action list. */ + void setMainWidgetForEventHandling(KexiMainWindow *mainWin, QWidget* mainWidget); + + protected: + QWidget *m_mainWidget; +}; + +//! @internal form-level action for handling "on click" actions +class KEXIFORMUTILS_EXPORT KexiFormEventAction : public KAction +{ + public: + //! A structure used in currentActionName() + class KEXIFORMUTILS_EXPORT ActionData + { + public: + ActionData(); + + /*! Decodes action string into action type/action argument parts. + Action string has to be in a form of "actiontype:actionarg" + - Action type is passed to \a actionType on success. Action type can be "kaction" + or any of the part names (see KexiPart::Info::objectName()), e.g. "table", "query", etc. + - Action argument can be an action name in case of "kaction" type or object name + in case of action of type "table", "query", etc. + \a ok is set to true on success and to false on failure. On failure no other + values are passed. + \return part info if action type is "table", "query", etc., or 0 for "kaction" type. */ + KexiPart::Info* decodeString(QString& actionType, QString& actionArg, bool& ok) const; + + //! \return true if the action is empty + bool isEmpty() const; + + QString string; //!< action string with prefix, like "kaction:edit_copy" or "table:<tableName>" + + QString option; //!< option used when name is "table/query/etc.:\<objectName\>" is set; + //!< can be set to "open", "design", "editText", etc. + //!< @see ActionToExecuteListView::showActionsForMimeType() + }; + + KexiFormEventAction(KexiMainWindow *mainWin, QObject* parent, const QString& actionName, + const QString& objectName, const QString& actionOption); + virtual ~KexiFormEventAction(); + + public slots: + //! Activates the action. If the object supports executing (macro, script), + //! it is executed; otherwise (table, query, form,...) it is opened in its data view. + virtual void activate(); + + private: + KexiMainWindow *m_mainWin; + QString m_actionName, m_objectName, m_actionOption; +}; + +#endif diff --git a/kexi/plugins/forms/kexiformhandler.desktop b/kexi/plugins/forms/kexiformhandler.desktop new file mode 100644 index 00000000..3b476daa --- /dev/null +++ b/kexi/plugins/forms/kexiformhandler.desktop @@ -0,0 +1,115 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kexi/Handler + +GenericName=Forms +GenericName[bg]=Формуляри +GenericName[br]=Paperennoù-reol +GenericName[ca]=Formularis +GenericName[cs]=Formuláře +GenericName[cy]=Ffurflenni +GenericName[da]=Formularer +GenericName[de]=Formulare +GenericName[el]=Φόρμες +GenericName[eo]=Formularoj +GenericName[es]=Formularios +GenericName[et]=Vormid +GenericName[eu]=Formularioak +GenericName[fa]=برگهها +GenericName[fi]=Lomakkeet +GenericName[fr]=Formulaires +GenericName[fy]=Formulieren +GenericName[ga]=Foirmeacha +GenericName[gl]=Formularios +GenericName[he]=טפסים +GenericName[hi]=फॉर्म्स +GenericName[hr]=Obrasci +GenericName[hu]=Űrlapok +GenericName[is]=Form +GenericName[it]=Moduli +GenericName[ja]=フォーム +GenericName[km]=សំណុំបែបបទ +GenericName[lt]=Formos +GenericName[lv]=Formas +GenericName[ms]=Borang +GenericName[nb]=Skjema +GenericName[nds]=Kiekwarken +GenericName[ne]=फारमहरू +GenericName[nn]=Skjema +GenericName[pl]=Formularze +GenericName[pt]=Formulários +GenericName[pt_BR]=Formulários +GenericName[ru]=Формы +GenericName[se]=Skovit +GenericName[sk]=Formuláre +GenericName[sl]=Obrazci +GenericName[sr]=Форме +GenericName[sr@Latn]=Forme +GenericName[sv]=Formulär +GenericName[ta]=படிவங்கள் +GenericName[tr]=Formlar +GenericName[uk]=Форми +GenericName[uz]=Shakllar +GenericName[uz@cyrillic]=Шакллар +GenericName[zh_CN]=表单 +GenericName[zh_TW]=表單 +Name=Forms +Name[bg]=Формуляри +Name[br]=Paperennoù-reol +Name[ca]=Formularis +Name[cs]=Formuláře +Name[cy]=Ffurflenni +Name[da]=Formularer +Name[de]=Formulare +Name[el]=Φόρμες +Name[eo]=Formularoj +Name[es]=Formularios +Name[et]=Vormid +Name[eu]=Formularioak +Name[fa]=برگهها +Name[fi]=Lomakkeet +Name[fr]=Formulaires +Name[fy]=Formulieren +Name[ga]=Foirmeacha +Name[gl]=Formularios +Name[he]=טפסים +Name[hi]=फ़ॉर्म +Name[hr]=Obrasci +Name[hu]=Űrlapok +Name[is]=Form +Name[it]=Moduli +Name[ja]=フォーム +Name[km]=សំណុំបែបបទ +Name[lt]=Formos +Name[lv]=Formas +Name[ms]=Borang +Name[nb]=Skjema +Name[nds]=Kiekwarken +Name[ne]=फारमहरू +Name[nn]=Skjema +Name[pl]=Formularze +Name[pt]=Formulários +Name[pt_BR]=Formulários +Name[ru]=Формы +Name[se]=Skovit +Name[sk]=Formuláre +Name[sl]=Obrazci +Name[sr]=Форме +Name[sr@Latn]=Forme +Name[sv]=Formulär +Name[ta]=Kவிதி +Name[tg]=Шаклҳо +Name[tr]=Formlar +Name[uk]=Форми +Name[uz]=Shakllar +Name[uz@cyrillic]=Шакллар +Name[zh_CN]=表单 +Name[zh_TW]=表單 +X-KDE-Library=kexihandler_form +X-KDE-ParentApp=kexi +X-Kexi-PartVersion=2 +X-Kexi-TypeName=form +X-Kexi-TypeMime=kexi/form +X-Kexi-ItemIcon=form +X-Kexi-SupportsDataExport=false +X-Kexi-SupportsPrinting=false diff --git a/kexi/plugins/forms/kexiformmanager.cpp b/kexi/plugins/forms/kexiformmanager.cpp new file mode 100644 index 00000000..6134cfc8 --- /dev/null +++ b/kexi/plugins/forms/kexiformmanager.cpp @@ -0,0 +1,235 @@ +/* This file is part of the KDE project + Copyright (C) 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 "kexiformmanager.h" +#include "widgets/kexidbform.h" +#include "widgets/kexidbautofield.h" +#include "kexiformscrollview.h" +#include "kexiformview.h" +#include "kexidatasourcepage.h" + +#include <formeditor/formmanager.h> +#include <formeditor/widgetpropertyset.h> +#include <formeditor/form.h> +#include <formeditor/widgetlibrary.h> +#include <formeditor/commands.h> +#include <formeditor/objecttree.h> + +#include <koproperty/set.h> +#include <koproperty/property.h> +#include <widget/kexicustompropertyfactory.h> + +KexiFormManager::KexiFormManager(KexiPart::Part *parent, const char* name) + : KFormDesigner::FormManager(parent, + KFormDesigner::FormManager::HideEventsInPopupMenu | + KFormDesigner::FormManager::SkipFileActions | + KFormDesigner::FormManager::HideSignalSlotConnections + , name) + , m_part(parent) +{ + m_emitSelectionSignalsUpdatesPropertySet = true; + KexiCustomPropertyFactory::init(); +} + +KexiFormManager::~KexiFormManager() +{ +} + +KAction* KexiFormManager::action( const char* name ) +{ + KActionCollection *col = m_part->actionCollectionForMode(Kexi::DesignViewMode); + if (!col) + return 0; + QCString n( translateName( name ).latin1() ); + KAction *a = col->action(n); + if (a) + return a; + KexiDBForm *dbform; + if (!activeForm() || !activeForm()->designMode() + || !(dbform = dynamic_cast<KexiDBForm*>(activeForm()->formWidget()))) + return 0; + KexiFormScrollView *scrollViewWidget = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject()); + if (!scrollViewWidget) + return 0; + KexiFormView* formViewWidget = dynamic_cast<KexiFormView*>(scrollViewWidget->parent()); + if (!formViewWidget) + return 0; + return formViewWidget->parentDialog()->mainWin()->actionCollection()->action(n); +} + +KexiFormView* KexiFormManager::activeFormViewWidget() const +{ + KexiDBForm *dbform; + if (!activeForm() || !activeForm()->designMode() + || !(dbform = dynamic_cast<KexiDBForm*>(activeForm()->formWidget()))) + return 0; + KexiFormScrollView *scrollViewWidget = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject()); + if (!scrollViewWidget) + return 0; + return dynamic_cast<KexiFormView*>(scrollViewWidget->parent()); +} + +void KexiFormManager::enableAction( const char* name, bool enable ) +{ + KexiFormView* formViewWidget = activeFormViewWidget(); + if (!formViewWidget) + return; +// if (QString(name)=="layout_menu") +// kdDebug() << "!!!!!!!!!!! " << enable << endl; + formViewWidget->setAvailable(translateName( name ).latin1(), enable); +} + +void KexiFormManager::setFormDataSource(const QCString& mime, const QCString& name) +{ + if (!activeForm()) + return; + KexiDBForm* formWidget = dynamic_cast<KexiDBForm*>(activeForm()->widget()); + if (!formWidget) + return; + +// setPropertyValueInDesignMode(formWidget, "dataSource", name); + + QCString oldDataSourceMimeType( formWidget->dataSourceMimeType() ); + QCString oldDataSource( formWidget->dataSource().latin1() ); + if (mime!=oldDataSourceMimeType || name!=oldDataSource) { + QMap<QCString, QVariant> propValues; + propValues.insert("dataSource", name); + propValues.insert("dataSourceMimeType", mime); + KFormDesigner::CommandGroup *group + = new KFormDesigner::CommandGroup(i18n("Set Form's Data Source to \"%1\"").arg(name), propertySet()); + propertySet()->createPropertyCommandsInDesignMode(formWidget, propValues, group, true /*addToActiveForm*/); + } + +/* + if (activeForm()->selectedWidget() == formWidget) { + //active form is selected: just use properties system + KFormDesigner::WidgetPropertySet *set = propertySet(); + if (!set || !set->contains("dataSource")) + return; + (*set)["dataSource"].setValue(name); + if (set->contains("dataSourceMimeType")) + (*set)["dataSourceMimeType"].setValue(mime); + return; + } + + //active form isn't selected: change it's data source and mime type by hand + QCString oldDataSourceMimeType( formWidget->dataSourceMimeType() ); + QCString oldDataSource( formWidget->dataSource().latin1() ); + + if (mime!=oldDataSourceMimeType || name!=oldDataSource) { + formWidget->setDataSourceMimeType(mime); + formWidget->setDataSource(name); + emit dirty(activeForm(), true); + + activeForm()->addCommand( + new KFormDesigner::PropertyCommand(propertySet(), QString(formWidget->name()), + oldDataSource, name, "dataSource"), + false ); + + // If the property is changed, we add it in ObjectTreeItem modifProp + KFormDesigner::ObjectTreeItem *fromTreeItem = activeForm()->objectTree()->lookup(formWidget->name()); + fromTreeItem->addModifiedProperty("dataSourceMimeType", mime); + fromTreeItem->addModifiedProperty("dataSource", name); + }*/ +} + +void KexiFormManager::setDataSourceFieldOrExpression(const QString& string, const QString& caption, + KexiDB::Field::Type type) +{ + if (!activeForm()) + return; +// KexiFormDataItemInterface* dataWidget = dynamic_cast<KexiFormDataItemInterface*>(activeForm()->selectedWidget()); +// if (!dataWidget) +// return; + + KFormDesigner::WidgetPropertySet *set = propertySet(); + if (!set || !set->contains("dataSource")) + return; + + (*set)["dataSource"].setValue(string); + + if (set->contains("autoCaption") && (*set)["autoCaption"].value().toBool()) { + if (set->contains("fieldCaptionInternal")) + (*set)["fieldCaptionInternal"].setValue(caption); + } + if (//type!=KexiDB::Field::InvalidType && + set->contains("widgetType") && (*set)["widgetType"].value().toString()=="Auto") + { + if (set->contains("fieldTypeInternal")) + (*set)["fieldTypeInternal"].setValue(type); + } + +/* QString oldDataSource( dataWidget->dataSource() ); + if (string!=oldDataSource) { + dataWidget->setDataSource(string); + emit dirty(activeForm(), true); + + buffer + }*/ +} + +void KexiFormManager::insertAutoFields(const QString& sourceMimeType, const QString& sourceName, + const QStringList& fields) +{ + KexiFormView* formViewWidget = activeFormViewWidget(); + if (!formViewWidget || !formViewWidget->form() || !formViewWidget->form()->activeContainer()) + return; + formViewWidget->insertAutoFields(sourceMimeType, sourceName, fields, + formViewWidget->form()->activeContainer()); +} + +void KexiFormManager::slotHistoryCommandExecuted() +{ + const KFormDesigner::CommandGroup *group = dynamic_cast<const KFormDesigner::CommandGroup*>(sender()); + if (group) { + if (group->commands().count()==2) { + KexiDBForm* formWidget = dynamic_cast<KexiDBForm*>(activeForm()->widget()); + if (!formWidget) + return; + QPtrListIterator<KCommand> it(group->commands()); + const KFormDesigner::PropertyCommand* pc1 = dynamic_cast<const KFormDesigner::PropertyCommand*>(it.current()); + ++it; + const KFormDesigner::PropertyCommand* pc2 = dynamic_cast<const KFormDesigner::PropertyCommand*>(it.current()); + if (pc1 && pc2 && pc1->property()=="dataSource" && pc2->property()=="dataSourceMimeType") { + const QMap<QCString, QVariant>::const_iterator it1( pc1->oldValues().constBegin() ); + const QMap<QCString, QVariant>::const_iterator it2( pc2->oldValues().constBegin() ); + if (it1.key()==formWidget->name() && it2.key()==formWidget->name()) + static_cast<KexiFormPart*>(m_part)->dataSourcePage()->setDataSource( + formWidget->dataSourceMimeType(), formWidget->dataSource().latin1()); + } + } + } +} + +/* +bool KexiFormManager::loadFormFromDomInternal(Form *form, QWidget *container, QDomDocument &inBuf) +{ + QMap<QCString,QString> customProperties; + FormIO::loadFormFromDom(myform, container, domDoc, &customProperties); +} + +bool KexiFormManager::saveFormToStringInternal(Form *form, QString &dest, int indent) +{ + QMap<QCString,QString> customProperties; + return KFormDesigner::FormIO::saveFormToString(form, dest, indent, &customProperties); +} + +*/ + +#include "kexiformmanager.moc" diff --git a/kexi/plugins/forms/kexiformmanager.h b/kexi/plugins/forms/kexiformmanager.h new file mode 100644 index 00000000..1cc5f0c6 --- /dev/null +++ b/kexi/plugins/forms/kexiformmanager.h @@ -0,0 +1,87 @@ +/* This file is part of the KDE project + Copyright (C) 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 KEXIFORMMANAGER_H +#define KEXIFORMMANAGER_H + +#include <formmanager.h> +#include <kexipart.h> + +class KCommand; +class KexiFormView; + +//! @internal +//! Used to customize KFormDesigner::FormManager behaviour. +class KEXIFORMUTILS_EXPORT KexiFormManager : public KFormDesigner::FormManager +{ + Q_OBJECT + + public: + KexiFormManager(KexiPart::Part *parent, const char* name = 0); + virtual ~KexiFormManager(); + + virtual KAction* action( const char* name ); + virtual void enableAction( const char* name, bool enable ); + + public slots: + //! Receives signal from KexiDataSourcePage about changed form's data source + void setFormDataSource(const QCString& mime, const QCString& name); + + /*! Receives signal from KexiDataSourcePage about changed widget's data source. + This is because we couldn't pass objects like KexiDB::QueryColumnInfo. + + Also sets following things in KexiDBAutoField: + - caption related to the data source + - data type related to the data source */ + void setDataSourceFieldOrExpression(const QString& string, const QString& caption, + KexiDB::Field::Type type); + + /*! Receives signal from KexiDataSourcePage and inserts autofields onto the current form. */ + void insertAutoFields(const QString& sourceMimeType, const QString& sourceName, + const QStringList& fields); + + protected slots: + void slotHistoryCommandExecuted(); + + protected: + inline QString translateName( const char* name ) const; + + private: + //! Helper: return active form's view widget or 0 if there's no active form having such widget + KexiFormView* activeFormViewWidget() const; + +// virtual bool loadFormFromDomInternal(Form *form, QWidget *container, QDomDocument &inBuf); +// virtual bool saveFormToStringInternal(Form *form, QString &dest, int indent = 0); + + KexiPart::Part* m_part; +}; + +QString KexiFormManager::translateName( const char* name ) const +{ + QString n( name ); + //translate to our name space: + if (n.startsWith("align_") || n.startsWith("adjust_") || n.startsWith("layout_") + || n=="format_raise" || n=="format_raise" || n=="taborder" | n=="break_layout") + { + n.prepend("formpart_"); + } + return n; +} + +#endif diff --git a/kexi/plugins/forms/kexiformpart.cpp b/kexi/plugins/forms/kexiformpart.cpp new file mode 100644 index 00000000..8693cb5b --- /dev/null +++ b/kexi/plugins/forms/kexiformpart.cpp @@ -0,0 +1,550 @@ +/* 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 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 <kdebug.h> +#include <kgenericfactory.h> +#include <kdialogbase.h> +#include <klistview.h> +#include <ktabwidget.h> +#include <kiconloader.h> +#include <kcombobox.h> +#include <kapplication.h> +#include <kconfig.h> + +#include <kexiviewbase.h> +#include <keximainwindow.h> +#include <kexiproject.h> +#include <kexipartitem.h> +#include <kexidialogbase.h> +#include <kexidatasourcecombobox.h> +#include <kexidb/connection.h> +#include <kexidb/fieldlist.h> +#include <kexidb/field.h> +#include <kexiutils/utils.h> + +#include <form.h> +#include <formIO.h> +#include <widgetpropertyset.h> +#include <widgetlibrary.h> +#include <objecttreeview.h> +#include <koproperty/property.h> + +#include "kexiformview.h" +#include "widgets/kexidbform.h" +#include "kexiformscrollview.h" +#include "kexiactionselectiondialog.h" +#include "kexiformmanager.h" +#include "kexiformpart.h" +#include "kexidatasourcepage.h" + +//! @todo #define KEXI_SHOW_SPLITTER_WIDGET + +KFormDesigner::WidgetLibrary* KexiFormPart::static_formsLibrary = 0L; + +//! @internal +class KexiFormPart::Private +{ + public: + Private() + { + } + ~Private() + { + delete static_cast<KFormDesigner::ObjectTreeView*>(objectTreeView); + delete static_cast<KexiDataSourcePage*>(dataSourcePage); + } +// QGuardedPtr<KFormDesigner::FormManager> manager; + QGuardedPtr<KFormDesigner::ObjectTreeView> objectTreeView; + QGuardedPtr<KexiDataSourcePage> dataSourcePage; + KexiDataSourceComboBox *dataSourceCombo; +}; + +KexiFormPart::KexiFormPart(QObject *parent, const char *name, const QStringList &l) + : KexiPart::Part(parent, name, l) + , d(new Private()) +{ + // REGISTERED ID: + m_registeredPartID = (int)KexiPart::FormObjectType; + + kexipluginsdbg << "KexiFormPart::KexiFormPart()" << endl; + m_names["instanceName"] + = i18n("Translate this word using only lowercase alphanumeric characters (a..z, 0..9). " + "Use '_' character instead of spaces. First character should be a..z character. " + "If you cannot use latin characters in your language, use english word.", + "form"); + m_names["instanceCaption"] = i18n("Form"); + m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode; + m_newObjectsAreDirty = true; + + // Only create form manager if it's not yet created. + // KexiReportPart could have created it already. + KFormDesigner::FormManager *formManager = KFormDesigner::FormManager::self(); + if (!formManager) + formManager = new KexiFormManager(this, "kexi_form_and_report_manager"); + + // Create and store a handle to forms' library. Reports will have their own library too. +/* @todo add configuration for supported factory groups */ + QStringList supportedFactoryGroups; + supportedFactoryGroups += "kexi"; + static_formsLibrary = KFormDesigner::FormManager::createWidgetLibrary( + formManager, supportedFactoryGroups); + static_formsLibrary->setAdvancedPropertiesVisible(false); + connect(static_formsLibrary, SIGNAL(widgetCreated(QWidget*)), + this, SLOT(slotWidgetCreatedByFormsLibrary(QWidget*))); + + connect(KFormDesigner::FormManager::self()->propertySet(), SIGNAL(widgetPropertyChanged(QWidget *, const QCString &, const QVariant&)), + this, SLOT(slotPropertyChanged(QWidget *, const QCString &, const QVariant&))); + connect(KFormDesigner::FormManager::self(), SIGNAL(autoTabStopsSet(KFormDesigner::Form*,bool)), + this, SLOT(slotAutoTabStopsSet(KFormDesigner::Form*,bool))); +} + +KexiFormPart::~KexiFormPart() +{ + delete d; +} + +KFormDesigner::WidgetLibrary* KexiFormPart::library() +{ + return static_formsLibrary; +} + +#if 0 +void KexiFormPart::initPartActions(KActionCollection *collection) +{ +//this is automatic? -no +//create child guicilent: guiClient()->setXMLFile("kexidatatableui.rc"); + + kexipluginsdbg<<"FormPart INIT ACTIONS***********************************************************************"<<endl; + //TODO + + //guiClient()->setXMLFile("kexiformui.rc"); +//js m_manager->createActions(collection, 0); +} + +void KexiFormPart::initInstanceActions( int mode, KActionCollection *col ) +{ + if (mode==Kexi::DesignViewMode) { + KFormDesigner::FormManager::self()->createActions(col, 0); + new KAction(i18n("Edit Tab Order..."), "tab_order", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(editTabOrder()), col, "taborder"); + new KAction(i18n("Adjust Size"), "viewmagfit", KShortcut(0), KFormDesigner::FormManager::self(), SLOT(ajustWidgetSize()), col, "adjust"); + } + //TODO +} +#endif + +void KexiFormPart::initPartActions() +{ +// new KAction(i18n("Show Form UI Code"), "show_form_ui", CTRL+Key_U, m_manager, SLOT(showFormUICode()), +// guiClient()->actionCollection(), "show_form_ui"); +} + +void KexiFormPart::initInstanceActions() +{ +#ifdef KEXI_DEBUG_GUI + kapp->config()->setGroup("General"); + if (kapp->config()->readBoolEntry("showInternalDebugger", false)) { + new KAction(i18n("Show Form UI Code"), "compfile", + CTRL+Key_U, KFormDesigner::FormManager::self(), SLOT(showFormUICode()), + actionCollectionForMode(Kexi::DesignViewMode), "show_form_ui"); + } +#endif + + KActionCollection *col = actionCollectionForMode(Kexi::DesignViewMode); + KFormDesigner::FormManager::self()->createActions( library(), col, (KXMLGUIClient*)col->parentGUIClient() ); //guiClient() ); + + //connect actions provided by widget factories + connect( col->action("widget_assign_action"), SIGNAL(activated()), this, SLOT(slotAssignAction())); + + createSharedAction(Kexi::DesignViewMode, i18n("Clear Widget Contents"), "editclear", 0, "formpart_clear_contents"); + createSharedAction(Kexi::DesignViewMode, i18n("Edit Tab Order..."), "tab_order", 0, "formpart_taborder"); +//TODO createSharedAction(Kexi::DesignViewMode, i18n("Edit Pixmap Collection"), "icons", 0, "formpart_pixmap_collection"); +//TODO createSharedAction(Kexi::DesignViewMode, i18n("Edit Form Connections"), "connections", 0, "formpart_connections"); + +// KFormDesigner::CreateLayoutCommand + + KAction *action = createSharedAction(Kexi::DesignViewMode, i18n("Layout Widgets"), "", 0, "formpart_layout_menu", "KActionMenu"); + KActionMenu *menu = static_cast<KActionMenu*>(action); + + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("&Horizontally"), + QString::null, 0, "formpart_layout_hbox")); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("&Vertically"), + QString::null, 0, "formpart_layout_vbox")); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("In &Grid"), + QString::null, 0, "formpart_layout_grid")); +#ifdef KEXI_SHOW_SPLITTER_WIDGET + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("Horizontally in &Splitter"), + QString::null, 0, "formpart_layout_hsplitter")); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("Verti&cally in Splitter"), + QString::null, 0, "formpart_layout_vsplitter")); +#endif + + createSharedAction(Kexi::DesignViewMode, i18n("&Break Layout"), QString::null, 0, "formpart_break_layout"); +/* + createSharedAction(Kexi::DesignViewMode, i18n("Lay Out Widgets &Horizontally"), QString::null, 0, "formpart_layout_hbox"); + createSharedAction(Kexi::DesignViewMode, i18n("Lay Out Widgets &Vertically"), QString::null, 0, "formpart_layout_vbox"); + createSharedAction(Kexi::DesignViewMode, i18n("Lay Out Widgets in &Grid"), QString::null, 0, "formpart_layout_grid"); +*/ + createSharedAction(Kexi::DesignViewMode, i18n("Bring Widget to Front"), "raise", 0, "formpart_format_raise"); + createSharedAction(Kexi::DesignViewMode, i18n("Send Widget to Back"), "lower", 0, "formpart_format_lower"); + +#ifndef KEXI_NO_UNFINISHED + action = createSharedAction(Kexi::DesignViewMode, i18n("Other Widgets"), "", 0, "other_widgets_menu", "KActionMenu"); +#endif + + action = createSharedAction(Kexi::DesignViewMode, i18n("Align Widgets Position"), "aoleft", 0, "formpart_align_menu", "KActionMenu"); + menu = static_cast<KActionMenu*>(action); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Left"), "aoleft", 0, "formpart_align_to_left") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Right"), "aoright", 0, "formpart_align_to_right") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Top"), "aotop", 0, "formpart_align_to_top") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Bottom"), "aobottom", 0, "formpart_align_to_bottom") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Grid"), "aopos2grid", 0, "formpart_align_to_grid") ); + + action = createSharedAction(Kexi::DesignViewMode, i18n("Adjust Widgets Size"), "aogrid", 0, "formpart_adjust_size_menu", "KActionMenu"); + menu = static_cast<KActionMenu*>(action); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Fit"), "aofit", 0, "formpart_adjust_to_fit") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Grid"), "aogrid", 0, "formpart_adjust_size_grid") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Shortest"), "aoshortest", 0, "formpart_adjust_height_small") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Tallest"), "aotallest", 0, "formpart_adjust_height_big") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Narrowest"), "aonarrowest", 0, "formpart_adjust_width_small") ); + menu->insert( createSharedAction(Kexi::DesignViewMode, i18n("To Widest"), "aowidest", 0, "formpart_adjust_width_big") ); +} + +KexiDialogTempData* +KexiFormPart::createTempData(KexiDialogBase* dialog) +{ + return new KexiFormPart::TempData(dialog); +} + +KexiViewBase* KexiFormPart::createView(QWidget *parent, KexiDialogBase* dialog, + KexiPart::Item &item, int viewMode, QMap<QString,QString>*) +{ + Q_UNUSED( viewMode ); + + kexipluginsdbg << "KexiFormPart::createView()" << endl; + KexiMainWindow *win = dialog->mainWin(); + if (!win || !win->project() || !win->project()->dbConnection()) + return 0; + + KexiFormView *view = new KexiFormView(win, parent, item.name().latin1(), + win->project()->dbConnection() ); + + return view; +} + +void +KexiFormPart::generateForm(KexiDB::FieldList *list, QDomDocument &domDoc) +{ + //this form generates a .ui from FieldList list + //basically that is a Label and a LineEdit for each field + domDoc = QDomDocument("UI"); + QDomElement uiElement = domDoc.createElement("UI"); + domDoc.appendChild(uiElement); + uiElement.setAttribute("version", "3.1"); + uiElement.setAttribute("stdsetdef", 1); + + QDomElement baseClass = domDoc.createElement("class"); + uiElement.appendChild(baseClass); + QDomText baseClassV = domDoc.createTextNode("QWidget"); + baseClass.appendChild(baseClassV); + QDomElement baseWidget = domDoc.createElement("widget"); + baseWidget.setAttribute("class", "QWidget"); + + int y=0; + + for(unsigned int i=0; i < list->fieldCount(); i++) + { + QDomElement lclass = domDoc.createElement("widget"); + baseWidget.appendChild(lclass); + lclass.setAttribute("class", "QLabel"); + QDomElement lNameProperty = domDoc.createElement("property"); + lNameProperty.setAttribute("name", "name"); + QDomElement lType = domDoc.createElement("cstring"); + QDomText lClassN = domDoc.createTextNode(QString("l%1").arg(list->field(i)->name())); + lType.appendChild(lClassN); + lNameProperty.appendChild(lType); + lclass.appendChild(lNameProperty); + + QDomElement gNameProperty = domDoc.createElement("property"); + gNameProperty.setAttribute("name", "geometry"); + QDomElement lGType = domDoc.createElement("rect"); + + QDomElement lx = domDoc.createElement("x"); + QDomText lxV = domDoc.createTextNode("10"); + lx.appendChild(lxV); + QDomElement ly = domDoc.createElement("y"); + QDomText lyV = domDoc.createTextNode(QString::number(y + 10)); + ly.appendChild(lyV); + QDomElement lWidth = domDoc.createElement("width"); + QDomText lWidthV = domDoc.createTextNode("100"); + lWidth.appendChild(lWidthV); + QDomElement lHeight = domDoc.createElement("height"); + QDomText lHeightV = domDoc.createTextNode("20"); + lHeight.appendChild(lHeightV); + + lGType.appendChild(lx); + lGType.appendChild(ly); + lGType.appendChild(lWidth); + lGType.appendChild(lHeight); + + gNameProperty.appendChild(lGType); + lclass.appendChild(gNameProperty); + + QDomElement tNameProperty = domDoc.createElement("property"); + tNameProperty.setAttribute("name", "text"); + QDomElement lTType = domDoc.createElement("string"); + QDomText lTextV = domDoc.createTextNode(list->field(i)->name()); + lTType.appendChild(lTextV); + tNameProperty.appendChild(lTType); + lclass.appendChild(tNameProperty); + + + ///line edit! + + + QDomElement vclass = domDoc.createElement("widget"); + baseWidget.appendChild(vclass); + vclass.setAttribute("class", "KLineEdit"); + QDomElement vNameProperty = domDoc.createElement("property"); + vNameProperty.setAttribute("name", "name"); + QDomElement vType = domDoc.createElement("cstring"); + QDomText vClassN = domDoc.createTextNode(list->field(i)->name()); + vType.appendChild(vClassN); + vNameProperty.appendChild(vType); + vclass.appendChild(vNameProperty); + + QDomElement vgNameProperty = domDoc.createElement("property"); + vgNameProperty.setAttribute("name", "geometry"); + QDomElement vGType = domDoc.createElement("rect"); + + QDomElement vx = domDoc.createElement("x"); + QDomText vxV = domDoc.createTextNode("110"); + vx.appendChild(vxV); + QDomElement vy = domDoc.createElement("y"); + QDomText vyV = domDoc.createTextNode(QString::number(y + 10)); + vy.appendChild(vyV); + QDomElement vWidth = domDoc.createElement("width"); + QDomText vWidthV = domDoc.createTextNode("200"); + vWidth.appendChild(vWidthV); + QDomElement vHeight = domDoc.createElement("height"); + QDomText vHeightV = domDoc.createTextNode("20"); + vHeight.appendChild(vHeightV); + + vGType.appendChild(vx); + vGType.appendChild(vy); + vGType.appendChild(vWidth); + vGType.appendChild(vHeight); + + vgNameProperty.appendChild(vGType); + vclass.appendChild(vgNameProperty); + + y += 20; + } + + QDomElement lNameProperty = domDoc.createElement("property"); + lNameProperty.setAttribute("name", "name"); + QDomElement lType = domDoc.createElement("cstring"); + QDomText lClassN = domDoc.createTextNode("DBForm"); + lType.appendChild(lClassN); + lNameProperty.appendChild(lType); + baseWidget.appendChild(lNameProperty); + + QDomElement wNameProperty = domDoc.createElement("property"); + wNameProperty.setAttribute("name", "geometry"); + QDomElement wGType = domDoc.createElement("rect"); + + QDomElement wx = domDoc.createElement("x"); + QDomText wxV = domDoc.createTextNode("0"); + wx.appendChild(wxV); + QDomElement wy = domDoc.createElement("y"); + QDomText wyV = domDoc.createTextNode("0"); + wy.appendChild(wyV); + QDomElement wWidth = domDoc.createElement("width"); + QDomText wWidthV = domDoc.createTextNode("340"); + wWidth.appendChild(wWidthV); + QDomElement wHeight = domDoc.createElement("height"); + QDomText wHeightV = domDoc.createTextNode(QString::number(y + 30)); + wHeight.appendChild(wHeightV); + + wGType.appendChild(wx); + wGType.appendChild(wy); + wGType.appendChild(wWidth); + wGType.appendChild(wHeight); + + wNameProperty.appendChild(wGType); + baseWidget.appendChild(wNameProperty); + + uiElement.appendChild(baseWidget); +} + +void KexiFormPart::slotAutoTabStopsSet(KFormDesigner::Form *form, bool set) +{ + Q_UNUSED( form ); + + KoProperty::Property &p = (*KFormDesigner::FormManager::self()->propertySet())["autoTabStops"]; + if (!p.isNull()) + p.setValue(QVariant(set, 4)); +} + +void KexiFormPart::slotAssignAction() +{ + KexiDBForm *dbform; + if (!KFormDesigner::FormManager::self()->activeForm() || !KFormDesigner::FormManager::self()->activeForm()->designMode() + || !(dbform = dynamic_cast<KexiDBForm*>(KFormDesigner::FormManager::self()->activeForm()->formWidget()))) + return; + + KFormDesigner::WidgetPropertySet * propSet = KFormDesigner::FormManager::self()->propertySet(); + + KoProperty::Property &onClickActionProp = propSet->property("onClickAction"); + if (onClickActionProp.isNull()) + return; + KoProperty::Property &onClickActionOptionProp = propSet->property("onClickActionOption"); + KexiFormEventAction::ActionData data; + data.string = onClickActionProp.value().toString(); + if (!onClickActionOptionProp.isNull()) + data.option = onClickActionOptionProp.value().toString(); + + KexiFormScrollView *scrollViewWidget = dynamic_cast<KexiFormScrollView*>(dbform->dataAwareObject()); + if (!scrollViewWidget) + return; + KexiFormView* formViewWidget = dynamic_cast<KexiFormView*>(scrollViewWidget->parent()); + if (!formViewWidget) + return; + + KexiMainWindow * mainWin = formViewWidget->parentDialog()->mainWin(); + KexiActionSelectionDialog dlg(mainWin, dbform, data, + propSet->property("name").value().toCString()); + + if(dlg.exec() == QDialog::Accepted) { + data = dlg.currentAction(); + //update property value + propSet->property("onClickAction").setValue(data.string); + propSet->property("onClickActionOption").setValue(data.option); + } +} + +QString +KexiFormPart::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const +{ + Q_UNUSED(dlg); + if (englishMessage=="Design of object \"%1\" has been modified.") + return i18n("Design of form \"%1\" has been modified."); + if (englishMessage=="Object \"%1\" already exists.") + return i18n("Form \"%1\" already exists."); + + return englishMessage; +} + +void +KexiFormPart::slotPropertyChanged(QWidget *w, const QCString &name, const QVariant &value) +{ + Q_UNUSED( w ); + + if (!KFormDesigner::FormManager::self()->activeForm()) + return; + if (name == "autoTabStops") { + //QWidget *w = KFormDesigner::FormManager::self()->activeForm()->selectedWidget(); + //update autoTabStops setting at KFD::Form level + KFormDesigner::FormManager::self()->activeForm()->setAutoTabStops( value.toBool() ); + } + if (KFormDesigner::FormManager::self()->activeForm()->widget() && name == "geometry") { + //fall back to sizeInternal property.... + if (KFormDesigner::FormManager::self()->propertySet()->contains("sizeInternal")) + KFormDesigner::FormManager::self()->propertySet()->property("sizeInternal").setValue(value.toRect().size()); + } +} + +/*KFormDesigner::FormManager* +KexiFormPart::manager() const +{ + return d->manager; +}*/ + +KexiDataSourcePage* KexiFormPart::dataSourcePage() const +{ + return d->dataSourcePage; +} + +void KexiFormPart::setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin) +{ + if (!d->objectTreeView) { + d->objectTreeView = new KFormDesigner::ObjectTreeView(0, "KexiFormPart:ObjectTreeView"); + KFormDesigner::FormManager::self()->setObjectTreeView(d->objectTreeView); //important: assign to manager + d->dataSourcePage = new KexiDataSourcePage(0, "dataSourcePage"); + connect(d->dataSourcePage, SIGNAL(jumpToObjectRequested(const QCString&, const QCString&)), + mainWin, SLOT(highlightObject(const QCString&, const QCString&))); + connect(d->dataSourcePage, SIGNAL(formDataSourceChanged(const QCString&, const QCString&)), + KFormDesigner::FormManager::self(), SLOT(setFormDataSource(const QCString&, const QCString&))); + connect(d->dataSourcePage, SIGNAL(dataSourceFieldOrExpressionChanged(const QString&, const QString&, KexiDB::Field::Type)), + KFormDesigner::FormManager::self(), SLOT(setDataSourceFieldOrExpression(const QString&, const QString&, KexiDB::Field::Type))); + connect(d->dataSourcePage, SIGNAL(insertAutoFields(const QString&, const QString&, const QStringList&)), + KFormDesigner::FormManager::self(), SLOT(insertAutoFields(const QString&, const QString&, const QStringList&))); + } + + KexiProject *prj = mainWin->project(); + d->dataSourcePage->setProject(prj); + + tab->addTab( d->dataSourcePage, SmallIconSet("database"), ""); + tab->setTabToolTip( d->dataSourcePage, i18n("Data Source")); + + tab->addTab( d->objectTreeView, SmallIconSet("widgets"), ""); + tab->setTabToolTip( d->objectTreeView, i18n("Widgets")); +} + +void KexiFormPart::slotWidgetCreatedByFormsLibrary(QWidget* widget) +{ + QStrList signalNames(widget->metaObject()->signalNames()); + if (!signalNames.isEmpty()) { + const char *handleDragMoveEventSignal = "handleDragMoveEvent(QDragMoveEvent*)"; + const char *handleDropEventSignal = "handleDropEvent(QDropEvent*)"; + + for (QStrListIterator it(signalNames); it.current(); ++it) { + if (0==qstrcmp(it.current(), handleDragMoveEventSignal)) { + kdDebug() << it.current() << endl; + KexiFormView *formView = KexiUtils::findParent<KexiFormView>(widget, "KexiFormView"); + if (formView) { + connect(widget, SIGNAL(handleDragMoveEvent(QDragMoveEvent*)), + formView, SLOT(slotHandleDragMoveEvent(QDragMoveEvent*))); + } + } + else if (0==qstrcmp(it.current(), handleDropEventSignal)) { + kdDebug() << it.current() << endl; + KexiFormView *formView = KexiUtils::findParent<KexiFormView>(widget, "KexiFormView"); + if (formView) { + connect(widget, SIGNAL(handleDropEvent(QDropEvent*)), + formView, SLOT(slotHandleDropEvent(QDropEvent*))); + } + } + } + } +} + +//---------------- + +KexiFormPart::TempData::TempData(QObject* parent) + : KexiDialogTempData(parent) +{ +} + +KexiFormPart::TempData::~TempData() +{ +} + +#include "kexiformpart.moc" diff --git a/kexi/plugins/forms/kexiformpart.h b/kexi/plugins/forms/kexiformpart.h new file mode 100644 index 00000000..1ddbab53 --- /dev/null +++ b/kexi/plugins/forms/kexiformpart.h @@ -0,0 +1,108 @@ +/* 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 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 KEXIFORMPART_H +#define KEXIFORMPART_H + +#include <qdom.h> +#include <qcstring.h> + +#include <kexi.h> +#include <kexipart.h> +#include <kexidialogbase.h> +#include <kexiblobbuffer.h> + +namespace KFormDesigner +{ + class WidgetLibrary; + class FormManager; + class Form; +} + +namespace KexiDB +{ + class FieldList; +} + +class KexiDataSourcePage; + +//! Kexi Form Plugin +/*! It just creates a \ref KexiFormView. See there for most of code. */ +class KEXIFORMUTILS_EXPORT KexiFormPart : public KexiPart::Part +{ + Q_OBJECT + + public: + KexiFormPart(QObject *parent, const char *name, const QStringList &); + virtual ~KexiFormPart(); + + //! \return a pointer to Forms Widget Library. + static KFormDesigner::WidgetLibrary* library(); + + KexiDataSourcePage* dataSourcePage() const; + + void generateForm(KexiDB::FieldList *list, QDomDocument &domDoc); + + class TempData : public KexiDialogTempData + { + public: + TempData(QObject* parent); + ~TempData(); + QGuardedPtr<KFormDesigner::Form> form; + QGuardedPtr<KFormDesigner::Form> previewForm; + QString tempForm; + QPoint scrollViewContentsPos; //!< to preserve contents pos after switching to other view + int resizeMode; //!< form's window's resize mode -one of KexiFormView::ResizeMode items + //! Used in KexiFormView::setUnsavedLocalBLOBs() + QMap<QWidget*, KexiBLOBBuffer::Id_t> unsavedLocalBLOBs; + //! Used when loading a form from (temporary) XML in Data View + //! to get unsaved blobs collected at design mode. + QMap<QCString, KexiBLOBBuffer::Id_t> unsavedLocalBLOBsByName; + }; + + virtual QString i18nMessage(const QCString& englishMessage, + KexiDialogBase* dlg) const; + + protected: + virtual KexiDialogTempData* createTempData(KexiDialogBase* dialog); + + virtual KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog, + KexiPart::Item &item, int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0); + + virtual void initPartActions(); + virtual void initInstanceActions(); + virtual void setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin); + + static KFormDesigner::WidgetLibrary* static_formsLibrary; + + protected slots: + void slotAutoTabStopsSet(KFormDesigner::Form *form, bool set); + void slotAssignAction(); + void slotPropertyChanged(QWidget *widget, const QCString &name, const QVariant &value); + void slotWidgetCreatedByFormsLibrary(QWidget* widget); + + private: + class Private; + Private* d; +}; + +#endif + diff --git a/kexi/plugins/forms/kexiformpartinstui.rc b/kexi/plugins/forms/kexiformpartinstui.rc new file mode 100644 index 00000000..75f233f2 --- /dev/null +++ b/kexi/plugins/forms/kexiformpartinstui.rc @@ -0,0 +1,77 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kexiformpartinst" version="13"> + +<MenuBar> + <Menu name="edit"> + <Action name="fompart_clear_contents"/> + <Separator /> + <Action name="formpart_taborder"/> + <Action name="formpart_adjust_size"/> + <Action name="formpart_pixmap_collection"/> + <Action name="formpart_connections"/> + <!-- Action name="change_style"/ --> + </Menu> + <Menu name="format" noMerge="0"> + <text>&Format</text> + <Action name="snap_to_grid"/> + <Separator/> + <Action name="formpart_layout_menu"/> + <Action name="formpart_break_layout"/> + <Separator/> + <Action name="formpart_align_menu"/> + <Action name="formpart_adjust_size_menu"/> + <Separator/> + <Action name="formpart_format_raise"/> + <Action name="formpart_format_lower"/> + </Menu> +</MenuBar> + +<ToolBar name="widgets" fullWidth="false"> + <text>Widgets</text> + <Action name="pointer"/> + <!-- Action name="drag_connection"/ --> + <Separator/> + <Action name="library_widget_KexiDBAutoField"/> + <Action name="library_widget_KexiDBLabel"/> + <Action name="library_widget_KexiPictureLabel"/> + <Action name="library_widget_KexiDBImageBox"/> + <Action name="library_widget_KexiDBLineEdit"/> + <Action name="library_widget_KexiDBTextEdit"/> + <Action name="library_widget_KPushButton"/> + <Action name="library_widget_KexiDBComboBox"/> + <!-- Action name="library_widget_QRadioButton"/ --> + <Action name="library_widget_KexiDBCheckBox"/> + <Action name="library_widget_Spacer"/> + <Action name="library_widget_Line"/> + <Separator/> + <Action name="library_widget_KexiFrame"/> + <Action name="library_widget_QGroupBox"/> + <Action name="library_widget_KFDTabWidget"/> + <!-- TODO Action name="library_widget_KexiDBSubForm"/ --> + <Separator/> + <Action name="library_widget_Spring"/> + <Separator/> + <Action name="other_widgets_menu"/> + <ActionList name="library_widgets" /> + <Merge/> +</ToolBar> +<ToolBar name="format" fullWidth="false" noMerge="1"> +<text>Format</text> + <!-- Action name="formpart_layout_menu"/ --> + <Action name="formpart_align_menu"/> + <Action name="formpart_adjust_size_menu"/> + <Action name="show_form_ui" /> +</ToolBar> +<!-- ToolBar name="tools" fullWidth="false"> + <Action name="change_style"/> +</ToolBar --> + +<Menu name="other_widgets_menu"> + <Action name="library_widget_KexiDBIntSpinBox"/> + <Action name="library_widget_KexiDBDoubleSpinBox"/> + <Action name="library_widget_KexiDBDateEdit"/> + <Action name="library_widget_KexiDBTimeEdit"/> + <Action name="library_widget_KexiDBDateTimeEdit"/> +</Menu> + +</kpartgui> diff --git a/kexi/plugins/forms/kexiformpartui.rc b/kexi/plugins/forms/kexiformpartui.rc new file mode 100644 index 00000000..20fd49d8 --- /dev/null +++ b/kexi/plugins/forms/kexiformpartui.rc @@ -0,0 +1,10 @@ +<!DOCTYPE kpartgui> +<kpartgui name="kexiformpart" version="6"> + +<!-- ToolBar name="design" fullWidth="false" noMerge="0"> + <text>Design</text> + <Action name="show_form_ui"/> +</ToolBar --> + +</kpartgui> + diff --git a/kexi/plugins/forms/kexiforms.cpp b/kexi/plugins/forms/kexiforms.cpp new file mode 100644 index 00000000..07c4726f --- /dev/null +++ b/kexi/plugins/forms/kexiforms.cpp @@ -0,0 +1,25 @@ +/* This file is part of the KDE project + Copyright (C) 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 <kgenericfactory.h> + +#include "kexiformpart.h" + +K_EXPORT_COMPONENT_FACTORY( kexihandler_form, KGenericFactory<KexiFormPart>("kexihandler_form") ) + diff --git a/kexi/plugins/forms/kexiformscrollview.cpp b/kexi/plugins/forms/kexiformscrollview.cpp new file mode 100644 index 00000000..351a1e3e --- /dev/null +++ b/kexi/plugins/forms/kexiformscrollview.cpp @@ -0,0 +1,587 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2006 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 "kexiformscrollview.h" +//#include "kexiformview.h" + +#include <formeditor/form.h> +#include <formeditor/formmanager.h> +#include <formeditor/objecttree.h> +#include <formeditor/commands.h> +#include <widget/utils/kexirecordmarker.h> + +#include <kpopupmenu.h> +#include <kdebug.h> + +KexiFormScrollView::KexiFormScrollView(QWidget *parent, bool preview) + : KexiScrollView(parent, preview) + , KexiRecordNavigatorHandler() + , KexiSharedActionClient() + , KexiDataAwareObjectInterface() + , KexiFormDataProvider() + , KexiFormEventHandler() +{ + m_currentLocalSortColumn = -1; /* no column */ + m_localSortingOrder = -1; /* no sorting */ + m_previousItem = 0; + m_navPanel = m_scrollViewNavPanel; //copy this pointer from KexiScrollView + if (preview) { + setRecordNavigatorVisible(true); +//tmp +// recordNavigator()->setEditingIndicatorEnabled(true); +// recordNavigator()->showEditingIndicator(true); + } + + connect(this, SIGNAL(resizingStarted()), this, SLOT(slotResizingStarted())); + + m_popupMenu = new KPopupMenu(this, "contextMenu"); + +// setFocusPolicy(NoFocus); +} + +KexiFormScrollView::~KexiFormScrollView() +{ + if (m_owner) + delete m_data; + m_data = 0; +} + +void +KexiFormScrollView::show() +{ + KexiScrollView::show(); + +#if 0 //moved to KexiFormView, OK? + //now get resize mode settings for entire form + if (m_preview) { + KexiFormView* fv = dynamic_cast<KexiFormView*>(parent()); + int resizeMode = fv ? fv->resizeMode() : KexiFormView::ResizeAuto; + if (resizeMode == KexiFormView::ResizeAuto) + setResizePolicy(AutoOneFit); + } +#endif +} + +void +KexiFormScrollView::slotResizingStarted() +{ + if(m_form && KFormDesigner::FormManager::self()) + setSnapToGrid(KFormDesigner::FormManager::self()->snapWidgetsToGrid(), m_form->gridSize()); + else + setSnapToGrid(false); +} + +int KexiFormScrollView::rowsPerPage() const +{ + //! @todo + return 10; +} + +void KexiFormScrollView::selectCellInternal() +{ + //m_currentItem is already set by KexiDataAwareObjectInterface::setCursorPosition() + if (m_currentItem) { + if (m_currentItem!=m_previousItem) { + fillDataItems(*m_currentItem, cursorAtNewRow()); + m_previousItem = m_currentItem; + } + } + else { + m_previousItem = 0; + } +} + +void KexiFormScrollView::ensureCellVisible(int row, int col/*=-1*/) +{ + Q_UNUSED( row ); + Q_UNUSED( col ); + //! @todo +// if (m_currentItem) + //fillDataItems(*m_currentItem); + +// if (m_form->tabStops()->first() && m_form->tabStops()->first()->widget()) +// m_form->tabStops()->first()->widget()->setFocus(); +} + +void KexiFormScrollView::moveToRecordRequested(uint r) +{ + //! @todo + selectRow(r); +} + +void KexiFormScrollView::moveToLastRecordRequested() +{ + //! @todo + selectLastRow(); +} + +void KexiFormScrollView::moveToPreviousRecordRequested() +{ + //! @todo + selectPrevRow(); +} + +void KexiFormScrollView::moveToNextRecordRequested() +{ + //! @todo + selectNextRow(); +} + +void KexiFormScrollView::moveToFirstRecordRequested() +{ + //! @todo + selectFirstRow(); +} + +void KexiFormScrollView::clearColumnsInternal(bool repaint) +{ + Q_UNUSED( repaint ); + //! @todo +} + +void KexiFormScrollView::addHeaderColumn(const QString& caption, const QString& description, + const QIconSet& icon, int width) +{ + Q_UNUSED( caption ); + Q_UNUSED( description ); + Q_UNUSED( icon ); + Q_UNUSED( width ); + + //! @todo +} + +int KexiFormScrollView::currentLocalSortingOrder() const +{ + //! @todo + return m_localSortingOrder; +} + +int KexiFormScrollView::currentLocalSortColumn() const +{ + return m_currentLocalSortColumn; +} + +void KexiFormScrollView::setLocalSortingOrder(int col, int order) +{ + //! @todo + m_currentLocalSortColumn = col; + m_localSortingOrder = order; +} + +void KexiFormScrollView::sortColumnInternal(int col, int order) +{ + Q_UNUSED( col ); + Q_UNUSED( order ); + //! @todo +} + +void KexiFormScrollView::updateGUIAfterSorting() +{ + //! @todo +} + +void KexiFormScrollView::createEditor(int row, int col, const QString& addText, + bool removeOld) +{ + Q_UNUSED( row ); + Q_UNUSED( addText ); + Q_UNUSED( removeOld ); + + if (isReadOnly()) { + kexipluginsdbg << "KexiFormScrollView::createEditor(): DATA IS READ ONLY!"<<endl; + return; + } + if (column( col )->isReadOnly()) { + kexipluginsdbg << "KexiFormScrollView::createEditor(): COL IS READ ONLY!"<<endl; + return; + } + + //! @todo + const bool startRowEdit = !m_rowEditing; //remember if we're starting row edit + + if (!m_rowEditing) { + //we're starting row editing session + m_data->clearRowEditBuffer(); + + m_rowEditing = true; + //indicate on the vheader that we are editing: + if (m_verticalHeader) + m_verticalHeader->setEditRow(m_curRow); + if (isInsertingEnabled() && m_currentItem==m_insertItem) { + //we should know that we are in state "new row editing" + m_newRowEditing = true; + //'insert' row editing: show another row after that: + m_data->append( m_insertItem ); + //new empty insert item + m_insertItem = m_data->createItem(); //new KexiTableItem(dataColumns()); +// updateContents(); + if (m_verticalHeader) + m_verticalHeader->addLabel(); +// m_verticalHeaderAlreadyAdded = true; + updateWidgetContentsSize(); + //refr. current and next row +// updateContents(columnPos(0), rowPos(row), viewport()->width(), d->rowHeight*2); +//js: warning this breaks behaviour (cursor is skipping, etc.): qApp->processEvents(500); +// ensureVisible(columnPos(m_curCol), rowPos(row+1)+d->rowHeight-1, columnWidth(m_curCol), d->rowHeight); + +// m_verticalHeader->setOffset(contentsY()); + } + } + + m_editor = editor(col); //m_dataItems.at(col); + if (!m_editor) + return; + + if (startRowEdit) { + recordNavigator()->showEditingIndicator(true); +// recordNavigator()->updateButtons(); //refresh 'next btn' + + emit rowEditStarted(m_curRow); + } +} + +KexiDataItemInterface *KexiFormScrollView::editor( int col, bool ignoreMissingEditor ) +{ + Q_UNUSED( ignoreMissingEditor ); + + if (!m_data || col<0 || col>=columns()) + return 0; + + return dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedDataAwareWidgets()->at( col )); +// KexiFormDataItemInterface *item = m_dataItems.at(col); + //return item; + +/* + KexiTableViewColumn *tvcol = m_data->column(col); +// int t = tvcol->field->type(); + + //find the editor for this column + KexiDataItemInterface *editor = d->editors[ tvcol ]; + if (editor) + return editor; + + //not found: create +// editor = KexiCellEditorFactory::createEditor(*m_data->column(col)->field, this); + editor = KexiCellEditorFactory::createEditor(*m_data->column(col), this); + if (!editor) {//create error! + if (!ignoreMissingEditor) { + //js TODO: show error??? + cancelRowEdit(); + } + return 0; + } + editor->hide(); + connect(editor,SIGNAL(editRequested()),this,SLOT(slotEditRequested())); + connect(editor,SIGNAL(cancelRequested()),this,SLOT(cancelEditor())); + connect(editor,SIGNAL(acceptRequested()),this,SLOT(acceptEditor())); + + editor->resize(columnWidth(col)-1, rowHeight()-1); + editor->installEventFilter(this); + if (editor->widget()) + editor->widget()->installEventFilter(this); + //store + d->editors.insert( tvcol, editor ); + return editor;*/ +} + +void KexiFormScrollView::editorShowFocus( int row, int col ) +{ + Q_UNUSED( row ); + Q_UNUSED( col ); + //! @todo +// if (m_currentItem) +// m_provider->fillDataItems(*m_currentItem); +} + +void KexiFormScrollView::updateCell(int row, int col) +{ + Q_UNUSED( row ); + Q_UNUSED( col ); + //! @todo +} + +void KexiFormScrollView::updateCurrentCell() +{ +} + +void KexiFormScrollView::updateRow(int row) +{ + Q_UNUSED(row) + //! @todo +} + +void KexiFormScrollView::updateWidgetContents() +{ + //! @todo +} + +void KexiFormScrollView::updateWidgetContentsSize() +{ + //! @todo +} + +void KexiFormScrollView::updateWidgetScrollBars() +{ + //! @todo +} + +void KexiFormScrollView::slotRowRepaintRequested(KexiTableItem& item) +{ + Q_UNUSED( item ); + //! @todo +} + +/*void KexiFormScrollView::slotAboutToDeleteRow(KexiTableItem& item, + KexiDB::ResultInfo* result, bool repaint) +{ + //! @todo +}*/ + +/*void KexiFormScrollView::slotRowDeleted() +{ + //! @todo +}*/ + +void KexiFormScrollView::slotRowInserted(KexiTableItem *item, bool repaint) +{ + Q_UNUSED( item ); + Q_UNUSED( repaint ); + //! @todo +} + +void KexiFormScrollView::slotRowInserted(KexiTableItem *item, uint row, bool repaint) +{ + Q_UNUSED( item ); + Q_UNUSED( row ); + Q_UNUSED( repaint ); + //! @todo +} + +void KexiFormScrollView::slotRowsDeleted( const QValueList<int> & ) +{ + //! @todo +} + +KexiDBForm* KexiFormScrollView::dbFormWidget() const +{ + return dynamic_cast<KexiDBForm*>(m_widget); +} + +int KexiFormScrollView::columns() const +{ + return dbFormWidget()->orderedDataAwareWidgets()->count(); //m_dataItems.count(); +} + +/*uint KexiFormScrollView::fieldNumberForColumn(int col) +{ + KexiFormDataItemInterface *item = dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedDataAwareWidgets()->at( col )); + if (!item) + return -1; + KexiFormDataItemInterfaceToIntMap::ConstIterator it(m_fieldNumbersForDataItems.find( item )); + return it!=m_fieldNumbersForDataItems.constEnd() ? it.data() : -1; +}*/ + +bool KexiFormScrollView::columnEditable(int col) +{ + kexipluginsdbg << "KexiFormScrollView::columnEditable(" << col << ")" << endl; + foreach_list (QPtrListIterator<KexiFormDataItemInterface>, it, m_dataItems) { + kexipluginsdbg << (dynamic_cast<QWidget*>(it.current()) ? dynamic_cast<QWidget*>(it.current())->name() : "" ) + << " " << it.current()->dataSource() << endl; + } + kexipluginsdbg << "-- focus widgets --" << endl; + foreach_list (QPtrListIterator<QWidget>, it, *dbFormWidget()->orderedFocusWidgets()) { + kexipluginsdbg << it.current()->name() << endl; + } + kexipluginsdbg << "-- data-aware widgets --" << endl; + foreach_list (QPtrListIterator<QWidget>, it, *dbFormWidget()->orderedDataAwareWidgets()) { + kexipluginsdbg << it.current()->name() << endl; + } + + //int index = dbFormWidget()->indexForDataItem( item ); +// KexiFormDataItemInterface *item1 = dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedFocusWidgets()->at( col )); + KexiFormDataItemInterface *item = dynamic_cast<KexiFormDataItemInterface*>(dbFormWidget()->orderedDataAwareWidgets()->at( col )); + + if (!item || item->isReadOnly()) + return false; + +// KexiFormDataItemInterfaceToIntMap::ConstIterator it(m_fieldNumbersForDataItems.find( item )); +// return KexiDataAwareObjectInterface::columnEditable( it!=m_fieldNumbersForDataItems.constEnd() ? it.data() : -1 ); + return KexiDataAwareObjectInterface::columnEditable( col ); +} + +void KexiFormScrollView::valueChanged(KexiDataItemInterface* item) +{ + if (!item) + return; + //only signal start editing when no row editing was started already + kexipluginsdbg << "** KexiFormScrollView::valueChanged(): editedItem=" + << (dbFormWidget()->editedItem ? dbFormWidget()->editedItem->value().toString() : QString::null) + << ", " + << (item ? item->value().toString() : QString::null) + << endl; + if (dbFormWidget()->editedItem!=item) { + kexipluginsdbg << "**>>> dbFormWidget()->editedItem = dynamic_cast<KexiFormDataItemInterface*>(item)" << endl; + dbFormWidget()->editedItem = dynamic_cast<KexiFormDataItemInterface*>(item); + startEditCurrentCell(); + } + fillDuplicatedDataItems(dynamic_cast<KexiFormDataItemInterface*>(item), item->value()); + + //value changed: clear 'default value' mode (e.g. a blue italic text) + dynamic_cast<KexiFormDataItemInterface*>(item)->setDisplayDefaultValue(dynamic_cast<QWidget*>(item), false); +} + +bool KexiFormScrollView::cursorAtNewRow() const +{ + return isInsertingEnabled() && ( m_currentItem==m_insertItem || m_newRowEditing ); +} + +void KexiFormScrollView::initDataContents() +{ + KexiDataAwareObjectInterface::initDataContents(); + + if (m_preview) { +//! @todo here we can react if user wanted to show the navigator + setRecordNavigatorVisible(m_data); + recordNavigator()->setEnabled(m_data); + if (m_data) { + recordNavigator()->setEditingIndicatorEnabled( !isReadOnly() ); + recordNavigator()->showEditingIndicator(false); + } + + dbFormWidget()->updateReadOnlyFlags(); + } +} + +KexiTableViewColumn* KexiFormScrollView::column(int col) +{ + const int id = fieldNumberForColumn(col); + return (id >= 0) ? m_data->column( id ) : 0; +} + +bool KexiFormScrollView::shouldDisplayDefaultValueForItem(KexiFormDataItemInterface* itemIface) const +{ + return cursorAtNewRow() + && !itemIface->columnInfo()->field->defaultValue().isNull() +//?? && (m_editor ? m_editor->value()==itemIface->columnInfo()->field->defaultValue() : true) + && !itemIface->columnInfo()->field->isAutoIncrement(); // default value defined +} + +bool KexiFormScrollView::cancelEditor() +{ + if (!dynamic_cast<KexiFormDataItemInterface*>(m_editor)) + return false; + + if (m_errorMessagePopup) + m_errorMessagePopup->close(); + + KexiFormDataItemInterface *itemIface = dynamic_cast<KexiFormDataItemInterface*>(m_editor); + itemIface->undoChanges(); + + const bool displayDefaultValue = shouldDisplayDefaultValueForItem(itemIface); + // now disable/enable "display default value" if needed (do it after setValue(), before setValue() turns it off) + if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue) + itemIface->setDisplayDefaultValue( dynamic_cast<QWidget*>(itemIface), displayDefaultValue ); + + fillDuplicatedDataItems(itemIface, m_editor->value()); + + // this will clear editor pointer and close message popup (if present) + return KexiDataAwareObjectInterface::cancelEditor(); +} + +void KexiFormScrollView::updateAfterCancelRowEdit() +{ + for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) { + if (dynamic_cast<QWidget*>(it.current())) { + kexipluginsdbg << "KexiFormScrollView::updateAfterCancelRowEdit(): " + << dynamic_cast<QWidget*>(it.current())->className() << " " + << dynamic_cast<QWidget*>(it.current())->name() << endl; + } + KexiFormDataItemInterface *itemIface = it.current(); + const bool displayDefaultValue = shouldDisplayDefaultValueForItem(itemIface); + itemIface->undoChanges(); + if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue) + itemIface->setDisplayDefaultValue( dynamic_cast<QWidget*>(itemIface), displayDefaultValue ); + } + recordNavigator()->showEditingIndicator(false); + dbFormWidget()->editedItem = 0; +} + +void KexiFormScrollView::updateAfterAcceptRowEdit() +{ + if (!m_currentItem) + return; + recordNavigator()->showEditingIndicator(false); + dbFormWidget()->editedItem = 0; + //update visible data because there could be auto-filled (eg. autonumber) fields + fillDataItems(*m_currentItem, cursorAtNewRow()); + m_previousItem = m_currentItem; +} + +void KexiFormScrollView::beforeSwitchView() +{ + m_editor = 0; +} + +void KexiFormScrollView::refreshContentsSize() +{ + KexiScrollView::refreshContentsSize(); + //only clear cmd history when KexiScrollView::refreshContentsSizeLater() has been called + if (!m_preview && sender()==&m_delayedResize) { + if (m_form) + m_form->clearCommandHistory(); + } +} + +void KexiFormScrollView::handleDataWidgetAction(const QString& actionName) +{ + QWidget *w = focusWidget(); + KexiFormDataItemInterface *item = 0; + while (w) { + item = dynamic_cast<KexiFormDataItemInterface*>(w); + if (item) + break; + w = w->parentWidget(); + } + if (item) + item->handleAction(actionName); +} + +void KexiFormScrollView::copySelection() +{ + handleDataWidgetAction("edit_copy"); +} + +void KexiFormScrollView::cutSelection() +{ + handleDataWidgetAction("edit_cut"); +} + +void KexiFormScrollView::paste() +{ + handleDataWidgetAction("edit_paste"); +} + +int KexiFormScrollView::lastVisibleRow() const +{ +//! @todo unimplemented for now, this will be used for continuous forms + return -1; +} + +#include "kexiformscrollview.moc" diff --git a/kexi/plugins/forms/kexiformscrollview.h b/kexi/plugins/forms/kexiformscrollview.h new file mode 100644 index 00000000..12315761 --- /dev/null +++ b/kexi/plugins/forms/kexiformscrollview.h @@ -0,0 +1,297 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-2006 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 KEXIFORMSCROLLVIEW_H +#define KEXIFORMSCROLLVIEW_H + +#include "kexidataprovider.h" +#include "kexiformeventhandler.h" +#include "widgets/kexidbform.h" +#include <widget/kexiscrollview.h> +#include <widget/utils/kexirecordnavigator.h> +#include <widget/utils/kexisharedactionclient.h> +#include <widget/tableview/kexidataawareobjectiface.h> + +//! @short KexiFormScrollView class provides a widget for displaying data in a form view +/*! This class also implements: + - record navigation handling (KexiRecordNavigatorHandler) + - shared actions handling (KexiSharedActionClient) + - data-aware behaviour (KexiDataAwareObjectInterface) + - data provider bound to data-aware widgets (KexiFormDataProvider) + + @see KexiTableView +*/ +class KEXIFORMUTILS_EXPORT KexiFormScrollView : + public KexiScrollView, + public KexiRecordNavigatorHandler, + public KexiSharedActionClient, + public KexiDataAwareObjectInterface, + public KexiFormDataProvider, + public KexiFormEventHandler +{ + Q_OBJECT + KEXI_DATAAWAREOBJECTINTERFACE + + public: + KexiFormScrollView(QWidget *parent, bool preview); + virtual ~KexiFormScrollView(); + + void setForm(KFormDesigner::Form *form) { m_form = form; } + + /*! Reimplemented from KexiDataAwareObjectInterface + for checking 'readOnly' flag from a widget + ('readOnly' flag from data member is still checked though). */ + virtual bool columnEditable(int col); + + /*! \return number of visible columns in this view. + There can be a number of duplicated columns defined, + so columns() can return greater or smaller number than dataColumns(). */ + virtual int columns() const; + + /*! \return column information for column number \a col. + Reimplemented for KexiDataAwareObjectInterface: + column data corresponding to widget number is used here + (see fieldNumberForColumn()). */ + virtual KexiTableViewColumn* column(int col); + + /*! \return field number within data model connected to a data-aware + widget at column \a col. */ + virtual int fieldNumberForColumn(int col) { + KexiFormDataItemInterface *item = dynamic_cast<KexiFormDataItemInterface*>( + dbFormWidget()->orderedDataAwareWidgets()->at( col )); + if (!item) + return -1; + KexiFormDataItemInterfaceToIntMap::ConstIterator it(m_fieldNumbersForDataItems.find( item )); + return it!=m_fieldNumbersForDataItems.constEnd() ? (int)it.data() : -1; + } + + /*! @internal Used by KexiFormView in view switching. */ + void beforeSwitchView(); + + /*! \return last row visible on the screen (counting from 0). + The returned value is guaranteed to be smaller or equal to currentRow() or -1 + if there are no rows. + Implemented for KexiDataAwareObjectInterface. */ +//! @todo unimplemented for now, this will be used for continuous forms + virtual int lastVisibleRow() const; + + /*! \return vertical scrollbar. Implemented for KexiDataAwareObjectInterface. */ + virtual QScrollBar* verticalScrollBar() const { return KexiScrollView::verticalScrollBar(); } + + public slots: + /*! Reimplemented to update resize policy. */ + virtual void show(); + + //virtual void setFocus(); + + //! Implementation for KexiDataAwareObjectInterface + //! \return arbitraty value of 10. + virtual int rowsPerPage() const; + + //! Implementation for KexiDataAwareObjectInterface + virtual void ensureCellVisible(int row, int col/*=-1*/); + + virtual void moveToRecordRequested(uint r); + virtual void moveToLastRecordRequested(); + virtual void moveToPreviousRecordRequested(); + virtual void moveToNextRecordRequested(); + virtual void moveToFirstRecordRequested(); + virtual void addNewRecordRequested() { KexiDataAwareObjectInterface::addNewRecordRequested(); } + + /*! Cancels changes made to the currently active editor. + Reverts the editor's value to old one. + \return true on success or false on failure (e.g. when editor does not exist) */ + virtual bool cancelEditor(); + + public slots: + /*! Reimplemented to also clear command history right after final resize. */ + virtual void refreshContentsSize(); + + /*! Handles verticalScrollBar()'s valueChanged(int) signal. + Called when vscrollbar's value has been changed. */ +//! @todo unused for now, will be used for continuous forms + virtual void vScrollBarValueChanged(int v) { KexiDataAwareObjectInterface::vScrollBarValueChanged(v); } + + /*! Handles sliderReleased() signal of the verticalScrollBar(). Used to hide the "row number" tooltip. */ +//! @todo unused for now, will be used for continuous forms + virtual void vScrollBarSliderReleased() { KexiDataAwareObjectInterface::vScrollBarSliderReleased(); } + + /*! Handles timeout() signal of the m_scrollBarTipTimer. If the tooltip is visible, + m_scrollBarTipTimerCnt is set to 0 and m_scrollBarTipTimerCnt is restarted; + else the m_scrollBarTipTimerCnt is just set to 0.*/ +//! @todo unused for now, will be used for continuous forms + virtual void scrollBarTipTimeout() { KexiDataAwareObjectInterface::scrollBarTipTimeout(); } + + signals: + virtual void itemChanged(KexiTableItem *, int row, int col); + virtual void itemChanged(KexiTableItem *, int row, int col, QVariant oldValue); + virtual void itemDeleteRequest(KexiTableItem *, int row, int col); + virtual void currentItemDeleteRequest(); + virtual void newItemAppendedForAfterDeletingInSpreadSheetMode(); //!< does nothing + virtual void dataRefreshed(); + virtual void dataSet( KexiTableViewData *data ); + virtual void itemSelected(KexiTableItem *); + virtual void cellSelected(int col, int row); + virtual void sortedColumnChanged(int col); + virtual void rowEditStarted(int row); + virtual void rowEditTerminated(int row); + virtual void reloadActions(); + + protected slots: + void slotResizingStarted(); + + //! Handles KexiTableViewData::rowRepaintRequested() signal + virtual void slotRowRepaintRequested(KexiTableItem& item); + + //! Handles KexiTableViewData::aboutToDeleteRow() signal. Prepares info for slotRowDeleted(). + virtual void slotAboutToDeleteRow(KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint) + { KexiDataAwareObjectInterface::slotAboutToDeleteRow(item, result, repaint); } + + //! Handles KexiTableViewData::rowDeleted() signal to repaint when needed. + virtual void slotRowDeleted() { KexiDataAwareObjectInterface::slotRowDeleted(); } + + //! Handles KexiTableViewData::rowInserted() signal to repaint when needed. + virtual void slotRowInserted(KexiTableItem *item, bool repaint); + + //! Like above, not db-aware version + virtual void slotRowInserted(KexiTableItem *item, uint row, bool repaint); + + virtual void slotRowsDeleted( const QValueList<int> & ); + + virtual void slotDataDestroying() { KexiDataAwareObjectInterface::slotDataDestroying(); } + + /*! Reloads data for this widget. + Handles KexiTableViewData::reloadRequested() signal. */ + virtual void reloadData() { KexiDataAwareObjectInterface::reloadData(); } + + //! Copy current selection to a clipboard (e.g. cell) + virtual void copySelection(); + + //! Cut current selection to a clipboard (e.g. cell) + virtual void cutSelection(); + + //! Paste current clipboard contents (e.g. to a cell) + virtual void paste(); + + protected: + //! Implementation for KexiDataAwareObjectInterface + virtual void clearColumnsInternal(bool repaint); + + //! Implementation for KexiDataAwareObjectInterface + virtual void addHeaderColumn(const QString& caption, const QString& description, + const QIconSet& icon, int width); + + //! Implementation for KexiDataAwareObjectInterface + virtual int currentLocalSortingOrder() const; + + //! Implementation for KexiDataAwareObjectInterface + virtual int currentLocalSortColumn() const; + + //! Implementation for KexiDataAwareObjectInterface + virtual void setLocalSortingOrder(int col, int order); + + //! Implementation for KexiDataAwareObjectInterface + void sortColumnInternal(int col, int order = 0); + + //! Implementation for KexiDataAwareObjectInterface + virtual void updateGUIAfterSorting(); + + //! Implementation for KexiDataAwareObjectInterface + virtual void createEditor(int row, int col, const QString& addText = QString::null, + bool removeOld = false); + + //! Implementation for KexiDataAwareObjectInterface + virtual KexiDataItemInterface *editor( int col, bool ignoreMissingEditor = false ); + + //! Implementation for KexiDataAwareObjectInterface + virtual void editorShowFocus( int row, int col ); + + /*! Implementation for KexiDataAwareObjectInterface + Redraws specified cell. */ + virtual void updateCell(int row, int col); + + /*! Redraws the current cell. Implemented after KexiDataAwareObjectInterface. */ + virtual void updateCurrentCell(); + + /*! Implementation for KexiDataAwareObjectInterface + Redraws all cells of specified row. */ + virtual void updateRow(int row); + + /*! Implementation for KexiDataAwareObjectInterface + Updates contents of the widget. Just call update() here on your widget. */ + virtual void updateWidgetContents(); + + /*! Implementation for KexiDataAwareObjectInterface + Implementation for KexiDataAwareObjectInterface + Updates widget's contents size e.g. using QScrollView::resizeContents(). */ + virtual void updateWidgetContentsSize(); + + /*! Implementation for KexiDataAwareObjectInterface + Updates scrollbars of the widget. + QScrollView::updateScrollbars() will be usually called here. */ + virtual void updateWidgetScrollBars(); + + KexiDBForm* dbFormWidget() const; + + //! Reimplemented from KexiFormDataProvider. Reaction for change of \a item. + virtual void valueChanged(KexiDataItemInterface* item); + + /*! Reimplemented from KexiFormDataProvider. + \return information whether we're currently at new row or now. + This can be used e.g. by data-aware widgets to determine if "(autonumber)" + label should be displayed. */ + virtual bool cursorAtNewRow() const; + + //! Implementation for KexiDataAwareObjectInterface + //! Called by KexiDataAwareObjectInterface::setCursorPosition() + //! if cursor's position is really changed. + inline virtual void selectCellInternal(); + + /*! Reimplementation: used to refresh "editing indicator" visibility. */ + virtual void initDataContents(); + + /*! @internal + Updates row appearance after canceling row edit. + Reimplemented from KexiDataAwareObjectInterface: just undoes changes for every data item. + Used by cancelRowEdit(). */ + virtual void updateAfterCancelRowEdit(); + + /*! @internal + Updates row appearance after accepting row edit. + Reimplemented from KexiDataAwareObjectInterface: just clears 'edit' indicator. + Used by cancelRowEdit(). */ + virtual void updateAfterAcceptRowEdit(); + + /*! @internal + Used to invoke copy/paste/cut etc. actions at the focused widget's level. */ + void handleDataWidgetAction(const QString& actionName); + + /*! @internal */ + bool shouldDisplayDefaultValueForItem(KexiFormDataItemInterface* itemIface) const; + + //virtual bool focusNextPrevChild( bool next ); + + KFormDesigner::Form *m_form; + int m_currentLocalSortColumn, m_localSortingOrder; + //! Used in selectCellInternal() to avoid fetching the same record twice + KexiTableItem *m_previousItem; +}; + +#endif diff --git a/kexi/plugins/forms/kexiformview.cpp b/kexi/plugins/forms/kexiformview.cpp new file mode 100644 index 00000000..7e52e5b6 --- /dev/null +++ b/kexi/plugins/forms/kexiformview.cpp @@ -0,0 +1,1278 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-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 "kexiformview.h" + +#include <qobjectlist.h> +#include <qfileinfo.h> + +#include <formeditor/form.h> +#include <formeditor/formIO.h> +#include <formeditor/formmanager.h> +#include <formeditor/objecttree.h> +#include <formeditor/container.h> +#include <formeditor/widgetpropertyset.h> +#include <formeditor/commands.h> +#include <formeditor/widgetwithsubpropertiesinterface.h> +#include <formeditor/objecttree.h> + +#include <kexi.h> +#include <kexidialogbase.h> +#include <kexidragobjects.h> +#include <kexidb/field.h> +#include <kexidb/fieldlist.h> +#include <kexidb/connection.h> +#include <kexidb/cursor.h> +#include <kexidb/utils.h> +#include <kexidb/preparedstatement.h> +#include <tableview/kexitableitem.h> +#include <tableview/kexitableviewdata.h> +#include <widget/kexipropertyeditorview.h> +#include <widget/kexiqueryparameters.h> +#include <kexiutils/utils.h> + +#include <koproperty/set.h> +#include <koproperty/property.h> + +#include "widgets/kexidbform.h" +#include "kexiformscrollview.h" +#include "kexidatasourcepage.h" +#include "widgets/kexidbautofield.h" + +#define NO_DSWIZARD + +//! @todo #define KEXI_SHOW_SPLITTER_WIDGET + +KexiFormView::KexiFormView(KexiMainWindow *mainWin, QWidget *parent, + const char *name, bool /*dbAware*/) + : KexiDataAwareView( mainWin, parent, name ) + , m_propertySet(0) + , m_resizeMode(KexiFormView::ResizeDefault) + , m_query(0) + , m_queryIsOwned(false) + , m_cursor(0) +// , m_firstFocusWidget(0) +{ + m_delayedFormContentsResizeOnShow = 0; + + QHBoxLayout *l = new QHBoxLayout(this); + l->setAutoAdd(true); + + m_scrollView = new KexiFormScrollView(this, viewMode()==Kexi::DataViewMode); + +//moved setViewWidget(m_scrollView); +// m_scrollView->show(); + + m_dbform = new KexiDBForm(m_scrollView->viewport(), m_scrollView, name/*, conn*/); +// m_dbform->resize( m_scrollView->viewport()->size() - QSize(20, 20) ); +// m_dbform->resize(QSize(400, 300)); + m_scrollView->setWidget(m_dbform); + m_scrollView->setResizingEnabled(viewMode()!=Kexi::DataViewMode); + +// initForm(); + + if (viewMode()==Kexi::DataViewMode) { + m_scrollView->recordNavigator()->setRecordHandler( m_scrollView ); + m_scrollView->viewport()->setPaletteBackgroundColor(m_dbform->palette().active().background()); +//moved to formmanager connect(formPart()->manager(), SIGNAL(noFormSelected()), SLOT(slotNoFormSelected())); + } + else + { + connect(KFormDesigner::FormManager::self(), SIGNAL(propertySetSwitched(KoProperty::Set*, bool, const QCString&)), + this, SLOT(slotPropertySetSwitched(KoProperty::Set*, bool, const QCString&))); + connect(KFormDesigner::FormManager::self(), SIGNAL(dirty(KFormDesigner::Form *, bool)), + this, SLOT(slotDirty(KFormDesigner::Form *, bool))); + + connect(m_dbform, SIGNAL(handleDragMoveEvent(QDragMoveEvent*)), + this, SLOT(slotHandleDragMoveEvent(QDragMoveEvent*))); + connect(m_dbform, SIGNAL(handleDropEvent(QDropEvent*)), + this, SLOT(slotHandleDropEvent(QDropEvent*))); + + // action stuff + plugSharedAction("formpart_taborder", KFormDesigner::FormManager::self(), SLOT(editTabOrder())); + plugSharedAction("formpart_adjust_size", KFormDesigner::FormManager::self(), SLOT(adjustWidgetSize())); +//TODO plugSharedAction("formpart_pixmap_collection", formPart()->manager(), SLOT(editFormPixmapCollection())); +//TODO plugSharedAction("formpart_connections", formPart()->manager(), SLOT(editConnections())); + + plugSharedAction("edit_copy", KFormDesigner::FormManager::self(), SLOT(copyWidget())); + plugSharedAction("edit_cut", KFormDesigner::FormManager::self(), SLOT(cutWidget())); + plugSharedAction("edit_paste", KFormDesigner::FormManager::self(), SLOT(pasteWidget())); + plugSharedAction("edit_delete", KFormDesigner::FormManager::self(), SLOT(deleteWidget())); + plugSharedAction("edit_select_all", KFormDesigner::FormManager::self(), SLOT(selectAll())); + plugSharedAction("formpart_clear_contents", KFormDesigner::FormManager::self(), SLOT(clearWidgetContent())); + plugSharedAction("edit_undo", KFormDesigner::FormManager::self(), SLOT(undo())); + plugSharedAction("edit_redo", KFormDesigner::FormManager::self(), SLOT(redo())); + + plugSharedAction("formpart_layout_menu", KFormDesigner::FormManager::self(), 0 ); + plugSharedAction("formpart_layout_hbox", KFormDesigner::FormManager::self(), SLOT(layoutHBox()) ); + plugSharedAction("formpart_layout_vbox", KFormDesigner::FormManager::self(), SLOT(layoutVBox()) ); + plugSharedAction("formpart_layout_grid", KFormDesigner::FormManager::self(), SLOT(layoutGrid()) ); +#ifdef KEXI_SHOW_SPLITTER_WIDGET + plugSharedAction("formpart_layout_hsplitter", KFormDesigner::FormManager::self(), SLOT(layoutHSplitter()) ); + plugSharedAction("formpart_layout_vsplitter", KFormDesigner::FormManager::self(), SLOT(layoutVSplitter()) ); +#endif + plugSharedAction("formpart_break_layout", KFormDesigner::FormManager::self(), SLOT(breakLayout()) ); + + plugSharedAction("formpart_format_raise", KFormDesigner::FormManager::self(), SLOT(bringWidgetToFront()) ); + plugSharedAction("formpart_format_lower", KFormDesigner::FormManager::self(), SLOT(sendWidgetToBack()) ); + + plugSharedAction("other_widgets_menu", KFormDesigner::FormManager::self(), 0 ); + setAvailable("other_widgets_menu", true); + + plugSharedAction("formpart_align_menu", KFormDesigner::FormManager::self(), 0 ); + plugSharedAction("formpart_align_to_left", KFormDesigner::FormManager::self(),SLOT(alignWidgetsToLeft()) ); + plugSharedAction("formpart_align_to_right", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToRight()) ); + plugSharedAction("formpart_align_to_top", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToTop()) ); + plugSharedAction("formpart_align_to_bottom", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToBottom()) ); + plugSharedAction("formpart_align_to_grid", KFormDesigner::FormManager::self(), SLOT(alignWidgetsToGrid()) ); + + plugSharedAction("formpart_adjust_size_menu", KFormDesigner::FormManager::self(), 0 ); + plugSharedAction("formpart_adjust_to_fit", KFormDesigner::FormManager::self(), SLOT(adjustWidgetSize()) ); + plugSharedAction("formpart_adjust_size_grid", KFormDesigner::FormManager::self(), SLOT(adjustSizeToGrid()) ); + plugSharedAction("formpart_adjust_height_small", KFormDesigner::FormManager::self(), SLOT(adjustHeightToSmall()) ); + plugSharedAction("formpart_adjust_height_big", KFormDesigner::FormManager::self(), SLOT(adjustHeightToBig()) ); + plugSharedAction("formpart_adjust_width_small", KFormDesigner::FormManager::self(), SLOT(adjustWidthToSmall()) ); + plugSharedAction("formpart_adjust_width_big", KFormDesigner::FormManager::self(), SLOT(adjustWidthToBig()) ); + + plugSharedAction("format_font", KFormDesigner::FormManager::self(), SLOT(changeFont()) ); + } + + initForm(); + + KexiDataAwareView::init( m_scrollView, m_scrollView, m_scrollView, + /* skip data-awarness if design mode */ viewMode()==Kexi::DesignViewMode ); + + connect(this, SIGNAL(focus(bool)), this, SLOT(slotFocus(bool))); + /// @todo skip this if ther're no borders +// m_dbform->resize( m_dbform->size()+QSize(m_scrollView->verticalScrollBar()->width(), m_scrollView->horizontalScrollBar()->height()) ); +} + +KexiFormView::~KexiFormView() +{ + if (m_cursor) { + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + conn->deleteCursor(m_cursor); + m_cursor = 0; + } + deleteQuery(); + + // Important: form window is closed. + // Set property set to 0 because there is *only one* instance of a property set class + // in Kexi, so the main window wouldn't know the set in fact has been changed. + m_propertySet = 0; + propertySetSwitched(); +} + +void +KexiFormView::deleteQuery() +{ + if (m_cursor) { + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + conn->deleteCursor(m_cursor); + m_cursor = 0; + } + + if (m_queryIsOwned) { + delete m_query; + } else { +//! @todo remove this shared query from listened queries list + } + m_query = 0; +} + +KFormDesigner::Form* +KexiFormView::form() const +{ + if(viewMode()==Kexi::DataViewMode) + return tempData()->previewForm; + else + return tempData()->form; +} + +void +KexiFormView::setForm(KFormDesigner::Form *f) +{ + if(viewMode()==Kexi::DataViewMode) + tempData()->previewForm = f; + else + tempData()->form = f; +} + +void +KexiFormView::initForm() +{ + setForm( new KFormDesigner::Form(KexiFormPart::library(), 0, viewMode()==Kexi::DesignViewMode) ); +// if (viewMode()==Kexi::DataViewMode) + //form()->setDesignMode(false); + form()->createToplevel(m_dbform, m_dbform); + + if (viewMode()==Kexi::DesignViewMode) { + //we want to be informed about executed commands + connect(form()->commandHistory(), SIGNAL(commandExecuted()), + KFormDesigner::FormManager::self(), SLOT(slotHistoryCommandExecuted())); + } + + const bool newForm = parentDialog()->id() < 0; + + KexiDB::FieldList *fields = 0; + if (newForm) { + // Show the form wizard if this is a new Form +#ifndef NO_DSWIZARD + KexiDataSourceWizard *w = new KexiDataSourceWizard(mainWin(), (QWidget*)mainWin(), "datasource_wizard"); + if(!w->exec()) + fields = 0; + else + fields = w->fields(); + delete w; +#endif + } + + if(fields) + { + QDomDocument dom; + formPart()->generateForm(fields, dom); + KFormDesigner::FormIO::loadFormFromDom(form(), m_dbform, dom); + //! @todo handle errors + } + else + loadForm(); + + if(form()->autoTabStops()) + form()->autoAssignTabStops(); + + //collect tab order information + m_dbform->updateTabStopsOrder(form()); + +// if (m_dbform->orderedFocusWidgets()->first()) + // m_scrollView->setFocusProxy( m_dbform->orderedFocusWidgets()->first() ); + + KFormDesigner::FormManager::self()->importForm(form(), viewMode()==Kexi::DataViewMode); + m_scrollView->setForm(form()); + +// m_dbform->updateTabStopsOrder(form()); +// QSize s = m_dbform->size(); +// QApplication::sendPostedEvents(); +// m_scrollView->resize( s ); +// m_dbform->resize(s); + m_scrollView->refreshContentsSize(); +// m_scrollView->refreshContentsSizeLater(true,true); + + if (newForm && !fields) { + /* Our form's area will be resized more than once. + Let's resize form widget itself later. */ + m_delayedFormContentsResizeOnShow = 3; + } + + updateDataSourcePage(); + + if (!newForm && viewMode()==Kexi::DesignViewMode) { + form()->clearCommandHistory(); + } +} + +void KexiFormView::updateAutoFieldsDataSource() +{ +//! @todo call this when form's data source is changed + //update autofields: + //-inherit captions + //-inherit data types + //(this data has not been stored in the form) + QString dataSourceString( m_dbform->dataSource() ); + QCString dataSourceMimeTypeString( m_dbform->dataSourceMimeType() ); + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + KexiDB::TableOrQuerySchema tableOrQuery( + conn, dataSourceString.latin1(), dataSourceMimeTypeString=="kexi/table"); + if (!tableOrQuery.table() && !tableOrQuery.query()) + return; + for (KFormDesigner::ObjectTreeDictIterator it(*form()->objectTree()->dict()); + it.current(); ++it) + { + KexiDBAutoField *afWidget = dynamic_cast<KexiDBAutoField*>( it.current()->widget() ); + if (afWidget) { + KexiDB::QueryColumnInfo *colInfo = tableOrQuery.columnInfo( afWidget->dataSource() ); + if (colInfo) { + afWidget->setColumnInfo(colInfo); + //setFieldTypeInternal((int)colInfo->field->type()); + //afWidget->setFieldCaptionInternal(colInfo->captionOrAliasOrName()); + } + } + } +} + +void KexiFormView::updateValuesForSubproperties() +{ +//! @todo call this when form's data source is changed + //update autofields: + //-inherit captions + //-inherit data types + //(this data has not been stored in the form) + QString dataSourceString( m_dbform->dataSource() ); + QCString dataSourceMimeTypeString( m_dbform->dataSourceMimeType() ); + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + KexiDB::TableOrQuerySchema tableOrQuery( + conn, dataSourceString.latin1(), dataSourceMimeTypeString=="kexi/table"); + if (!tableOrQuery.table() && !tableOrQuery.query()) + return; + + for (KFormDesigner::ObjectTreeDictIterator it(*form()->objectTree()->dict()); + it.current(); ++it) + { + // (delayed) set values for subproperties +//! @todo this could be at the KFD level, but KFD is going to be merged anyway with kexiforms, right? + KFormDesigner::WidgetWithSubpropertiesInterface* subpropIface + = dynamic_cast<KFormDesigner::WidgetWithSubpropertiesInterface*>( it.current()->widget() ); + if (subpropIface && subpropIface->subwidget() && it.current()->subproperties() ) { + QWidget *subwidget = subpropIface->subwidget(); + QMap<QString, QVariant>* subprops = it.current()->subproperties(); + for (QMapConstIterator<QString, QVariant> subpropIt = subprops->constBegin(); subpropIt!=subprops->constEnd(); ++subpropIt) { + kexipluginsdbg << "KexiFormView::loadForm(): delayed setting of the subproperty: widget=" + << it.current()->widget()->name() << " prop=" << subpropIt.key() << " val=" << subpropIt.data() << endl; + + const int count = subwidget->metaObject()->findProperty(subpropIt.key().latin1(), true); + const QMetaProperty *meta = count!=-1 ? subwidget->metaObject()->property(count, true) : 0; + if (meta) { + // Special case: the property value of type enum (set) but is saved as a string list, + // not as int, so we need to translate it to int. It's been created as such + // by FormIO::readPropertyValue(). Example: "alignment" property. + if (meta->isSetType() && subpropIt.data().type()==QVariant::StringList) { + QStrList keys; + const QStringList list( subpropIt.data().toStringList() ); + for (QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + keys.append((*it).latin1()); + subwidget->setProperty( subpropIt.key().latin1(), meta->keysToValue(keys) ); + } + else { + subwidget->setProperty( subpropIt.key().latin1(), subpropIt.data() ); + } + } + }//for + } + } +} + +//! Used in KexiFormView::loadForm() +static void setUnsavedBLOBIdsForDataViewMode( + QWidget* widget, const QMap<QCString, KexiBLOBBuffer::Id_t>& unsavedLocalBLOBsByName) +{ + if (-1 != widget->metaObject()->findProperty("pixmapId")) { + const KexiBLOBBuffer::Id_t blobID = unsavedLocalBLOBsByName[ widget->name() ]; + if (blobID > 0) + widget->setProperty("pixmapId", (uint /* KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - will be fixed in Qt4*/)blobID); + } + const QObjectList *list = widget->children(); + if (!list) + return; + for (QObjectListIterator it(*list); it.current(); ++it) { + if (dynamic_cast<QWidget*>(it.current())) + setUnsavedBLOBIdsForDataViewMode(dynamic_cast<QWidget*>(it.current()), unsavedLocalBLOBsByName); + } +} + +void +KexiFormView::loadForm() +{ +//@todo also load m_resizeMode ! + + kexipluginsdbg << "KexiFormView::loadForm() Loading the form with id : " << parentDialog()->id() << endl; + // If we are previewing the Form, use the tempData instead of the form stored in the db + if(viewMode()==Kexi::DataViewMode && !tempData()->tempForm.isNull() ) + { + KFormDesigner::FormIO::loadFormFromString(form(), m_dbform, tempData()->tempForm); + setUnsavedBLOBIdsForDataViewMode( m_dbform, tempData()->unsavedLocalBLOBsByName ); + updateAutoFieldsDataSource(); + updateValuesForSubproperties(); + return; + } + + // normal load + QString data; + loadDataBlock(data); + KFormDesigner::FormIO::loadFormFromString(form(), m_dbform, data); + + //"autoTabStops" property is loaded -set it within the form tree as well + form()->setAutoTabStops( m_dbform->autoTabStops() ); + + updateAutoFieldsDataSource(); + updateValuesForSubproperties(); +} + +void +KexiFormView::slotPropertySetSwitched(KoProperty::Set *set, bool forceReload, const QCString& propertyToSelect) +{ +// if (set && parentDialog()!=parentDialog()->mainWin()->currentDialog()) + if (form() != KFormDesigner::FormManager::self()->activeForm()) + return; //this is not the current form view + m_propertySet = set; + if (forceReload) + propertySetReloaded(true/*preservePrevSelection*/, propertyToSelect); + else + propertySetSwitched(); + + formPart()->dataSourcePage()->assignPropertySet(m_propertySet); +} + +tristate +KexiFormView::beforeSwitchTo(int mode, bool &dontStore) +{ + if (mode!=viewMode()) { + if (viewMode()==Kexi::DataViewMode) { + if (!m_scrollView->acceptRowEdit()) + return cancelled; + + m_scrollView->beforeSwitchView(); + } + else { + //remember our pos + tempData()->scrollViewContentsPos + = QPoint(m_scrollView->contentsX(), m_scrollView->contentsY()); + } + } + + // we don't store on db, but in our TempData + dontStore = true; + if(dirty() && (mode == Kexi::DataViewMode) && form()->objectTree()) { + KexiFormPart::TempData* temp = tempData(); + if (!KFormDesigner::FormIO::saveFormToString(form(), temp->tempForm)) + return false; + + //collect blobs from design mode by name for use in data view mode + temp->unsavedLocalBLOBsByName.clear(); + for (QMapConstIterator<QWidget*, KexiBLOBBuffer::Id_t> it = temp->unsavedLocalBLOBs.constBegin(); + it!=temp->unsavedLocalBLOBs.constEnd(); ++it) + { + if (!it.key()) + continue; + temp->unsavedLocalBLOBsByName.insert( it.key()->name(), it.data() ); + } + } + + return true; +} + +tristate +KexiFormView::afterSwitchFrom(int mode) +{ + if (mode == 0 || mode == Kexi::DesignViewMode) { + if (parentDialog()->neverSaved()) { + m_dbform->resize(QSize(400, 300)); + m_scrollView->refreshContentsSizeLater(true,true); + //m_delayedFormContentsResizeOnShow = false; + } + } + + if (mode != 0 && mode != Kexi::DesignViewMode) { + //preserve contents pos after switching to other view + m_scrollView->setContentsPos(tempData()->scrollViewContentsPos.x(), + tempData()->scrollViewContentsPos.y()); + } +// if (mode == Kexi::DesignViewMode) { + //m_scrollView->move(0,0); + //m_scrollView->setContentsPos(0,0); + //m_scrollView->moveChild(m_dbform, 0, 0); +// } + + if((mode == Kexi::DesignViewMode) && viewMode()==Kexi::DataViewMode) { + // The form may have been modified, so we must recreate the preview + delete m_dbform; // also deletes form() + m_dbform = new KexiDBForm(m_scrollView->viewport(), m_scrollView, "KexiDBForm"); + m_scrollView->setWidget(m_dbform); + + initForm(); +//moved to formmanager slotNoFormSelected(); + + //reset position + m_scrollView->setContentsPos(0,0); + m_dbform->move(0,0); + + } + + //update tab stops if needed + if (viewMode()==Kexi::DataViewMode) { +// //propagate current "autoTabStops" property value to the form tree +// form()->setAutoTabStops( m_dbform->autoTabStops() ); + +// if(form()->autoTabStops()) +// form()->autoAssignTabStops(); + } + else { + //set "autoTabStops" property + m_dbform->setAutoTabStops( form()->autoTabStops() ); + } + + if (viewMode() == Kexi::DataViewMode) { +//TMP!! + initDataSource(); + + //handle events for this form + m_scrollView->setMainWidgetForEventHandling(parentDialog()->mainWin(), m_dbform); + + //set focus on 1st focusable widget which has valid dataSource property set + if (!m_dbform->orderedFocusWidgets()->isEmpty()) { +// QWidget *www = focusWidget(); + //if (Kexi::hasParent(this, qApp->focusWidget())) { + KexiUtils::unsetFocusWithReason(qApp->focusWidget(), QFocusEvent::Tab); + //} + + QPtrListIterator<QWidget> it(*m_dbform->orderedFocusWidgets()); + for (;it.current(); ++it) { + KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>(it.current()); + if (iface) + kexipluginsdbg << iface->dataSource() << endl; + if (iface && iface->columnInfo() && !iface->isReadOnly() +/*! @todo add option for skipping autoincremented fields */ + /* also skip autoincremented fields:*/ + && !iface->columnInfo()->field->isAutoIncrement()) //!iface->dataSource().isEmpty() + break; + } + if (!it.current()) //eventually, focus first available widget if nothing other is available + it.toFirst(); + + it.current()->setFocus(); + KexiUtils::setFocusWithReason(it.current(), QFocusEvent::Tab); + m_setFocusInternalOnce = it.current(); + } + + if (m_query) + m_scrollView->selectFirstRow(); + } + + //dirty only if it's a new object + if (mode == 0) + setDirty( parentDialog()->partItem()->neverSaved() ); + + if (mode==Kexi::DataViewMode && viewMode()==Kexi::DesignViewMode) { +// slotPropertySetSwitched +// emit KFormDesigner::FormManager::self()->propertySetSwitched( KFormDesigner::FormManager::self()->propertySet()->set(), true ); + } + + return true; +} + +void KexiFormView::initDataSource() +{ + deleteQuery(); + QString dataSourceString( m_dbform->dataSource() ); + QCString dataSourceMimeTypeString( m_dbform->dataSourceMimeType() ); +//! @todo also handle anonymous (not stored) queries provided as statements here + bool ok = !dataSourceString.isEmpty(); + +/* if (m_previousDataSourceString.lower()==dataSourceString.lower() && !m_cursor) { + //data source changed: delete previous cursor + m_conn->deleteCursor(m_cursor); + m_cursor = 0; + }*/ + + KexiDB::TableSchema *tableSchema = 0; + KexiDB::Connection *conn = 0; + QStringList sources; + bool forceReadOnlyDataSource = false; + + if (ok) { +// m_previousDataSourceString = dataSourceString; + + //collect all data-aware widgets and create query schema + m_scrollView->setMainDataSourceWidget(m_dbform); + sources = m_scrollView->usedDataSources(); + conn = parentDialog()->mainWin()->project()->dbConnection(); + if (dataSourceMimeTypeString.isEmpty() /*table type is the default*/ + || dataSourceMimeTypeString=="kexi/table") + { + tableSchema = conn->tableSchema( dataSourceString ); + if (tableSchema) { + /* We will build a _minimum_ query schema from selected table fields. */ + m_query = new KexiDB::QuerySchema(); + m_queryIsOwned = true; + + if (dataSourceMimeTypeString.isEmpty()) + m_dbform->setDataSourceMimeType("kexi/table"); //update for compatibility + } + } + + if (!tableSchema) { + if (dataSourceMimeTypeString.isEmpty() /*also try to find a query (for compatibility with Kexi<=0.9)*/ + || dataSourceMimeTypeString=="kexi/query") + { + //try to find predefined query schema. + //Note: In general, we could not skip unused fields within this query because + // it can have GROUP BY clause. + //! @todo check if the query could have skipped unused fields (no GROUP BY, no joins, etc.) + m_query = conn->querySchema( dataSourceString ); + m_queryIsOwned = false; + ok = m_query != 0; + if (ok && dataSourceMimeTypeString.isEmpty()) + m_dbform->setDataSourceMimeType("kexi/query"); //update for compatibility + // query results are read-only +//! @todo There can be read-write queries, e.g. simple "SELECT * FROM...". Add a checking function to KexiDB. + forceReadOnlyDataSource = true; + } + else //no other mime types supported + ok = false; + } + } + + QDict<char> invalidSources(997); + if (ok) { + KexiDB::IndexSchema *pkey = tableSchema ? tableSchema->primaryKey() : 0; + if (pkey) { + //always add all fields from table's primary key + // (don't worry about duplicates, unique list will be computed later) + sources += pkey->names(); + kexipluginsdbg << "KexiFormView::initDataSource(): pkey added to data sources: " << pkey->names() << endl; + } + kexipluginsdbg << "KexiFormView::initDataSource(): sources=" << sources << endl; + + uint index = 0; + for (QStringList::ConstIterator it = sources.constBegin(); + it!=sources.constEnd(); ++it, index++) { +/*! @todo add expression support */ + QString fieldName( (*it).lower() ); + //remove "tablename." if it was prepended + if (tableSchema && fieldName.startsWith( tableSchema->name().lower()+"." )) + fieldName = fieldName.mid(tableSchema->name().length()+1); + //remove "queryname." if it was prepended + if (!tableSchema && fieldName.startsWith( m_query->name().lower()+"." )) + fieldName = fieldName.mid(m_query->name().length()+1); + KexiDB::Field *f = tableSchema ? tableSchema->field(fieldName) : m_query->field(fieldName); + if (!f) { +/*! @todo show error */ + //remove this widget from the set of data widgets in the provider +/*! @todo fieldName is ok, but what about expressions? */ + invalidSources.insert( fieldName, (const char*)1 ); // += index; + kexipluginsdbg << "KexiFormView::initDataSource(): invalidSources+=" << index << " (" + << (*it) << ")" << endl; + continue; + } + if (tableSchema) { + if (!m_query->hasField( f )) { + //we're building a new query: add this field + m_query->addField( f ); + } + } + } + if (invalidSources.count()==sources.count()) { + //all data sources are invalid! don't execute the query + deleteQuery(); + } + else { + KexiDB::debug( m_query->parameters() ); + // like in KexiQueryView::executeQuery() + QValueList<QVariant> params; + { + KexiUtils::WaitCursorRemover remover; + params = KexiQueryParameters::getParameters(this, *conn->driver(), *m_query, ok); + } + if (ok) //input cancelled + m_cursor = conn->executeQuery( *m_query, params ); + } + m_scrollView->invalidateDataSources( invalidSources, m_query ); + ok = m_cursor!=0; + } + + if (!invalidSources.isEmpty()) + m_dbform->updateTabStopsOrder(); + + if (ok) { +//! @todo PRIMITIVE!! data setting: +//! @todo KexiTableViewData is not great name for data class here... rename/move? + KexiTableViewData* data = new KexiTableViewData(m_cursor); + if (forceReadOnlyDataSource) + data->setReadOnly(true); + data->preloadAllRows(); + +///*! @todo few backends return result count for free! - no need to reopen() */ +// int resultCount = -1; +// if (ok) { +// resultCount = m_conn->resultCount(m_conn->selectStatement(*m_query)); +// ok = m_cursor->reopen(); +// } +// if (ok) +// ok = ! (!m_cursor->moveFirst() && m_cursor->error()); + + m_scrollView->setData( data, true /*owner*/ ); + } + else + m_scrollView->setData( 0, false ); +} + +void +KexiFormView::slotDirty(KFormDesigner::Form *dirtyForm, bool isDirty) +{ + if(dirtyForm == form()) + KexiViewBase::setDirty(isDirty); +} + +KexiDB::SchemaData* +KexiFormView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel) +{ + KexiDB::SchemaData *s = KexiViewBase::storeNewData(sdata, cancel); + kexipluginsdbg << "KexiDBForm::storeNewData(): new id:" << s->id() << endl; + + if (!s || cancel) { + delete s; + return 0; + } + if (!storeData()) { + //failure: remove object's schema data to avoid garbage + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + conn->removeObject( s->id() ); + delete s; + return 0; + } + return s; +} + +tristate +KexiFormView::storeData(bool dontAsk) +{ + Q_UNUSED(dontAsk); + kexipluginsdbg << "KexiDBForm::storeData(): " << parentDialog()->partItem()->name() + << " [" << parentDialog()->id() << "]" << endl; + + //-- first, store local BLOBs, so identifiers can be updated +//! @todo remove unused data stored previously + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + KexiDB::TableSchema *blobsTable = conn->tableSchema("kexi__blobs"); + if (!blobsTable) { //compatibility check for older Kexi project versions +//! @todo show message about missing kexi__blobs? + return false; + } + // Not all engines accept passing NULL to PKEY o_id, so we're omitting it. + QStringList blobsFieldNamesWithoutID(blobsTable->names()); + blobsFieldNamesWithoutID.pop_front(); + KexiDB::FieldList *blobsFieldsWithoutID = blobsTable->subList(blobsFieldNamesWithoutID); + + KexiDB::PreparedStatement::Ptr st = conn->prepareStatement( + KexiDB::PreparedStatement::InsertStatement, *blobsFieldsWithoutID); + if (!st) { + delete blobsFieldsWithoutID; + //! @todo show message + return false; + } + KexiBLOBBuffer *blobBuf = KexiBLOBBuffer::self(); + KexiFormView *designFormView + = dynamic_cast<KexiFormView*>( parentDialog()->viewForMode(Kexi::DesignViewMode) ); + if (designFormView) { + for (QMapConstIterator<QWidget*, KexiBLOBBuffer::Id_t> it = tempData()->unsavedLocalBLOBs.constBegin(); + it!=tempData()->unsavedLocalBLOBs.constEnd(); ++it) + { + if (!it.key()) { + kexipluginswarn << "KexiFormView::storeData(): it.key()==0 !" << endl; + continue; + } + kexipluginsdbg << "name=" << it.key()->name() << " dataID=" << it.data() << endl; + KexiBLOBBuffer::Handle h( blobBuf->objectForId(it.data(), /*!stored*/false) ); + if (!h) + continue; //no BLOB assigned + + QString originalFileName(h.originalFileName()); + QFileInfo fi(originalFileName); + QString caption(fi.baseName().replace('_', " ").simplifyWhiteSpace()); + + if (st) { + *st /* << NO, (pgsql doesn't support this):QVariant()*/ /*id*/ + << h.data() << originalFileName << caption + << h.mimeType() << (uint)/*! @todo unsafe */h.folderId(); + if (!st->execute()) { + delete blobsFieldsWithoutID; + kexipluginsdbg << " execute error" << endl; + return false; + } + } + delete blobsFieldsWithoutID; + blobsFieldsWithoutID=0; + const Q_ULLONG storedBLOBID = conn->lastInsertedAutoIncValue("o_id", "kexi__blobs"); + if ((Q_ULLONG)-1 == storedBLOBID) { + //! @todo show message? + return false; + } + kexipluginsdbg << " storedDataID=" << storedBLOBID << endl; + h.setStoredWidthID((KexiBLOBBuffer::Id_t /*unsafe - will be fixed in Qt4*/)storedBLOBID); + //set widget's internal property so it can be saved... + const QVariant oldStoredPixmapId( it.key()->property("storedPixmapId") ); + it.key()->setProperty("storedPixmapId", + QVariant((uint /* KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - will be fixed in Qt4*/)storedBLOBID)); + KFormDesigner::ObjectTreeItem *widgetItem = designFormView->form()->objectTree()->lookup(it.key()->name()); //form()->objectTree()->lookup(it.key()->name()); + if (widgetItem) + widgetItem->addModifiedProperty( "storedPixmapId", oldStoredPixmapId ); + else + kexipluginswarn << "KexiFormView::storeData(): no '" << widgetItem->name() << "' widget found within a form" << endl; + } + } + + //-- now, save form's XML + QString data; + if (!KFormDesigner::FormIO::saveFormToString(tempData()->form, data)) + return false; + if (!storeDataBlock(data)) + return false; + + //all blobs are now saved + tempData()->unsavedLocalBLOBs.clear(); + + tempData()->tempForm = QString::null; + return true; +} + +#if 0 +/// Action stuff ///////////////// +void +KexiFormView::slotWidgetSelected(KFormDesigner::Form *f, bool multiple) +{ + if(f != form()) + return; + + enableFormActions(); + // Enable edit actions + setAvailable("edit_copy", true); + setAvailable("edit_cut", true); + setAvailable("edit_clear", true); + + // 'Align Widgets' menu + setAvailable("formpart_align_menu", multiple); + setAvailable("formpart_align_to_left", multiple); + setAvailable("formpart_align_to_right", multiple); + setAvailable("formpart_align_to_top", multiple); + setAvailable("formpart_align_to_bottom", multiple); + + setAvailable("formpart_adjust_size_menu", true); + setAvailable("formpart_adjust_width_small", multiple); + setAvailable("formpart_adjust_width_big", multiple); + setAvailable("formpart_adjust_height_small", multiple); + setAvailable("formpart_adjust_height_big", multiple); + + setAvailable("formpart_format_raise", true); + setAvailable("formpart_format_lower", true); + + // If the widgets selected is a container, we enable layout actions + if(!multiple) + { + KFormDesigner::ObjectTreeItem *item = f->objectTree()->lookup( f->selectedWidgets()->first()->name() ); + if(item && item->container()) + multiple = true; + } + // Layout actions + setAvailable("formpart_layout_hbox", multiple); + setAvailable("formpart_layout_vbox", multiple); + setAvailable("formpart_layout_grid", multiple); + + KFormDesigner::Container *container = f->activeContainer(); + setAvailable("formpart_break_layout", container ? + (container->layoutType() != KFormDesigner::Container::NoLayout) : false ); +} + +void +KexiFormView::slotFormWidgetSelected(KFormDesigner::Form *f) +{ + if(f != form()) + return; + + disableWidgetActions(); + enableFormActions(); + + // Layout actions + setAvailable("formpart_layout_hbox", true); + setAvailable("formpart_layout_vbox", true); + setAvailable("formpart_layout_grid", true); + setAvailable("formpart_break_layout", (f->toplevelContainer()->layoutType() != KFormDesigner::Container::NoLayout)); +} + +void +KexiFormView::slotNoFormSelected() // == form in preview mode +{ + disableWidgetActions(); + + // Disable paste action + setAvailable("edit_paste", false); + setAvailable("edit_undo", false); + setAvailable("edit_redo", false); + + // Disable 'Tools' actions + setAvailable("formpart_pixmap_collection", false); + setAvailable("formpart_connections", false); + setAvailable("formpart_taborder", false); + setAvailable("formpart_change_style", false); +} + +void +KexiFormView::enableFormActions() +{ + // Enable 'Tools' actions + setAvailable("formpart_pixmap_collection", true); + setAvailable("formpart_connections", true); + setAvailable("formpart_taborder", true); + + setAvailable("edit_paste", KFormDesigner::FormManager::self()->isPasteEnabled()); +} + +void +KexiFormView::disableWidgetActions() +{ + // Disable edit actions + setAvailable("edit_copy", false); + setAvailable("edit_cut", false); + setAvailable("edit_clear", false); + + // Disable format functions + setAvailable("formpart_align_menu", false); + setAvailable("formpart_align_to_left", false); + setAvailable("formpart_align_to_right", false); + setAvailable("formpart_align_to_top", false); + setAvailable("formpart_align_to_bottom", false); + + setAvailable("formpart_adjust_size_menu", false); + setAvailable("formpart_adjust_width_small", false); + setAvailable("formpart_adjust_width_big", false); + setAvailable("formpart_adjust_height_small", false); + setAvailable("formpart_adjust_height_big", false); + + setAvailable("formpart_format_raise", false); + setAvailable("formpart_format_lower", false); + + setAvailable("formpart_layout_hbox", false); + setAvailable("formpart_layout_vbox", false); + setAvailable("formpart_layout_grid", false); + setAvailable("formpart_break_layout", false); +} + +void +KexiFormView::setUndoEnabled(bool enabled) +{ + setAvailable("edit_undo", enabled); +} + +void +KexiFormView::setRedoEnabled(bool enabled) +{ + setAvailable("edit_redo", enabled); +} +#endif //0 + +QSize +KexiFormView::preferredSizeHint(const QSize& otherSize) +{ + if (parentDialog()->neverSaved()) { + //ignore otherSize if possible +// return KexiViewBase::preferredSizeHint( (parentDialog() && parentDialog()->mdiParent()) ? QSize(10000,10000) : otherSize); + } + + return (m_dbform->size() + +QSize(m_scrollView->verticalScrollBar()->isVisible() ? m_scrollView->verticalScrollBar()->width()*3/2 : 10, + m_scrollView->horizontalScrollBar()->isVisible() ? m_scrollView->horizontalScrollBar()->height()*3/2 : 10)) + .expandedTo( KexiViewBase::preferredSizeHint(otherSize) ); +} + +void +KexiFormView::resizeEvent( QResizeEvent *e ) +{ + if (viewMode()==Kexi::DataViewMode) { + m_scrollView->refreshContentsSizeLater( + e->size().width()!=e->oldSize().width(), + e->size().height()!=e->oldSize().height() + ); + } + KexiViewBase::resizeEvent(e); + m_scrollView->updateNavPanelGeometry(); + if (m_delayedFormContentsResizeOnShow>0) { // && isVisible()) { + m_delayedFormContentsResizeOnShow--; + m_dbform->resize( e->size() - QSize(30, 30) ); + } +} + +void +KexiFormView::setFocusInternal() +{ + if (viewMode() == Kexi::DataViewMode) { + if (m_dbform->focusWidget()) { + //better-looking focus + if (m_setFocusInternalOnce) { + KexiUtils::setFocusWithReason(m_setFocusInternalOnce, QFocusEvent::Other);//Tab); + m_setFocusInternalOnce = 0; + } + else { + //ok? SET_FOCUS_USING_REASON(m_dbform->focusWidget(), QFocusEvent::Other);//Tab); + } + return; + } + } + QWidget::setFocus(); +} + +void +KexiFormView::show() +{ + KexiDataAwareView::show(); + +//moved from KexiFormScrollView::show(): + + //now get resize mode settings for entire form + // if (resizeMode() == KexiFormView::ResizeAuto) + if (viewMode()==Kexi::DataViewMode) { + if (resizeMode() == KexiFormView::ResizeAuto) + m_scrollView->setResizePolicy(QScrollView::AutoOneFit); + } +} + +void +KexiFormView::slotFocus(bool in) +{ + if(in && form() && KFormDesigner::FormManager::self() && KFormDesigner::FormManager::self()->activeForm() != form()) { + KFormDesigner::FormManager::self()->windowChanged(m_dbform); + updateDataSourcePage(); + } +} + +void +KexiFormView::updateDataSourcePage() +{ + if (viewMode()==Kexi::DesignViewMode) { + QCString dataSourceMimeType, dataSource; + KFormDesigner::WidgetPropertySet *set = KFormDesigner::FormManager::self()->propertySet(); + if (set->contains("dataSourceMimeType")) + dataSourceMimeType = (*set)["dataSourceMimeType"].value().toCString(); + if (set->contains("dataSource")) + dataSource = (*set)["dataSource"].value().toCString(); + + formPart()->dataSourcePage()->setDataSource(dataSourceMimeType, dataSource); + } +} + +void +KexiFormView::slotHandleDragMoveEvent(QDragMoveEvent* e) +{ + if (KexiFieldDrag::canDecodeMultiple( e )) { + e->accept(true); + //dirty: drawRect(QRect( e->pos(), QSize(50, 20)), 2); + } +} + +void +KexiFormView::slotHandleDropEvent(QDropEvent* e) +{ + const QWidget *targetContainerWidget = dynamic_cast<const QWidget*>(sender()); + KFormDesigner::ObjectTreeItem *targetContainerWidgetItem = targetContainerWidget + ? form()->objectTree()->lookup( targetContainerWidget->name() ) : 0; + if (targetContainerWidgetItem && targetContainerWidgetItem->container() + && KexiFieldDrag::canDecodeMultiple( e )) + { + QString sourceMimeType, sourceName; + QStringList fields; + if (!KexiFieldDrag::decodeMultiple( e, sourceMimeType, sourceName, fields )) + return; + insertAutoFields(sourceMimeType, sourceName, fields, + targetContainerWidgetItem->container(), e->pos()); + } +} + +void +KexiFormView::insertAutoFields(const QString& sourceMimeType, const QString& sourceName, + const QStringList& fields, KFormDesigner::Container* targetContainer, const QPoint& _pos) +{ + if (fields.isEmpty()) + return; + + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + KexiDB::TableOrQuerySchema tableOrQuery(conn, sourceName.latin1(), sourceMimeType=="kexi/table"); + if (!tableOrQuery.table() && !tableOrQuery.query()) { + kexipluginswarn << "KexiFormView::insertAutoFields(): no such table/query \"" + << sourceName << "\"" << endl; + return; + } + + QPoint pos(_pos); + //if pos is not specified, compute a new position: + if (pos==QPoint(-1,-1)) { + if (m_widgetGeometryForRecentInsertAutoFields.isValid()) { + pos = m_widgetGeometryForRecentInsertAutoFields.bottomLeft() + + QPoint(0,form()->gridSize()); + } + else { + pos = QPoint(40, 40); //start here + } + } + + // there will be many actions performed, do not update property pane until all that's finished + KFormDesigner::FormManager::self()->blockPropertyEditorUpdating(this); + +//! todo unnamed query colums are not supported + +// KFormDesigner::WidgetList* prevSelection = form()->selectedWidgets(); + KFormDesigner::WidgetList widgetsToSelect; + KFormDesigner::CommandGroup *group = new KFormDesigner::CommandGroup( + fields.count()==1 ? i18n("Insert AutoField widget") : i18n("Insert %1 AutoField widgets").arg(fields.count()), + KFormDesigner::FormManager::self()->propertySet() + ); + + foreach( QStringList::ConstIterator, it, fields ) { + KexiDB::QueryColumnInfo* column = tableOrQuery.columnInfo(*it); + if (!column) { + kexipluginswarn << "KexiFormView::insertAutoFields(): no such field \"" + << *it << "\" in table/query \"" << sourceName << "\"" << endl; + continue; + } +//! todo add autolabel using field's caption or name + //KFormDesigner::Container *targetContainer; +/* QWidget* targetContainerWidget = QApplication::widgetAt(pos, true); + while (targetContainerWidget + && !dynamic_cast<KFormDesigner::Container*>(targetContainerWidget)) + { + targetContainerWidget = targetContainerWidget->parentWidget(); + } + if (dynamic_cast<KFormDesigner::Container*>(targetContainerWidget)) + targetContainer = dynamic_cast<KFormDesigner::Container*>(targetContainerWidget); + else + targetContainer = form()->toplevelContainer();*/ + KFormDesigner::InsertWidgetCommand *insertCmd + = new KFormDesigner::InsertWidgetCommand(targetContainer, + //! todo this is hardcoded! + "KexiDBAutoField", + //! todo this name can be invalid for expressions: if so, fall back to a default class' prefix! + pos, column->aliasOrName() + ); + insertCmd->execute(); + group->addCommand(insertCmd, false/*don't exec twice*/); + + KFormDesigner::ObjectTreeItem *newWidgetItem + = form()->objectTree()->dict()->find(insertCmd->widgetName()); + KexiDBAutoField* newWidget + = newWidgetItem ? dynamic_cast<KexiDBAutoField*>(newWidgetItem->widget()) : 0; + widgetsToSelect.append(newWidget); +//#if 0 + KFormDesigner::CommandGroup *subGroup + = new KFormDesigner::CommandGroup("", KFormDesigner::FormManager::self()->propertySet()); + QMap<QCString, QVariant> propValues; + propValues.insert("dataSource", column->aliasOrName()); + propValues.insert("fieldTypeInternal", (int)column->field->type()); + propValues.insert("fieldCaptionInternal", column->captionOrAliasOrName()); + KFormDesigner::FormManager::self()->propertySet()->createPropertyCommandsInDesignMode( + newWidget, propValues, subGroup, false/*!addToActiveForm*/, + true /*!execFlagForSubCommands*/); + subGroup->execute(); + group->addCommand( subGroup, false/*will not be executed on CommandGroup::execute()*/ ); + +//#endif + //set data source and caption + //-we don't need to use PropertyCommand here beacause we don't need UNDO + // for these single commands +// newWidget->setDataSource(column->aliasOrName()); +// newWidget->setFieldTypeInternal((int)column->field->type()); +// newWidget->setFieldCaptionInternal(column->captionOrAliasOrName()); + //resize again because autofield's type changed what can lead to changed sizeHint() +// newWidget->resize(newWidget->sizeHint()); + KFormDesigner::WidgetList list; + list.append(newWidget); + KFormDesigner::AdjustSizeCommand *adjustCommand + = new KFormDesigner::AdjustSizeCommand(KFormDesigner::AdjustSizeCommand::SizeToFit, + list, form()); + adjustCommand->execute(); + group->addCommand( adjustCommand, + false/*will not be executed on CommandGroup::execute()*/ + ); + + if (newWidget) {//move position down for next widget + pos.setY( pos.y() + newWidget->height() + form()->gridSize()); + } + } + if (widgetsToSelect.last()) { + //resize form if needed + QRect oldFormRect( m_dbform->geometry() ); + QRect newFormRect( oldFormRect ); + newFormRect.setWidth(QMAX(m_dbform->width(), widgetsToSelect.last()->geometry().right()+1)); + newFormRect.setHeight(QMAX(m_dbform->height(), widgetsToSelect.last()->geometry().bottom()+1)); + if (newFormRect != oldFormRect) { + //1. resize by hand + m_dbform->setGeometry( newFormRect ); + //2. store information about resize + KFormDesigner::PropertyCommand *resizeFormCommand = new KFormDesigner::PropertyCommand( + KFormDesigner::FormManager::self()->propertySet(), m_dbform->name(), + oldFormRect, newFormRect, "geometry"); + group->addCommand(resizeFormCommand, true/*will be executed on CommandGroup::execute()*/); + } + + //remember geometry of the last inserted widget + m_widgetGeometryForRecentInsertAutoFields = widgetsToSelect.last()->geometry(); + } + + //eventually, add entire command group to active form + form()->addCommand( group, true/*exec*/ ); + +// group->debug(); + + //enable proper REDO usage + group->resetAllowExecuteFlags(); + + m_scrollView->repaint(); + m_scrollView->viewport()->repaint(); + m_scrollView->repaintContents(); + m_scrollView->updateContents(); + m_scrollView->clipper()->repaint(); + m_scrollView->refreshContentsSize(); + + //select all inserted widgets, if multiple + if (widgetsToSelect.count()>1) { + form()->setSelectedWidget(0); + foreach_list (KFormDesigner::WidgetListIterator, it, widgetsToSelect) + form()->setSelectedWidget(it.current(), true/*add*/, true/*dontRaise*/); + } + + // eventually, update property pane + KFormDesigner::FormManager::self()->unblockPropertyEditorUpdating(this, KFormDesigner::FormManager::self()->propertySet()); +} + +void +KexiFormView::setUnsavedLocalBLOB(QWidget *widget, KexiBLOBBuffer::Id_t id) +{ +//! @todo if there already was data assigned, remember it should be dereferenced + if (id==0) + tempData()->unsavedLocalBLOBs.remove(widget); + else + tempData()->unsavedLocalBLOBs.insert(widget, id); +} + +/* +todo +void KexiFormView::updateActions(bool activated) +{ + if (viewMode()==Kexi::DesignViewMode) { + if (form()->selectedWidget()) { + if (form()->widget() == form()->selectedWidget()) + KFormDesigner::FormManager::self()->emitFormWidgetSelected( form() ); + else + KFormDesigner::FormManager::self()->emitWidgetSelected( form(), false ); + } + else if (form()->selectedWidgets()) { + KFormDesigner::FormManager::self()->emitWidgetSelected( form(), true ); + } + } + KexiDataAwareView::updateActions(activated); +}*/ + +/* +void KexiFormView::parentDialogDetached() +{ + m_dbform->updateTabStopsOrder(form()); +} + +void KexiFormView::parentDialogAttached(KMdiChildFrm *) +{ + m_dbform->updateTabStopsOrder(form()); +}*/ + +#include "kexiformview.moc" + diff --git a/kexi/plugins/forms/kexiformview.h b/kexi/plugins/forms/kexiformview.h new file mode 100644 index 00000000..0a774556 --- /dev/null +++ b/kexi/plugins/forms/kexiformview.h @@ -0,0 +1,231 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + Copyright (C) 2004-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 KEXIFORMVIEW_H +#define KEXIFORMVIEW_H + +#include <qtimer.h> + +#include <kexiviewbase.h> +#include <widget/kexidataawareview.h> + +#include "kexiformpart.h" +#include <core/kexiblobbuffer.h> + +class KexiFormPart; +class KexiMainWindow; +class KexiDBForm; +class KexiTableItem; +class KexiTableViewData; +class KexiFormScrollView; +namespace KexiDB { class Cursor; } +namespace KFormDesigner +{ + class Container; +} + +//! The KexiFormView lass provides a data-driven (record-based) form view . +/*! The KexiFormView can display data provided "by hand" + or from KexiDB-compatible database source. + + This class provides a single view used inside KexiDialogBase. + It takes care of saving/loading form, of enabling actions when needed. + One KexiFormView object is instantiated for data view mode + and a second KexiFormView object is instantiated for design view mode. + + @see KexiDataTable +*/ +class KEXIFORMUTILS_EXPORT KexiFormView : public KexiDataAwareView +{ + Q_OBJECT + + public: + enum ResizeMode { + ResizeAuto = 0, + ResizeDefault = ResizeAuto, + ResizeFixed = 1, + NoResize = 2 /*! @todo */ + }; + +// KexiFormView(KexiMainWindow *win, QWidget *parent, const char *name, KexiDB::Connection *conn); + KexiFormView(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0, + bool dbAware = true); + virtual ~KexiFormView(); + +// KexiDB::Connection* connection() { return m_conn; } + + virtual QSize preferredSizeHint(const QSize& otherSize); + + int resizeMode() const { return m_resizeMode; } + + KFormDesigner::Form* form() const; + + /*! Assigns \a id local (static) BLOB's identifier for \a widget widget. + Previously assigned BLOB will be usassigned. + If \a id is 0, BLOB is unassigned and no new is assigned. + + This method is called when a widget supporting BLOB data + (currently, images from KexiDBImageBox, within KexiDBFactory) has BLOB assigned by identifier \a id. + BLOB identifiers are defined by KexiBLOBBuffer (KexiBLOBBuffer::self() instance). + + The data collected by this method is used on form's design saving (in design mode). + Local BLOBs are retrieved KexiBLOBBuffer::self() and stored in "kexi__blobs" 'system' table. + Note that db-aware BLOBs (non local) are not handled this way. + */ + void setUnsavedLocalBLOB(QWidget *widget, KexiBLOBBuffer::Id_t id); + + public slots: + /*! Reimplemented to update resize policy. */ + virtual void show(); + + /*! Inserts autofields onto the form at \a pos position. + \a sourceMimeType can be "kexi/table" or "kexi/query", + \a sourceName is a name of a table or query, \a fields is a list of fields to insert (one or more) + Fields are inserted using standard KFormDesigner::InsertWidgetCommand framework, + so undo/redo is available for this operation. + + If multiple fields are provided, they will be aligned vertically. + If \a pos is QPoint(-1,-1) (the default), position is computed automatically + based on a position last inserted field using this method. + If this method has not been called yet, position of QPoint(40, 40) will be set. + + Called by: + - slotHandleDropEvent() when field(s) are dropped from the data source pane onto the form + - KexiFormManager is a used clicked "Insert fields" button on the data source pane. */ + void insertAutoFields(const QString& sourceMimeType, const QString& sourceName, + const QStringList& fields, KFormDesigner::Container* targetContainerWidget, + const QPoint& pos = QPoint(-1,-1)); + + protected slots: + void slotPropertySetSwitched(KoProperty::Set *b, bool forceReload = false, + const QCString& propertyToSelect = QCString()); + void slotDirty(KFormDesigner::Form *f, bool isDirty); + void slotFocus(bool in); + void slotHandleDragMoveEvent(QDragMoveEvent* e); + + //! Handles field(s) dropping from the data source pane onto the form + //! @see insertAutoFields() + void slotHandleDropEvent(QDropEvent* e); + +//moved to formmanager void slotWidgetSelected(KFormDesigner::Form *form, bool multiple); +//moved to formmanager void slotFormWidgetSelected(KFormDesigner::Form *form); +//moved to formmanager void slotNoFormSelected(); + +//moved to formmanager void setUndoEnabled(bool enabled); +//moved to formmanager void setRedoEnabled(bool enabled); + + protected: + virtual tristate beforeSwitchTo(int mode, bool &dontStore); + virtual tristate afterSwitchFrom(int mode); + virtual KoProperty::Set* propertySet() { return m_propertySet; } + + virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel); + virtual tristate storeData(bool dontAsk = false); + + KexiFormPart::TempData* tempData() const { + return dynamic_cast<KexiFormPart::TempData*>(parentDialog()->tempData()); } + KexiFormPart* formPart() const { return dynamic_cast<KexiFormPart*>(part()); } + +//moved to formmanager void disableWidgetActions(); +//moved to formmanager void enableFormActions(); + + void setForm(KFormDesigner::Form *f); + + void initForm(); + + void loadForm(); + + //! Used in loadForm() + void updateAutoFieldsDataSource(); + + //! Used in loadForm() + void updateValuesForSubproperties(); + + virtual void resizeEvent ( QResizeEvent * ); + + void initDataSource(); + + virtual void setFocusInternal(); + +/* // for navigator + virtual void moveToRecordRequested(uint r); + virtual void moveToLastRecordRequested(); + virtual void moveToPreviousRecordRequested(); + virtual void moveToNextRecordRequested(); + virtual void moveToFirstRecordRequested(); + virtual void addNewRecordRequested();*/ + + /*! Called after loading the form contents (before showing it). + Also called when the form window (KexiDialogBase) is detached + (in KMDI's Child Frame mode), because otherwise tabstop ordering can get broken. */ + void updateTabStopsOrder(); + + /*! @internal */ + void deleteQuery(); + + /*! @internal */ + void updateDataSourcePage(); + + /*! Reimplemented after KexiViewBase. + Updates actions (e.g. availability). */ +// todo virtual void updateActions(bool activated); + + KexiDBForm *m_dbform; + KexiFormScrollView *m_scrollView; + KoProperty::Set *m_propertySet; + + /*! Database cursor used for data retrieving. + It is shared between subsequent Data view sessions (just reopened on switch), + but deleted and recreated from scratch when form's "dataSource" property changed + since last form viewing (m_previousDataSourceString is used for that). */ + QString m_previousDataSourceString; + + int m_resizeMode; + + KexiDB::QuerySchema* m_query; + + /*! True, if m_query is created as temporary object within this form. + If user selected an existing, predefined (stored) query, m_queryIsOwned will be false, + so the query object will not be destroyed. */ + bool m_queryIsOwned; + + KexiDB::Cursor *m_cursor; + + /*! For new (empty) forms only: + Our form's area will be resized more than once. + We will resize form widget itself later (in resizeEvent()). */ + int m_delayedFormContentsResizeOnShow; + + //! Used in setFocusInternal() + QGuardedPtr<QWidget> m_setFocusInternalOnce; + + + /*! Stores geometry of widget recently inserted using insertAutoFields() method. + having this information, we'r eable to compute position for a newly + inserted widget in insertAutoFields() is such position has not been specified. + (the position is specified when a widget is inserted with mouse drag & dropping + but not with clicking of 'Insert fields' button from Data Source pane) */ + QRect m_widgetGeometryForRecentInsertAutoFields; + + //! Used in setUnsavedLocalBLOBs() +// QMap<QWidget*, KexiBLOBBuffer::Id_t> m_unsavedLocalBLOBs; +}; + +#endif diff --git a/kexi/plugins/forms/kformdesigner_kexidbfactory.desktop b/kexi/plugins/forms/kformdesigner_kexidbfactory.desktop new file mode 100644 index 00000000..4e5bb719 --- /dev/null +++ b/kexi/plugins/forms/kformdesigner_kexidbfactory.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Type=Service +ServiceTypes=KFormDesigner/WidgetFactory + +Name=Kexi DB Widgets +Name[bg]=Графични обекти на Kexi за бази данни +Name[ca]=Estris DB de Kexi +Name[cy]=Celfigion Cronfa Ddata Kexi +Name[da]=Kexi DB-kontroller +Name[de]=Kexi Datenbank-Elemente +Name[el]=Γραφικά συστατικά Kexi DB +Name[eo]=Kexi DB-fenestraĵo +Name[es]=Wigdet de BD de Kexi +Name[et]=Kexi andmebaasividinad +Name[eu]=Kexi-ren datu-baseko trepetak +Name[fa]=عناصر Kexi DB +Name[fi]=Kexi tietokantaelementit +Name[fr]=Éléments graphiques de base de données Kexi +Name[fy]=Kexi DB-widgets +Name[gl]=Elementos de Base de Datos Kexi +Name[he]=פריטי מסד נתונים של Kexi +Name[hr]=Kexi DB widgeti +Name[hu]=Kexi adatbázis-kezelési grafikus elemek +Name[is]=Kexi gagnagrunns hlutir +Name[it]=Oggetti per banche dati per Kexi +Name[ja]=Kexi DB ウィジェット +Name[km]=ធាតុក្រាហ្វិក DB សម្រាប់ Kexi +Name[lv]=Kexi DB logdaļas +Name[ms]=Widget DB Kexi +Name[nb]=DB-element for Kexi +Name[nds]=Datenbank-Stüerelementen för Kexi +Name[ne]=केक्सी DB विजेटहरू +Name[nl]=Kexi DB-widgets +Name[nn]=DB-element for Kexi +Name[pl]=Kontrolki baz danych dla Kexi +Name[pt]=Elementos de Base de Dados Kexi +Name[pt_BR]=Widgets de BD do Kexi +Name[ru]=Элементы управления для работы с базами данных Kexi +Name[se]=Kexi-DV-áđat +Name[sk]=Komponenty Kexi DB +Name[sl]=Gradniki za zbirko podatkov za Kexi +Name[sr]=Kexi-јеве DB контроле +Name[sr@Latn]=Kexi-jeve DB kontrole +Name[sv]=Kexi-databaskomponenter +Name[ta]=கெக்சி டிபி சாளர உருக்கள் +Name[tr]=Kexi DB Parçacıkları +Name[uk]=Віджети Kexi DB +Name[uz]=Kexi maʼlumot baza vidjetlari +Name[uz@cyrillic]=Kexi маълумот база виджетлари +Name[zh_CN]=Kexi 数据库部件 +Name[zh_TW]=Kexi DB 視窗元件 + +X-KDE-Library=kformdesigner_kexidbwidgets +X-KFormDesigner-FactoryGroup=kexi +X-KFormDesigner-WidgetFactoryVersion=2 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 |