diff --git a/src/plugins/forms/kexidatasourcepage.cpp b/src/plugins/forms/kexidatasourcepage.cpp index 57e8b70fb..1ebbce4f4 100644 --- a/src/plugins/forms/kexidatasourcepage.cpp +++ b/src/plugins/forms/kexidatasourcepage.cpp @@ -1,454 +1,407 @@ /* This file is part of the KDE project Copyright (C) 2005-2016 Jarosław Staniek 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include KexiDataSourcePage::KexiDataSourcePage(QWidget *parent) - : KexiPropertyPaneViewBase(parent) + : QWidget(parent) , m_noDataSourceAvailableSingleText( - xi18n("No data source could be assigned for this widget.") ) + xi18n("[Can't assign to this widget]") ) , m_noDataSourceAvailableMultiText( - xi18n("No data source could be assigned for multiple widgets.") ) + xi18n("[Can't assign to multiple widgets]") ) , m_insideClearFormDataSourceSelection(false) + , m_slotWidgetDataSourceTextChangedEnabled(true) #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT , m_tableOrQuerySchema(0) #endif { - infoLabel()->setContentsMargins(0, 0, 0, spacing()); + const KexiStyle::PropertyPane &s = KexiStyle::propertyPane(); + m_mainLyr = s.createVLayout(this); - m_noDataSourceAvailableLabel = new QLabel(m_noDataSourceAvailableSingleText, this); - m_noDataSourceAvailableLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); - m_noDataSourceAvailableLabel->setContentsMargins(0, 0, 0, spacing()); - m_noDataSourceAvailableLabel->setAlignment(Qt::AlignBottom | Qt::AlignLeft); - m_noDataSourceAvailableLabel->setWordWrap(true); - mainLayout()->addWidget(m_noDataSourceAvailableLabel); + s.createTitleLabel(xi18nc("@label Form data source - title", "Data source"), m_mainLyr); - //-Widget's Data Source - QHBoxLayout *hlyr = new QHBoxLayout(); - mainLayout()->addLayout(hlyr); -#if 0 -//! @todo unhide this when expression work -// m_widgetDSLabel = new QLabel(futureI18nc("Table Field, Query Field or Expression", "Source field or expression"), this); -#else - m_widgetDSLabel = new QLabel( - xi18nc("Table Field or Query Field", "Widget's data source:"), this); -#endif - m_widgetDSLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - m_widgetDSLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); - hlyr->addWidget(m_widgetDSLabel); - mainLayout()->addSpacing(KexiUtils::spacingHint()); // needed because unlike m_dataSourceLabel we have no button in hlyr - -#if 0 - m_clearWidgetDSButton = new KexiSmallToolButton( - koIcon("edit-clear-locationbar-rtl"), QString(), this); - m_clearWidgetDSButton->setObjectName("clearWidgetDSButton"); - m_clearWidgetDSButton->setMinimumHeight(m_widgetDSLabel->minimumHeight()); - m_clearWidgetDSButton->setToolTip(futureI18n("Clear widget's data source")); - hlyr->addWidget(m_clearWidgetDSButton); - connect(m_clearWidgetDSButton, SIGNAL(clicked()), - this, SLOT(clearWidgetDataSourceSelection())); -#endif - - m_widgetDataSourceCombo = new KexiFieldComboBox(this); - m_widgetDataSourceCombo->setObjectName("sourceFieldCombo"); - m_widgetDataSourceCombo->setContentsMargins(0, 0, 0, 0); - m_widgetDSLabel->setBuddy(m_widgetDataSourceCombo); - connect(m_widgetDataSourceCombo, SIGNAL(editTextChanged(QString)), - this, SLOT(slotWidgetDataSourceTextChanged(QString))); - mainLayout()->addWidget(m_widgetDataSourceCombo); - - m_widgetDataSourceComboSpacer = addWidgetSpacer(); + m_formLyr = s.createFormLayout(m_mainLyr); //- Form's Data Source - hlyr = new QHBoxLayout(); - hlyr->setContentsMargins(0, 0, 0, 0); - mainLayout()->addLayout(hlyr); - m_dataSourceLabel = new QLabel(xi18n("Form's data source:"), this); - m_dataSourceLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - m_dataSourceLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); - hlyr->addWidget(m_dataSourceLabel); - +//! @todo Port "Go to selected form's data source" +#if 0 m_gotoButton = new KexiSmallToolButton( koIcon("go-jump"), QString(), this); m_gotoButton->setObjectName("gotoButton"); m_gotoButton->setToolTip(xi18n("Go to selected form's data source")); m_gotoButton->setWhatsThis(xi18n("Goes to selected form's data source")); hlyr->addWidget(m_gotoButton); connect(m_gotoButton, SIGNAL(clicked()), this, SLOT(slotGotoSelected())); - -#if 0 - m_clearDSButton = new KexiSmallToolButton( - koIcon("edit-clear-locationbar-rtl"), QString(), this); - m_clearDSButton->setObjectName("clearDSButton"); - m_clearDSButton->setMinimumHeight(m_dataSourceLabel->minimumHeight()); - m_clearDSButton->setToolTip(futureI18n("Clear form's data source")); - hlyr->addWidget(m_clearDSButton); - connect(m_clearDSButton, SIGNAL(clicked()), this, SLOT(clearFormDataSourceSelection())); #endif - m_formDataSourceCombo = new KexiDataSourceComboBox(this); + m_formDataSourceCombo = new KexiDataSourceComboBox; m_formDataSourceCombo->setObjectName("dataSourceCombo"); - m_formDataSourceCombo->setContentsMargins(0, 0, 0, 0); - m_dataSourceLabel->setBuddy(m_formDataSourceCombo); - mainLayout()->addWidget(m_formDataSourceCombo); + s.addLabelAndWidget(xi18nc("@label Forms's data source (table or query)", "Form"), + m_formDataSourceCombo, m_formLyr); - m_formDataSourceComboSpacer = addWidgetSpacer(); + //-Widget's Data Source + m_widgetDataSourceContainer = new QWidget; + QVBoxLayout *widgetDataSourceContainerLyr = new QVBoxLayout(m_widgetDataSourceContainer); + widgetDataSourceContainerLyr->setContentsMargins(0, 0, 0, 0); + + m_widgetDataSourceCombo = new KexiFieldComboBox; + widgetDataSourceContainerLyr->addWidget(m_widgetDataSourceCombo); + s.alterComboBoxStyle(m_widgetDataSourceCombo); + m_widgetDataSourceCombo->setObjectName("sourceFieldCombo"); + connect(m_widgetDataSourceCombo, &KexiFieldComboBox::editTextChanged, + this, &KexiDataSourcePage::slotWidgetDataSourceTextChanged); + + m_noDataSourceAvailableLabel = s.createWarningLabel(m_noDataSourceAvailableSingleText); + m_noDataSourceAvailableLabel->hide(); + widgetDataSourceContainerLyr->addWidget(m_noDataSourceAvailableLabel); + + s.addLabelAndWidget(xi18nc("@label Widget's data source (table field or query field)", "Widget"), + m_widgetDataSourceContainer, m_formLyr); #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT - mainLayout()->addStretch(); + m_mainLyr->addStretch(); #else //2. Inserting fields //helper info //! @todo allow to hide such helpers by adding global option hlyr = new QHBoxLayout(); hlyr->setContentsMargins(0, 0, 0, 0); - mainLayout()->addLayout(hlyr); + m_mainLyr->addLayout(hlyr); m_mousePointerLabel = new QLabel(this); hlyr->addWidget(m_mousePointerLabel); m_mousePointerLabel->setPixmap(koIcon("tool-pointer")); m_mousePointerLabel->setFixedWidth(m_mousePointerLabel->pixmap() ? m_mousePointerLabel->pixmap()->width() : 0); m_availableFieldsDescriptionLabel = new QLabel( futureI18n("Select fields from the list below and drag them onto" " a form or click the Insert button"), this); m_availableFieldsDescriptionLabel->setAlignment(Qt::AlignLeft); m_availableFieldsDescriptionLabel->setWordWrap(true); hlyr->addWidget(m_availableFieldsDescriptionLabel); //Available Fields hlyr = new QHBoxLayout(); hlyr->setContentsMargins(0, 0, 0, 0); - mainLayout()->addLayout(hlyr); + m_mainLyr->addLayout(hlyr); m_availableFieldsLabel = new QLabel(futureI18n("Available fields"), this); m_availableFieldsLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); hlyr->addWidget(m_availableFieldsLabel); m_addField = new KexiSmallToolButton( KexiIcon("add-field"), futureI18nc("Insert selected field into form", "Insert"), this); m_addField->setObjectName("addFieldButton"); m_addField->setFocusPolicy(Qt::StrongFocus); m_addField->setToolTip(futureI18n("Insert selected fields into form")); m_addField->setWhatsThis(futureI18n("Inserts selected fields into form")); hlyr->addWidget(m_addField); connect(m_addField, SIGNAL(clicked()), this, SLOT(slotInsertSelectedFields())); m_fieldListView = new KexiFieldListView(this, KexiFieldListView::ShowDataTypes | KexiFieldListView::AllowMultiSelection); m_fieldListView->setObjectName("fieldListView"); m_fieldListView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); m_availableFieldsLabel->setBuddy(m_fieldListView); - mainLayout()->addWidget(m_fieldListView, 1); + m_mainLyr->addWidget(m_fieldListView, 1); connect(m_fieldListView, SIGNAL(selectionChanged()), this, SLOT(slotFieldListViewSelectionChanged())); connect(m_fieldListView, SIGNAL(fieldDoubleClicked(QString,QString,QString)), this, SLOT(slotFieldDoubleClicked(QString,QString,QString))); #endif - mainLayout()->addStretch(1); + m_mainLyr->addStretch(1); connect(m_formDataSourceCombo, SIGNAL(editTextChanged(QString)), this, SLOT(slotFormDataSourceTextChanged(QString))); connect(m_formDataSourceCombo, SIGNAL(dataSourceChanged()), this, SLOT(slotFormDataSourceChanged())); connect(m_widgetDataSourceCombo, SIGNAL(selected()), this, SLOT(slotFieldSelected())); clearFormDataSourceSelection(); slotFieldListViewSelectionChanged(); } KexiDataSourcePage::~KexiDataSourcePage() { #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT delete m_tableOrQuerySchema; #endif } void KexiDataSourcePage::setProject(KexiProject *prj) { m_widgetDataSourceCombo->setProject(prj); m_formDataSourceCombo->setProject(prj); } void KexiDataSourcePage::clearFormDataSourceSelection(bool alsoClearComboBox) { if (m_insideClearFormDataSourceSelection) return; m_insideClearFormDataSourceSelection = true; if (alsoClearComboBox && !m_formDataSourceCombo->selectedName().isEmpty()) m_formDataSourceCombo->setDataSource(QString(), QString()); - m_gotoButton->setEnabled(false); + //! @todo m_gotoButton->setEnabled(false); m_widgetDataSourceCombo->setFieldOrExpression(QString()); #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_addField->setEnabled(false); m_fieldListView->clear(); #endif m_insideClearFormDataSourceSelection = false; } void KexiDataSourcePage::slotWidgetDataSourceTextChanged(const QString &text) { + if (!m_slotWidgetDataSourceTextChangedEnabled) { + return; + } if (text.isEmpty()) { clearWidgetDataSourceSelection(); } } void KexiDataSourcePage::clearWidgetDataSourceSelection() { m_widgetDataSourceCombo->setFieldOrExpression(QString()); slotFieldSelected(); } void KexiDataSourcePage::slotGotoSelected() { const QString pluginId(m_formDataSourceCombo->selectedPluginId()); if (pluginId == "org.kexi-project.table" || pluginId == "org.kexi-project.query") { if (m_formDataSourceCombo->isSelectionValid()) emit jumpToObjectRequested(pluginId, m_formDataSourceCombo->selectedName()); } } void KexiDataSourcePage::slotInsertSelectedFields() { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT QStringList selectedFieldNames(m_fieldListView->selectedFieldNames()); if (selectedFieldNames.isEmpty()) return; emit insertAutoFields(m_fieldListView->schema()->table() ? "org.kexi-project.table" : "org.kexi-project.query", m_fieldListView->schema()->name(), selectedFieldNames); #endif } void KexiDataSourcePage::slotFieldDoubleClicked(const QString& sourcePluginId, const QString& sourceName, const QString& fieldName) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT QStringList selectedFields; selectedFields.append(fieldName); emit insertAutoFields(sourcePluginId, sourceName, selectedFields); #else Q_UNUSED(sourcePluginId); Q_UNUSED(sourceName); Q_UNUSED(fieldName); #endif } void KexiDataSourcePage::slotFormDataSourceTextChanged(const QString &text) { const bool enable = m_formDataSourceCombo->isSelectionValid(); if (text.isEmpty()) { clearFormDataSourceSelection(); } else if (!enable) { clearFormDataSourceSelection(m_formDataSourceCombo->selectedName().isEmpty()/*alsoClearComboBox*/); } updateSourceFieldWidgetsAvailability(); } void KexiDataSourcePage::slotFormDataSourceChanged() { if (!m_formDataSourceCombo->project()) return; const QString pluginId(m_formDataSourceCombo->selectedPluginId()); bool dataSourceFound = false; QString name(m_formDataSourceCombo->selectedName()); const bool isIdAcceptable = pluginId == QLatin1String("org.kexi-project.table") || pluginId == QLatin1String("org.kexi-project.query"); if (isIdAcceptable && m_formDataSourceCombo->isSelectionValid()) { KDbTableOrQuerySchema *tableOrQuery = new KDbTableOrQuerySchema( m_formDataSourceCombo->project()->dbConnection(), name.toLatin1(), pluginId == "org.kexi-project.table"); if (tableOrQuery->table() || tableOrQuery->query()) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_fieldListView->setSchema(tableOrQuery); #else m_tableOrQuerySchema = tableOrQuery; #endif dataSourceFound = true; + m_slotWidgetDataSourceTextChangedEnabled = false; // block clearing widget's data source property m_widgetDataSourceCombo->setTableOrQuery(name, pluginId == "org.kexi-project.table"); + m_slotWidgetDataSourceTextChangedEnabled = true; } else { delete tableOrQuery; } } if (!dataSourceFound) { m_widgetDataSourceCombo->setTableOrQuery(QString(), true); } - m_gotoButton->setEnabled(dataSourceFound); + //! @todo m_gotoButton->setEnabled(dataSourceFound); if (dataSourceFound) { slotFieldListViewSelectionChanged(); } else { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_addField->setEnabled(false); #endif } updateSourceFieldWidgetsAvailability(); emit formDataSourceChanged(pluginId, name); } void KexiDataSourcePage::slotFieldSelected() { KDbField::Type dataType = KDbField::InvalidType; #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT //! @todo this should also work for expressions KDbField *field = m_fieldListView->schema()->field( m_widgetDataSourceCombo->fieldOrExpression()); #else KDbField *field = m_tableOrQuerySchema->field( m_widgetDataSourceCombo->fieldOrExpression()); //temp #endif if (field) dataType = field->type(); emit dataSourceFieldOrExpressionChanged( m_widgetDataSourceCombo->fieldOrExpression(), m_widgetDataSourceCombo->fieldOrExpressionCaption(), dataType ); } void KexiDataSourcePage::setFormDataSource(const QString& pluginId, const QString& name) { m_formDataSourceCombo->setDataSource(pluginId, name); } -#define KexiDataSourcePage_FADE 1 - -void KexiDataSourcePage::assignPropertySet(KPropertySet* propertySet) +void KexiDataSourcePage::assignPropertySet(KPropertySet* propertySet, AssignFlags flags) { + const KexiStyle::PropertyPane &s = KexiStyle::propertyPane(); QString objectName; if (propertySet) objectName = propertySet->propertyValue("objectName").toString(); - if (!objectName.isEmpty() && objectName == m_currentObjectName) + if (flags != ForceAssign && !objectName.isEmpty() && objectName == m_currentObjectName) { return; //the same object + } m_currentObjectName = objectName; - -//! @todo -#if KexiDataSourcePage_FADE - KexiFadeWidgetEffect *animation = 0; - if (isVisible()) - animation = new KexiFadeWidgetEffect(this); -#endif QString objectClassName; if (propertySet) { objectClassName = propertySet->propertyValue("this:className").toString(); } - updateInfoLabelForPropertySet(propertySet); const bool isForm = objectClassName == "KexiDBForm"; const bool multipleSelection = objectClassName == "special:multiple"; const bool hasDataSourceProperty = propertySet && propertySet->contains("dataSource") && !multipleSelection; if (!isForm) { //this is a widget QString dataSource; if (hasDataSourceProperty) { if (propertySet) { dataSource = (*propertySet)["dataSource"].value().toString(); } m_noDataSourceAvailableLabel->hide(); m_widgetDataSourceCombo->setFieldOrExpression(dataSource); - m_widgetDataSourceCombo->setEnabled(true); - m_widgetDSLabel->show(); m_widgetDataSourceCombo->show(); - m_widgetDataSourceComboSpacer->show(); + s.setFormLabelAndWidgetVisible(m_widgetDataSourceContainer, m_formLyr, true); updateSourceFieldWidgetsAvailability(); } } if (isForm) { - m_noDataSourceAvailableLabel->hide(); + // no source field can be set + s.setFormLabelAndWidgetVisible(m_widgetDataSourceContainer, m_formLyr, false); } else if (!hasDataSourceProperty) { if (multipleSelection) { m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableMultiText); } else { m_noDataSourceAvailableLabel->setText(m_noDataSourceAvailableSingleText); } + s.setFormLabelAndWidgetVisible(m_widgetDataSourceContainer, m_formLyr, true); + m_widgetDataSourceCombo->hide(); m_noDataSourceAvailableLabel->show(); m_widgetDataSourceCombo->setEditText(QString()); } - if (isForm || !hasDataSourceProperty) { - //no source field can be set - m_widgetDSLabel->hide(); - m_widgetDataSourceCombo->hide(); - m_widgetDataSourceComboSpacer->hide(); - } -//! @todo -#if KexiDataSourcePage_FADE - if (animation) - animation->start(100); -#endif + m_mainLyr->update(); + qApp->processEvents(); } void KexiDataSourcePage::slotFieldListViewSelectionChanged() { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT //update "add field" button's state for (Q3ListViewItemIterator 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_formDataSourceCombo->isSelectionValid(); m_widgetDataSourceCombo->setEnabled(hasDataSource); - m_widgetDSLabel->setEnabled(hasDataSource); #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT m_fieldListView->setEnabled(hasDataSource); m_availableFieldsLabel->setEnabled(hasDataSource); m_mousePointerLabel->setEnabled(hasDataSource); m_availableFieldsDescriptionLabel->setEnabled(hasDataSource); #endif } - diff --git a/src/plugins/forms/kexidatasourcepage.h b/src/plugins/forms/kexidatasourcepage.h index ac4342e7f..e2a3e5203 100644 --- a/src/plugins/forms/kexidatasourcepage.h +++ b/src/plugins/forms/kexidatasourcepage.h @@ -1,114 +1,128 @@ /* This file is part of the KDE project - Copyright (C) 2005-2009 Jarosław Staniek + Copyright (C) 2005-2016 Jarosław Staniek 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 "kexiformutils_export.h" #include -#include - #include #include #include #include +#include + class KexiDataSourceComboBox; class KexiFieldComboBox; class KexiFieldListView; class KexiProject; class QToolButton; class QLabel; +class QVBoxLayout; +class QGridLayout; +class QSpacerItem; //! A page within form designer's property tabbed pane, providing data source editor -class KEXIFORMUTILS_EXPORT KexiDataSourcePage : public KexiPropertyPaneViewBase +class KEXIFORMUTILS_EXPORT KexiDataSourcePage : public QWidget { Q_OBJECT public: - explicit KexiDataSourcePage(QWidget *parent); + explicit KexiDataSourcePage(QWidget *parent = 0); virtual ~KexiDataSourcePage(); + //QSize sizeHint() const Q_DECL_OVERRIDE { return QSize(); } + + enum AssignFlag { + NoFlags = 0, + ForceAssign = 1 + }; + Q_DECLARE_FLAGS(AssignFlags, AssignFlag) + public Q_SLOTS: void setProject(KexiProject *prj); void clearFormDataSourceSelection(bool alsoClearComboBox = true); void clearWidgetDataSourceSelection(); //! Sets data source of a currently selected form. //! This is performed on form initialization and on activating. void setFormDataSource(const QString& pluginId, const QString& name); //! Receives a pointer to a new property \a set (from KexiFormView::managerPropertyChanged()) - void assignPropertySet(KPropertySet* propertySet); + void assignPropertySet(KPropertySet* propertySet, AssignFlags flags = NoFlags); Q_SIGNALS: //! Signal emitted when helper button 'go to selected data source' is clicked. void jumpToObjectRequested(const QString& mime, const QString& name); //! Signal emitted when form's data source has been changed. It's connected to the Form Manager. void formDataSourceChanged(const QString& mime, const QString& 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, KDbField::Type type); /*! Signal emitted when 'insert fields' button has been clicked */ void insertAutoFields(const QString& sourcePartClass, const QString& sourceName, const QStringList& fields); protected Q_SLOTS: void slotWidgetDataSourceTextChanged(const QString &text); void slotFormDataSourceTextChanged(const QString &text); void slotFormDataSourceChanged(); void slotFieldSelected(); void slotGotoSelected(); void slotInsertSelectedFields(); void slotFieldListViewSelectionChanged(); void slotFieldDoubleClicked(const QString& sourcePluginId, const QString& sourceName, const QString& fieldName); protected: void updateSourceFieldWidgetsAvailability(); + QVBoxLayout *m_mainLyr; + QGridLayout *m_formLyr; KexiFieldComboBox *m_widgetDataSourceCombo; - QWidget *m_widgetDataSourceComboSpacer; + QWidget *m_widgetDataSourceContainer; KexiDataSourceComboBox* m_formDataSourceCombo; QWidget *m_formDataSourceComboSpacer; - QLabel *m_dataSourceLabel, *m_noDataSourceAvailableLabel, *m_widgetDSLabel; + QLabel *m_noDataSourceAvailableLabel; QToolButton *m_gotoButton; QString m_noDataSourceAvailableSingleText; QString m_noDataSourceAvailableMultiText; bool m_insideClearFormDataSourceSelection; + bool m_slotWidgetDataSourceTextChangedEnabled; #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT KexiFieldListView* m_availableFieldsLabel; KexiFieldListView* m_fieldListView; QLabel *m_mousePointerLabel; QLabel *m_availableFieldsDescriptionLabel; QToolButton *m_addField; #else KDbTableOrQuerySchema *m_tableOrQuerySchema; //!< temp. #endif //! Used only in assignPropertySet() to check whether we already have the set assigned QString m_currentObjectName; }; #endif diff --git a/src/plugins/forms/kexiformpart.cpp b/src/plugins/forms/kexiformpart.cpp index 5ae30e44f..ef0ee3da9 100644 --- a/src/plugins/forms/kexiformpart.cpp +++ b/src/plugins/forms/kexiformpart.cpp @@ -1,394 +1,390 @@ /* This file is part of the KDE project Copyright (C) 2004 Lucijan Busch Copyright (C) 2004 Cedric Pasteur Copyright (C) 2005 Jarosław Staniek 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 "kexiformpart.h" #include #include #include #include #include #include -#include +#include +#include #include #include #include #include #include #include "widgets/kexidbform.h" #include "kexiformscrollview.h" #include "kexiformmanager.h" #include "kexidatasourcepage.h" #include #include #include #include #include -#include #include #include //! @todo #define KEXI_SHOW_SPLITTER_WIDGET //! @internal class Q_DECL_HIDDEN KexiFormPart::Private { public: Private() { } ~Private() { delete static_cast(widgetTreeWidget); delete static_cast(dataSourcePage); } QPointer dataSourcePage; QPointer widgetTree; QPointer widgetTreeWidget; }; KexiFormPart::KexiFormPart(QObject *parent, const QVariantList &l) : KexiPart::Part(parent, xi18nc("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"), xi18nc("tooltip", "Create new form"), xi18nc("what's this", "Creates new form."), l) , d(new Private) { setInternalPropertyValue("newObjectsAreDirty", true); // Only create form manager if it's not yet created. // KexiReportPart could have created it already. KexiFormManager::self()->init(this, d->widgetTree); // this should create KexiFormManager singleton } KexiFormPart::~KexiFormPart() { delete d; } void KexiFormPart::initPartActions() { } void KexiFormPart::initInstanceActions() { //connect actions provided by widget factories createSharedAction(Kexi::DesignViewMode, xi18n("Clear Widget Contents"), koIconName("edit-clear"), QKeySequence(), "formpart_clear_contents"); createSharedAction(Kexi::DesignViewMode, xi18n("Edit Tab Order..."), KexiIconName("widgets-tab-order"), QKeySequence(), "formpart_taborder"); //! @todo createSharedAction(Kexi::DesignViewMode, xi18n("Edit Pixmap Collection"), koIconName("icons"), 0, "formpart_pixmap_collection"); //! @todo createSharedAction(Kexi::DesignViewMode, xi18n("Edit Form Connections"), koIconName("connections"), 0, "formpart_connections"); createSharedAction(Kexi::DesignViewMode, xi18n("Bring Widget to Front"), koIconName("object-order-front"), QKeySequence(), "formpart_format_raise"); createSharedAction(Kexi::DesignViewMode, xi18n("Send Widget to Back"), koIconName("object-order-back"), QKeySequence(), "formpart_format_lower"); #ifdef KEXI_SHOW_UNFINISHED createSharedAction(Kexi::DesignViewMode, futureI18n("Other Widgets"), QString(), QKeySequence(), "other_widgets_menu", "KActionMenu"); #endif QAction *action = createSharedAction(Kexi::DesignViewMode, xi18n("Align Widgets Position"), koIconName("align-horizontal-left"), QKeySequence(), "formpart_align_menu", "KActionMenu"); KActionMenu *menu = static_cast(action); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Left"), koIconName("align-horizontal-left"), QKeySequence(), "formpart_align_to_left")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Right"), koIconName("align-horizontal-right"), QKeySequence(), "formpart_align_to_right")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Top"), koIconName("align-vertical-top"), QKeySequence(), "formpart_align_to_top")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Bottom"), koIconName("align-vertical-bottom"), QKeySequence(), "formpart_align_to_bottom")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Grid"), koIconName("align-grid"), QKeySequence(), "formpart_align_to_grid")); action = createSharedAction(Kexi::DesignViewMode, xi18n("Adjust Widgets Size"), koIconName("fit-grid"), QKeySequence(), "formpart_adjust_size_menu", "KActionMenu"); menu = static_cast(action); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Fit"), koIconName("fit-contents"), QKeySequence(), "formpart_adjust_to_fit")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Grid"), koIconName("fit-grid"), QKeySequence(), "formpart_adjust_size_grid")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Shortest"), koIconName("fit-shortest"), QKeySequence(), "formpart_adjust_height_small")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Tallest"), koIconName("fit-tallest"), QKeySequence(), "formpart_adjust_height_big")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Narrowest"), koIconName("fit-narrowest"), QKeySequence(), "formpart_adjust_width_small")); menu->addAction(createSharedAction(Kexi::DesignViewMode, xi18n("To Widest"), koIconName("fit-widest"), QKeySequence(), "formpart_adjust_width_big")); } KexiWindowData* KexiFormPart::createWindowData(KexiWindow* window) { return new KexiFormPartTempData(window); } KexiView* KexiFormPart::createView(QWidget *parent, KexiWindow* window, KexiPart::Item *item, Kexi::ViewMode viewMode, QMap*) { Q_ASSERT(item); Q_UNUSED(window); Q_UNUSED(viewMode); qDebug(); KexiMainWindowIface *win = KexiMainWindowIface::global(); if (!win || !win->project() || !win->project()->dbConnection()) return 0; KexiFormView *view = new KexiFormView(parent, win->project()->dbConnection()); view->setObjectName(item->name().toLatin1()); return view; } #ifndef KEXI_NO_FORM_DATASOURCE_WIZARD void KexiFormPart::generateForm(KDbFieldList *list, QDomDocument &domDoc) { //this form generates a .ui from KDbFieldList 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", "QLineEdit"); 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); } #endif KLocalizedString KexiFormPart::i18nMessage( const QString& englishMessage, KexiWindow* window) const { Q_UNUSED(window); if (englishMessage == "Design of object %1 has been modified.") return kxi18nc(I18NC_NOOP("@info", "Design of form %1 has been modified.")); if (englishMessage == "Object %1 already exists.") return kxi18nc(I18NC_NOOP("@info", "Form %1 already exists.")); return Part::i18nMessage(englishMessage, window); } KexiDataSourcePage* KexiFormPart::dataSourcePage() const { return d->dataSourcePage; } KFormDesigner::WidgetTreeWidget* KexiFormPart::widgetTreePage() const { return d->widgetTree; } -void KexiFormPart::setupPropertyPane(QToolBox *toolBox) +void KexiFormPart::setupPropertyPane(KexiPropertyPaneWidget *pane) { if (!d->dataSourcePage) { d->dataSourcePage = new KexiDataSourcePage(0); d->dataSourcePage->setObjectName("dataSourcePage"); connect(d->dataSourcePage, SIGNAL(jumpToObjectRequested(QString,QString)), KexiMainWindowIface::global()->thisWidget(), SLOT(highlightObject(QString,QString))); connect(d->dataSourcePage, SIGNAL(formDataSourceChanged(QString,QString)), KexiFormManager::self(), SLOT(setFormDataSource(QString,QString))); connect(d->dataSourcePage, SIGNAL(dataSourceFieldOrExpressionChanged(QString,QString,KDbField::Type)), KexiFormManager::self(), SLOT(setDataSourceFieldOrExpression(QString,QString,KDbField::Type))); #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT connect(d->dataSourcePage, SIGNAL(insertAutoFields(QString,QString,QStringList)), KexiFormManager::self(), SLOT(insertAutoFields(QString,QString,QStringList))); #endif } KexiProject *prj = KexiMainWindowIface::global()->project(); d->dataSourcePage->setProject(prj); - if (toolBox->indexOf(d->dataSourcePage) == -1) { - toolBox->addItem(d->dataSourcePage, xi18n("Data source")); - } + pane->addSection(d->dataSourcePage, xi18n("Data source")); if (!d->widgetTreeWidget) { d->widgetTreeWidget = new QWidget; QVBoxLayout *lyr = new QVBoxLayout(d->widgetTreeWidget); lyr->setContentsMargins(0, 0, 0, 0); d->widgetTree = new KFormDesigner::WidgetTreeWidget; d->widgetTree->setObjectName("KexiFormPart:WidgetTreeWidget"); lyr->addWidget(d->widgetTree); } - if (toolBox->indexOf(d->widgetTreeWidget) == -1) { - toolBox->addItem(d->widgetTreeWidget, xi18n("Widgets")); - } + //! @todo pane->addSection(d->widgetTreeWidget, xi18n("Widgets")); } //---------------- KexiFormPartTempData::KexiFormPartTempData(QObject* parent) : KexiWindowData(parent) { } KexiFormPartTempData::~KexiFormPartTempData() { } diff --git a/src/plugins/forms/kexiformview.cpp b/src/plugins/forms/kexiformview.cpp index acd07d25b..0dd11d5f0 100644 --- a/src/plugins/forms/kexiformview.cpp +++ b/src/plugins/forms/kexiformview.cpp @@ -1,1293 +1,1301 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004-2016 Jarosław Staniek 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "widgets/kexidbform.h" #include "kexiformscrollview.h" #include "kexidatasourcepage.h" #include "kexiformmanager.h" #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT #include "kexidbautofield.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! @todo #define KEXI_SHOW_SPLITTER_WIDGET class Q_DECL_HIDDEN KexiFormView::Private { public: Private() : resizeMode(KexiFormView::ResizeDefault) , query(0) , queryIsOwned(false) , cursor(0) { } KexiDBForm *dbform; KexiFormScrollView *scrollView; /*! 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 (d->previousDataSourceString is used for that). */ QString previousDataSourceString; int resizeMode; KDbQuerySchema* query; /*! True, if d->query is created as temporary object within this form. If user selected an existing, predefined (stored) query, d->queryIsOwned will be false, so the query object will not be destroyed. */ bool queryIsOwned; KDbCursor *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 delayedFormContentsResizeOnShow; //! Used in setFocusInternal() QPointer setFocusInternalOnce; #ifndef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT /*! 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 widgetGeometryForRecentInsertAutoFields; #endif //! Cached form pointer QPointer form; }; KexiFormView::KexiFormView(QWidget *parent, bool dbAware) : KexiDataAwareView(parent) , d(new Private) { Q_UNUSED(dbAware); d->delayedFormContentsResizeOnShow = 0; //! @todo remove? setSortedProperties(true); d->scrollView = new KexiFormScrollView( // will be added to layout this, viewMode() == Kexi::DataViewMode); // in KexiDataAwareView::init() (void)initForm(); if (viewMode() == Kexi::DesignViewMode) { connect(form(), SIGNAL(propertySetSwitched()), this, SLOT(slotPropertySetSwitched())); connect(form(), SIGNAL(modified(bool)), this, SLOT(setDirty(bool))); connect(d->scrollView, SIGNAL(resized()), this, SLOT(setFormModified())); connect(d->dbform, SIGNAL(handleDragMoveEvent(QDragMoveEvent*)), this, SLOT(slotHandleDragMoveEvent(QDragMoveEvent*))); connect(d->dbform, SIGNAL(handleDropEvent(QDropEvent*)), this, SLOT(slotHandleDropEvent(QDropEvent*))); // action stuff plugSharedAction("formpart_taborder", form(), SLOT(editTabOrder())); plugSharedAction("formpart_adjust_size", form(), SLOT(adjustWidgetSize())); //! @todo add formpart_pixmap_collection action //! @todo add formpart_connections action plugSharedAction("edit_copy", form(), SLOT(copyWidget())); plugSharedAction("edit_cut", form(), SLOT(cutWidget())); plugSharedAction("edit_paste", form(), SLOT(pasteWidget())); plugSharedAction("edit_delete", form(), SLOT(deleteWidget())); plugSharedAction("edit_select_all", form(), SLOT(selectAll())); plugSharedAction("formpart_clear_contents", form(), SLOT(clearWidgetContent())); plugSharedAction("edit_undo", form(), SLOT(undo())); plugSharedAction("edit_redo", form(), SLOT(redo())); plugSharedAction("formpart_format_raise", form(), SLOT(bringWidgetToFront())); plugSharedAction("formpart_format_lower", form(), SLOT(sendWidgetToBack())); plugSharedAction("other_widgets_menu", form(), 0); setAvailable("other_widgets_menu", true); plugSharedAction("formpart_align_menu", form(), 0); plugSharedAction("formpart_align_to_left", form(), SLOT(alignWidgetsToLeft())); plugSharedAction("formpart_align_to_right", form(), SLOT(alignWidgetsToRight())); plugSharedAction("formpart_align_to_top", form(), SLOT(alignWidgetsToTop())); plugSharedAction("formpart_align_to_bottom", form(), SLOT(alignWidgetsToBottom())); plugSharedAction("formpart_align_to_grid", form(), SLOT(alignWidgetsToGrid())); plugSharedAction("formpart_adjust_size_menu", form(), 0); plugSharedAction("formpart_adjust_to_fit", form(), SLOT(adjustWidgetSize())); plugSharedAction("formpart_adjust_size_grid", form(), SLOT(adjustSizeToGrid())); plugSharedAction("formpart_adjust_height_small", form(), SLOT(adjustHeightToSmall())); plugSharedAction("formpart_adjust_height_big", form(), SLOT(adjustHeightToBig())); plugSharedAction("formpart_adjust_width_small", form(), SLOT(adjustWidthToSmall())); plugSharedAction("formpart_adjust_width_big", form(), SLOT(adjustWidthToBig())); plugSharedAction("format_font", form(), SLOT(changeFont())); // - setup local actions QList viewActions; QAction* a; a = form()->action("edit_undo"); a->setProperty("iconOnly", true); viewActions << a; a = form()->action("edit_redo"); a->setProperty("iconOnly", true); viewActions << a; setViewActions(viewActions); } KexiDataAwareView::init(d->scrollView, d->scrollView, d->scrollView, /* skip data-awarness if design mode */ viewMode() == Kexi::DesignViewMode); connect(this, SIGNAL(focus(bool)), this, SLOT(slotFocus(bool))); } KexiFormView::~KexiFormView() { deleteQuery(); propertySetSwitched(); delete d; } void KexiFormView::deleteQuery() { if (d->cursor) { KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); conn->deleteCursor(d->cursor); d->cursor = 0; } if (d->queryIsOwned) { delete d->query; } else { //! @todo remove this shared query from listened queries list } d->query = 0; } void KexiFormView::setForm(KFormDesigner::Form *f) { if (viewMode() == Kexi::DataViewMode) tempData()->previewForm = f; else tempData()->form = f; d->form = f; } bool KexiFormView::initForm() { d->dbform = new KexiDBForm(d->scrollView->widget(), d->scrollView); if (viewMode() == Kexi::DataViewMode) { d->scrollView->setWidget(d->dbform); } else { d->scrollView->setMainAreaWidget(d->dbform); } d->dbform->setObjectName( xi18nc("A prefix for identifiers of forms. Based on that, identifiers such as " "form1, form2 are generated. " "This string can be used to refer the widget object as variables in programming " "languages or macros so it must _not_ contain white spaces and non latin1 characters, " "should start with lower case letter and if there are subsequent words, these should " "start with upper case letter. Example: smallCamelCase. " "Moreover, try to make this prefix as short as possible.", "form")); QPalette pal(d->dbform->palette()); pal.setBrush(QPalette::Window, palette().brush(QPalette::Window)); d->dbform->setPalette(pal); // avoid inheriting QPalette::Window role d->scrollView->setResizingEnabled(true); if (viewMode() == Kexi::DataViewMode) { d->scrollView->recordNavigator()->setRecordHandler(d->scrollView); QPalette pal(d->scrollView->viewport()->palette()); pal.setBrush(d->scrollView->viewport()->backgroundRole(), d->dbform->palette().brush(d->dbform->backgroundRole())); d->scrollView->viewport()->setPalette(pal); } setForm( new KFormDesigner::Form( KexiFormManager::self()->library(), viewMode() == Kexi::DataViewMode ? KFormDesigner::Form::DataMode : KFormDesigner::Form::DesignMode, *KexiMainWindowIface::global()->actionCollection(), *KexiFormManager::self()->widgetActionGroup()) ); form()->createToplevel(d->dbform, d->dbform); const bool newForm = window()->id() < 0; KDbFieldList *fields = 0; #ifndef KEXI_NO_FORM_DATASOURCE_WIZARD if (newForm) { // Show the form wizard if this is a new Form KexiDataSourceWizard *w = new KexiDataSourceWizard( KexiMainWindowIface::global()->thisWidget()); if (!w->exec()) fields = 0; else fields = w->fields(); delete w; } #endif if (fields) { #ifndef KEXI_NO_FORM_DATASOURCE_WIZARD QDomDocument dom; formPart()->generateForm(fields, dom); KFormDesigner::FormIO::loadFormFromDom(form(), d->dbform, &dom); //! @todo handle errors #endif } else { if (!loadForm()) { return false; } } if (form()->autoTabStops()) form()->autoAssignTabStops(); //collect tab order information d->dbform->updateTabStopsOrder(form()); if (viewMode() == Kexi::DesignViewMode) { connect(form(), SIGNAL(widgetNameChanged(QByteArray,QByteArray)), this, SLOT(slotWidgetNameChanged(QByteArray,QByteArray))); connect(form(), SIGNAL(selectionChanged(QWidget*,KFormDesigner::Form::WidgetSelectionFlags)), this, SLOT(slotWidgetSelectionChanged(QWidget*,KFormDesigner::Form::WidgetSelectionFlags))); form()->selectWidget(form()->widget()); } else { form()->setMode(KFormDesigner::Form::DataMode); d->dbform->setMinimumSize(d->dbform->size()); // make vscrollbar appear when viewport is too small } d->scrollView->setForm(form()); d->scrollView->refreshContentsSize(); if (newForm && !fields) { /* Our form's area will be resized more than once. Let's resize form widget itself later. */ d->delayedFormContentsResizeOnShow = 3; } slotPropertySetSwitched(); // this prepares the data source page updateDataSourcePage(); if (!newForm && viewMode() == Kexi::DesignViewMode) { form()->clearUndoStack(); } return true; } void KexiFormView::updateAutoFieldsDataSource() { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT //! @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(d->dbform->dataSource()); QString dataSourcePartClassString(d->dbform->dataSourcePluginId()); KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableOrQuerySchema tableOrQuery( conn, dataSourceString.toLatin1(), dataSourcePartClassString == "org.kexi-project.table"); if (!tableOrQuery.table() && !tableOrQuery.query()) return; foreach (KFormDesigner::ObjectTreeItem *item, *form()->objectTree()->hash()) { KexiDBAutoField *afWidget = dynamic_cast(item->widget()); if (afWidget) { KDbQueryColumnInfo *colInfo = tableOrQuery.columnInfo(afWidget->dataSource()); if (colInfo) { afWidget->setColumnInfo(colInfo); } } } #endif } +void KexiFormView::propertySetSwitched() +{ + KexiDataAwareView::propertySetSwitched(); + updateDataSourcePage(); + if (viewMode() == Kexi::DesignViewMode) { + formPart()->dataSourcePage()->assignPropertySet(form()->propertySet(), KexiDataSourcePage::ForceAssign); + } +} + 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(d->dbform->dataSource()); QString dataSourcePartClassString(d->dbform->dataSourcePluginId()); KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableOrQuerySchema tableOrQuery( conn, dataSourceString.toLatin1(), dataSourcePartClassString == "org.kexi-project.table"); if (!tableOrQuery.table() && !tableOrQuery.query()) return; foreach (KFormDesigner::ObjectTreeItem *item, *form()->objectTree()->hash()) { // (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(item->widget()); if (subpropIface && subpropIface->subwidget() && item->subproperties()) { QWidget *subwidget = subpropIface->subwidget(); QHash* subprops = item->subproperties(); for (QHash::const_iterator subpropIt = subprops->constBegin(); subpropIt != subprops->constEnd(); ++subpropIt) { //qDebug() << "delayed setting of the subproperty: widget=" // << item->widget()->objectName() << " prop=" << subpropIt.key() << " val=" // << subpropIt.value(); QMetaProperty meta = KexiUtils::findPropertyWithSuperclasses( subwidget, qPrintable(subpropIt.key())); if (meta.isValid()) { // 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.isEnumType() && subpropIt.value().type() == QVariant::StringList) { const QByteArray keysCombined(subpropIt.value().toStringList().join("|").toLatin1()); subwidget->setProperty(subpropIt.key().toLatin1(), meta.enumerator().keysToValue(keysCombined.constData())); } else { subwidget->setProperty(subpropIt.key().toLatin1(), subpropIt.value()); } } }//for } } } //! Used in KexiFormView::loadForm() static void setUnsavedBLOBIdsForDataViewMode( QWidget* widget, const QHash& unsavedLocalBLOBsByName) { if (widget) { if (-1 != widget->metaObject()->indexOfProperty("pixmapId")) { const KexiBLOBBuffer::Id_t blobID = unsavedLocalBLOBsByName.value(widget->objectName().toLatin1()); if (blobID > 0) //! @todo KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - fix it widget->setProperty( "pixmapId", int(blobID)); } const QList list(widget->findChildren()); if (list.isEmpty()) return; foreach(QWidget *w, list) { setUnsavedBLOBIdsForDataViewMode(w, unsavedLocalBLOBsByName); } } } bool KexiFormView::loadForm() { //! @todo also load d->resizeMode //qDebug() << "Loading the form with id" << window()->id(); // If we are previewing the Form, use the tempData instead of the form stored in the db if (viewMode() == Kexi::DataViewMode && !tempData()->tempForm.isNull()) { if (!KFormDesigner::FormIO::loadFormFromString(form(), d->dbform, tempData()->tempForm)) { return false; } setUnsavedBLOBIdsForDataViewMode(d->dbform, tempData()->unsavedLocalBLOBsByName); updateAutoFieldsDataSource(); updateValuesForSubproperties(); return true; } if (!window()->neverSaved()) { // normal load QString data; if (!loadDataBlock(&data)) { return false; } if (!KFormDesigner::FormIO::loadFormFromString(form(), d->dbform, data)) { return false; } } //"autoTabStops" property is loaded -set it within the form tree as well form()->setAutoTabStops(d->dbform->autoTabStops()); updateAutoFieldsDataSource(); updateValuesForSubproperties(); return true; } void KexiFormView::slotPropertySetSwitched() { propertySetReloaded(); if (viewMode() == Kexi::DesignViewMode) { formPart()->dataSourcePage()->assignPropertySet(form()->propertySet()); } } tristate KexiFormView::beforeSwitchTo(Kexi::ViewMode mode, bool *dontStore) { Q_ASSERT(dontStore); if (mode != viewMode()) { if (viewMode() == Kexi::DataViewMode) { if (!d->scrollView->acceptRecordEditing()) return cancelled; d->scrollView->beforeSwitchView(); } else { //remember our pos tempData()->scrollViewContentsPos = QPoint(d->scrollView->horizontalScrollBar()->value(), d->scrollView->verticalScrollBar()->value()); } } if (d->scrollView->data() && viewMode() == Kexi::DataViewMode) { //old data won't be needed nor valid d->scrollView->setData(0, false); } // we don't store on db, but in our TempData *dontStore = true; if (isDirty() && (mode == Kexi::DataViewMode) && form()->objectTree()) { KexiFormPartTempData* 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 (QHash::const_iterator it = temp->unsavedLocalBLOBs.constBegin(); it != temp->unsavedLocalBLOBs.constEnd(); ++it) { if (!it.key()) continue; temp->unsavedLocalBLOBsByName.insert(it.key()->objectName().toLatin1(), it.value()); } } return true; } tristate KexiFormView::afterSwitchFrom(Kexi::ViewMode mode) { if (mode == 0 || mode == Kexi::DesignViewMode) { if (window()->neverSaved()) { d->scrollView->refreshContentsSizeLater(); } } if (mode == Kexi::DataViewMode) { //preserve contents pos after switching to other view d->scrollView->horizontalScrollBar()->setValue(tempData()->scrollViewContentsPos.x()); d->scrollView->verticalScrollBar()->setValue(tempData()->scrollViewContentsPos.y()); } if ((mode == Kexi::DesignViewMode) && viewMode() == Kexi::DataViewMode) { // The form may have been modified, so we must recreate the preview delete d->dbform; // also deletes form() if (!initForm()) { return false; } //reset position d->scrollView->horizontalScrollBar()->setValue(0); d->scrollView->verticalScrollBar()->setValue(0); d->dbform->move(0, 0); } //update tab stops if needed if (viewMode() == Kexi::DataViewMode) { } else { //set "autoTabStops" property d->dbform->setAutoTabStops(form()->autoTabStops()); } if (viewMode() == Kexi::DataViewMode) { //TMP!! initDataSource(); //handle events for this form d->scrollView->setMainWidgetForEventHandling(d->dbform); //set focus on 1st focusable widget which has valid dataSource property set QList *orderedFocusWidgets = d->dbform->orderedFocusWidgets(); if (!orderedFocusWidgets->isEmpty()) { KexiUtils::unsetFocusWithReason(QApplication::focusWidget(), Qt::TabFocusReason); QWidget *widget = 0; foreach(widget, *orderedFocusWidgets) { KexiFormDataItemInterface *iface = dynamic_cast(widget); if (iface) { //qDebug() << iface->dataSource(); } if (iface && iface->columnInfo() && !iface->isReadOnly() /*! @todo add option for skipping autoincremented fields */ /* also skip autoincremented fields:*/ && !iface->columnInfo()->field()->isAutoIncrement()) { break; } } if (!widget) //eventually, focus first available widget if nothing other is available widget = orderedFocusWidgets->first(); widget->setFocus(); KexiUtils::setFocusWithReason(widget, Qt::TabFocusReason); d->setFocusInternalOnce = widget; } if (d->query) d->scrollView->selectFirstRecord(); } //dirty only if it's a new object if (mode == Kexi::NoViewMode) setDirty(window()->partItem()->neverSaved()); updateActionsInternal(); return true; } KPropertySet* KexiFormView::propertySet() { return d->form->propertySet(); } KexiFormPartTempData* KexiFormView::tempData() const { return dynamic_cast(window()->data()); } KexiFormPart* KexiFormView::formPart() const { return dynamic_cast(part()); } void KexiFormView::initDataSource() { deleteQuery(); //! @todo also handle anonymous (not stored) queries provided as statements here KDbTableSchema *tableSchema = 0; KDbConnection *conn = 0; QStringList sources; bool forceReadOnlyDataSource = false; QString dataSourceString(d->dbform->dataSource()); bool ok = !dataSourceString.isEmpty(); if (ok) { //collect all data-aware widgets and create query schema d->scrollView->setMainDataSourceWidget(d->dbform); sources = d->scrollView->usedDataSources(); conn = KexiMainWindowIface::global()->project()->dbConnection(); QString dataSourcePartClassString(d->dbform->dataSourcePluginId()); if (dataSourcePartClassString.isEmpty() /*table type is the default*/ || dataSourcePartClassString == "org.kexi-project.table") { tableSchema = conn->tableSchema(dataSourceString); if (tableSchema) { /* We will build a _minimud-> query schema from selected table fields. */ d->query = new KDbQuerySchema(); d->queryIsOwned = true; if (dataSourcePartClassString.isEmpty()) d->dbform->setDataSourcePluginId("org.kexi-project.table"); //update for compatibility } } if (!tableSchema) { if (dataSourcePartClassString.isEmpty() /*also try to find a query (for compatibility with Kexi<=0.9)*/ || dataSourcePartClassString == "org.kexi-project.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.) d->query = conn->querySchema(dataSourceString); d->queryIsOwned = false; ok = d->query != 0; if (ok && dataSourcePartClassString.isEmpty()) d->dbform->setDataSourcePluginId("org.kexi-project.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 KDb. forceReadOnlyDataSource = true; } else { //no other classes are supported ok = false; } } } QSet invalidSources; if (ok) { KDbIndexSchema *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(); //qDebug() << "pkey added to data sources:" << pkey->names(); } //qDebug() << "sources=" << sources; int index = 0; for (QStringList::ConstIterator it = sources.constBegin(); it != sources.constEnd(); ++it, index++) { /*! @todo add expression support */ QString fieldName((*it).toLower()); //remove "tablename." if it was prepended if (tableSchema && fieldName.startsWith(tableSchema->name() + QLatin1Char('.'), Qt::CaseInsensitive)) fieldName.remove(0, tableSchema->name().length() + 1); //remove "queryname." if it was prepended if (!tableSchema && fieldName.startsWith(d->query->name() + QLatin1Char('.'), Qt::CaseInsensitive)) fieldName.remove(0, d->query->name().length() + 1); KDbField *f = tableSchema ? tableSchema->field(fieldName) : d->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); //qDebug() << "invalidSources+=" << index << " (" << (*it) << ")"; continue; } if (tableSchema) { if (!d->query->hasField(*f)) { //we're building a new query: add this field d->query->addField(f); } } } if (invalidSources.count() == sources.count()) { //all data sources are invalid! don't execute the query deleteQuery(); } else { qDebug() << d->query->parameters(); // like in KexiQueryView::executeQuery() QList params; { KexiUtils::WaitCursorRemover remover; params = KexiQueryParameters::getParameters(this, *conn->driver(), d->query, &ok); } if (ok) //input cancelled d->cursor = conn->executeQuery(d->query, params); } d->scrollView->invalidateDataSources(invalidSources, d->query); ok = d->cursor != 0; } if (!invalidSources.isEmpty()) d->dbform->updateTabStopsOrder(); if (ok) { //! @todo PRIMITIVE!! data setting: //! @todo KDbTableViewData is not a great name for data class here... rename/move? KDbTableViewData* data = new KDbTableViewData(d->cursor); if (forceReadOnlyDataSource) data->setReadOnly(true); data->preloadAllRecords(); ///*! @todo few backends return result count for free! - no need to reopen() */ // int resultCount = -1; // if (ok) { // resultCount = d->conn->resultCount(d->conn->selectStatement(*d->query)); // ok = d->cursor->reopen(); // } // if (ok) // ok = ! (!d->cursor->moveFirst() && d->cursor->error()); d->scrollView->setData(data, true /*owner*/); } else { d->scrollView->setData(0, false); } } void KexiFormView::setFormModified() { form()->setModified(true); } KDbObject* KexiFormView::storeNewData(const KDbObject& object, KexiView::StoreNewDataOptions options, bool *cancel) { Q_ASSERT(cancel); KDbObject *s = KexiView::storeNewData(object, options, cancel); //qDebug() << "new id:" << s->id(); if (!s || *cancel) { delete s; return 0; } if (!storeData()) { //failure: remove object's object data to avoid garbage KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); conn->removeObject(s->id()); delete s; return 0; } return s; } tristate KexiFormView::storeData(bool dontAsk) { Q_UNUSED(dontAsk); //qDebug() << window()->partItem()->name() << "[" << window()->id() << "]"; //-- first, store local BLOBs, so identifiers can be updated //! @todo remove unused data stored previously KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableSchema *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(); KDbFieldList *blobsFieldsWithoutID = blobsTable->subList(blobsFieldNamesWithoutID); KDbPreparedStatement st = conn->prepareStatement( KDbPreparedStatement::InsertStatement, blobsFieldsWithoutID); if (!st.isValid()) { delete blobsFieldsWithoutID; //! @todo show message return false; } KexiBLOBBuffer *blobBuf = KexiBLOBBuffer::self(); KexiFormView *designFormView = dynamic_cast( window()->viewForMode(Kexi::DesignViewMode)); if (designFormView) { for (QHash::const_iterator it = tempData()->unsavedLocalBLOBs.constBegin(); it != tempData()->unsavedLocalBLOBs.constEnd(); ++it) { if (!it.key()) { qWarning() << "it.key()==0 !"; continue; } //qDebug() << "name=" << it.key()->objectName() << " dataID=" << it.value(); KexiBLOBBuffer::Handle h(blobBuf->objectForId(it.value(), /*!stored*/false)); if (!h) continue; //no BLOB assigned QString originalFileName(h.originalFileName()); QFileInfo fi(originalFileName); QString caption(fi.baseName().replace('_', ' ').simplified()); KDbPreparedStatementParameters parameters; parameters << h.data() << originalFileName << caption << h.mimeType() << int(/*! @todo unsafe */h.folderId()); if (!st.execute(parameters)) { delete blobsFieldsWithoutID; qWarning() << "execute error"; return false; } delete blobsFieldsWithoutID; blobsFieldsWithoutID = 0; const quint64 storedBLOBID = KDb::lastInsertedAutoIncValue( conn, st.lastInsertRecordId(), "o_id", "kexi__blobs"); if (std::numeric_limits::max() == storedBLOBID) { //! @todo show message? return false; } //qDebug() << "storedDataID=" << storedBLOBID; //! @todo unsafe - fix! h.setStoredWidthID((KexiBLOBBuffer::Id_t)storedBLOBID); //set widget's internal property so it can be saved... const QVariant oldStoredPixmapId(it.key()->property("storedPixmapId")); //! @todo KexiBLOBBuffer::Id_t is unsafe and unsupported by QVariant - fix! it.key()->setProperty("storedPixmapId", QVariant(int(storedBLOBID))); KFormDesigner::ObjectTreeItem *widgetItem = designFormView->form()->objectTree()->lookup(it.key()->objectName()); if (widgetItem) widgetItem->addModifiedProperty("storedPixmapId", oldStoredPixmapId); else qWarning() << "no" << it.key()->objectName() << "widget found within a form"; } } //-- 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.clear(); return true; } //! @todo reuse the action stuff code #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; } } void KexiFormView::slotFormWidgetSelected(KFormDesigner::Form *f) { if (f != form()) return; disableWidgetActions(); enableFormActions(); } 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); //! @todo KEXI3 Port this.. //! @todo 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); } void KexiFormView::setUndoEnabled(bool enabled) { setAvailable("edit_undo", enabled); } void KexiFormView::setRedoEnabled(bool enabled) { setAvailable("edit_redo", enabled); } #endif //0 int KexiFormView::resizeMode() const { return d->resizeMode; } KFormDesigner::Form* KexiFormView::form() const { return d->form; } QSize KexiFormView::preferredSizeHint(const QSize& otherSize) { return (d->dbform->size() + QSize(d->scrollView->verticalScrollBar()->isVisible() ? d->scrollView->verticalScrollBar()->width()*3 / 2 : 10, d->scrollView->horizontalScrollBar()->isVisible() ? d->scrollView->horizontalScrollBar()->height()*3 / 2 : 10)) .expandedTo(KexiView::preferredSizeHint(otherSize)); } void KexiFormView::resizeEvent(QResizeEvent *e) { if (viewMode() == Kexi::DataViewMode) { d->scrollView->refreshContentsSizeLater(); } KexiView::resizeEvent(e); if (d->delayedFormContentsResizeOnShow > 0) { d->delayedFormContentsResizeOnShow--; d->dbform->resize(e->size() - QSize(30, 30)); } } void KexiFormView::contextMenuEvent(QContextMenuEvent *e) { // qDebug() << form()->selectedWidget() << form()->widget() << e->reason(); if (form()->selectedWidget() && form()->selectedWidget() == form()->widget() && e->reason() == QContextMenuEvent::Keyboard) { // Outer form area received context key. // Redirect the event to top-level form widget. // It will be received in Container::eventFilter(). e->accept(); QContextMenuEvent me(QContextMenuEvent::Keyboard, QPoint(-1, -1)); QApplication::sendEvent(form()->widget(), &me); return; } KexiView::contextMenuEvent(e); } void KexiFormView::setFocusInternal() { if (viewMode() == Kexi::DataViewMode) { if (d->dbform->focusWidget()) { //better-looking focus if (d->setFocusInternalOnce) { KexiUtils::setFocusWithReason(d->setFocusInternalOnce, Qt::OtherFocusReason); d->setFocusInternalOnce = 0; } else { //ok? SET_FOCUS_USING_REASON(d->dbform->focusWidget(), QFocusEvent::Other); } return; } } QWidget::setFocus(); } void KexiFormView::slotFocus(bool in) { Q_UNUSED(in); } void KexiFormView::updateDataSourcePage() { if (viewMode() == Kexi::DesignViewMode) { - KPropertySet *set = form()->propertySet(); - const QString dataSourcePartClass = set->propertyValue("dataSourcePartClass").toString(); - const QString dataSource = set->propertyValue("dataSource").toString(); + const QString dataSourcePartClass = d->dbform->dataSourcePluginId(); + const QString dataSource = d->dbform->dataSource(); formPart()->dataSourcePage()->setFormDataSource(dataSourcePartClass, dataSource); } } void KexiFormView::slotHandleDragMoveEvent(QDragMoveEvent* e) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT if (KexiFieldDrag::canDecode(e)) { e->setAccepted(true); } #else Q_UNUSED(e); #endif } void KexiFormView::slotHandleDropEvent(QDropEvent* e) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT const QWidget *targetContainerWidget = dynamic_cast(sender()); KFormDesigner::ObjectTreeItem *targetContainerWidgetItem = targetContainerWidget ? form()->objectTree()->lookup(targetContainerWidget->objectName()) : 0; if (targetContainerWidgetItem && targetContainerWidgetItem->container() && KexiFieldDrag::canDecode(e)) { QString sourcePartClass, sourceName; QStringList fields; if (!KexiFieldDrag::decode(e, &sourcePartClass, &sourceName, &fields)) return; insertAutoFields(sourcePartClass, sourceName, fields, targetContainerWidgetItem->container(), e->pos()); } #else Q_UNUSED(e); #endif } void KexiFormView::insertAutoFields(const QString& sourcePartClass, const QString& sourceName, const QStringList& fields, KFormDesigner::Container* targetContainer, const QPoint& _pos) { #ifdef KEXI_AUTOFIELD_FORM_WIDGET_SUPPORT if (fields.isEmpty()) return; KDbConnection *conn = KexiMainWindowIface::global()->project()->dbConnection(); KDbTableOrQuerySchema tableOrQuery(conn, sourceName.toLatin1(), sourcePartClass == "org.kexi-project.table"); if (!tableOrQuery.table() && !tableOrQuery.query()) { qWarning() << "no such table/query" << sourceName; return; } QPoint pos(_pos); //if pos is not specified, compute a new position: if (pos == QPoint(-1, -1)) { if (d->widgetGeometryForRecentInsertAutoFields.isValid()) { pos = d->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 //! todo unnamed query columns are not supported QWidgetList widgetsToSelect; KFormDesigner::PropertyCommandGroup *group = new KFormDesigner::PropertyCommandGroup( fields.count() == 1 ? futureI18n("Insert AutoField widget") : futureI18n2("Insert %1 AutoField widgets", fields.count()) ); foreach(const QString& field, fields) { KDbQueryColumnInfo* column = tableOrQuery.columnInfo(field); if (!column) { qWarning() << "no such field" << field << "in table/query" << sourceName; continue; } //! todo add autolabel using field's caption or name 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(), group ); insertCmd->redo(); KFormDesigner::ObjectTreeItem *newWidgetItem = form()->objectTree()->hash()->value(insertCmd->widgetName()); KexiDBAutoField* newWidget = newWidgetItem ? dynamic_cast(newWidgetItem->widget()) : 0; widgetsToSelect.append(newWidget); KFormDesigner::PropertyCommandGroup *subGroup = new KFormDesigner::PropertyCommandGroup( QString(), group); QHash propValues; propValues.insert("dataSource", column->aliasOrName()); propValues.insert("fieldTypeInternal", (int)column->field->type()); propValues.insert("fieldCaptionInternal", column->captionOrAliasOrName()); form()->createPropertyCommandsInDesignMode( newWidget, propValues, subGroup, false/*!addToActiveForm*/); subGroup->redo(); //set data source and caption //-we don't need to use PropertyCommand here beacause we don't need UNDO // for these single commands //resize again because autofield's type changed what can lead to changed sizeHint() QWidgetList list; list.append(newWidget); KFormDesigner::AdjustSizeCommand *adjustCommand = new KFormDesigner::AdjustSizeCommand( *form(), KFormDesigner::AdjustSizeCommand::SizeToFit, list, group); adjustCommand->redo(); 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(d->dbform->geometry()); QRect newFormRect(oldFormRect); newFormRect.setWidth(qMax(d->dbform->width(), widgetsToSelect.last()->geometry().right() + 1)); newFormRect.setHeight(qMax(d->dbform->height(), widgetsToSelect.last()->geometry().bottom() + 1)); if (newFormRect != oldFormRect) { //1. resize by hand d->dbform->setGeometry(newFormRect); //2. store information about resize (void)new KFormDesigner::PropertyCommand( *form(), d->dbform->objectName().toLatin1(), oldFormRect, newFormRect, "geometry", group); } //remember geometry of the last inserted widget d->widgetGeometryForRecentInsertAutoFields = widgetsToSelect.last()->geometry(); } //eventually, add entire command group to active form form()->addCommand(group); //qDebug() << *group; d->scrollView->widget()->update(); d->scrollView->refreshContentsSize(); //select all inserted widgets, if multiple if (widgetsToSelect.count() > 1) { form()->selectWidget(0); foreach (QWidget *w, widgetsToSelect) { form()->selectWidget(w, KFormDesigner::Form::AddToPreviousSelection | KFormDesigner::Form::DontRaise); } } //! @todo eventually, update property pane #else Q_UNUSED(sourcePartClass); Q_UNUSED(sourceName); Q_UNUSED(fields); Q_UNUSED(targetContainer); Q_UNUSED(_pos); #endif } 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); } void KexiFormView::updateActions(bool activated) { if (viewMode()==Kexi::DesignViewMode) { if (activated) { form()->emitActionSignals(); formPart()->widgetTreePage()->setForm(form()); } } KexiDataAwareView::updateActions(activated); updateActionsInternal(); } void KexiFormView::slotWidgetNameChanged(const QByteArray& oldname, const QByteArray& newname) { Q_UNUSED(oldname); Q_UNUSED(newname); //qDebug() << oldname << newname << form()->propertySet().propertyValue("objectName").toString(); KexiMainWindowIface::global()->updatePropertyEditorInfoLabel(); } void KexiFormView::slotWidgetSelectionChanged(QWidget *w, KFormDesigner::Form::WidgetSelectionFlags flags) { Q_UNUSED(w) Q_UNUSED(flags) updateActionsInternal(); } void KexiFormView::updateActionsInternal() { const QWidget* selectedWidget = form()->selectedWidget(); //qDebug() << selectedWidget << (viewMode()==Kexi::DesignViewMode) << widget_assign_action; QByteArray wClass; if (selectedWidget) { wClass = selectedWidget->metaObject()->className(); //qDebug() << wClass; } QAction *widget_assign_action = KexiFormManager::self()->action("widget_assign_action"); if (widget_assign_action) { widget_assign_action->setEnabled( viewMode()==Kexi::DesignViewMode && selectedWidget && (wClass == "QPushButton" || wClass == "KPushButton" || wClass == "KexiDBPushButton" || wClass == "KexiPushButton" || wClass == "KexiDBCommandLinkButton") ); } #ifdef KEXI_DEBUG_GUI QAction *show_form_ui_action = KexiFormManager::self()->action("show_form_ui"); if (show_form_ui_action) { show_form_ui_action->setEnabled(viewMode()==Kexi::DesignViewMode); } #endif } diff --git a/src/plugins/forms/kexiformview.h b/src/plugins/forms/kexiformview.h index dac403185..d99e4ff5b 100644 --- a/src/plugins/forms/kexiformview.h +++ b/src/plugins/forms/kexiformview.h @@ -1,175 +1,178 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004-2016 Jarosław Staniek 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 #include #include #include #include #include #include #include #include "kexiformutils_export.h" #define KEXI_NO_FORM_DATASOURCE_WIZARD class KexiFormPart; class KexiFormPartTempData; 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 KDb-compatible database source. This class provides a single view used inside KexiWindow. 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 KexiDataTableView */ class KEXIFORMUTILS_EXPORT KexiFormView : public KexiDataAwareView { Q_OBJECT public: enum ResizeMode { ResizeAuto = 0, ResizeDefault = ResizeAuto, ResizeFixed = 1, NoResize = 2 /*! @todo */ }; explicit KexiFormView(QWidget *parent, bool dbAware = true); virtual ~KexiFormView(); virtual QSize preferredSizeHint(const QSize& otherSize); int resizeMode() const; 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 Q_SLOTS: /*! Inserts autofields onto the form at \a pos position. \a sourcePartClass can be "org.kexi-project.table" or "org.kexi-project.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& sourcePartClass, const QString& sourceName, const QStringList& fields, KFormDesigner::Container* targetContainerWidget, const QPoint& pos = QPoint(-1, -1)); protected Q_SLOTS: void slotPropertySetSwitched(); void setFormModified(); 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); void slotWidgetSelectionChanged(QWidget *w, KFormDesigner::Form::WidgetSelectionFlags flags); void slotWidgetNameChanged(const QByteArray& oldname, const QByteArray& newname); protected: virtual tristate beforeSwitchTo(Kexi::ViewMode mode, bool *dontStore); virtual tristate afterSwitchFrom(Kexi::ViewMode mode); virtual KPropertySet* propertySet(); virtual KDbObject* storeNewData(const KDbObject& object, KexiView::StoreNewDataOptions options, bool *cancel); virtual tristate storeData(bool dontAsk = false); KexiFormPartTempData* tempData() const; KexiFormPart* formPart() const; void setForm(KFormDesigner::Form *f); bool initForm(); bool loadForm(); //! Used in loadForm() void updateAutoFieldsDataSource(); //! Used in loadForm() void updateValuesForSubproperties(); + //! Reimplemented to pass the information + void propertySetSwitched() Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent *); //! Reimplemented for context key event of top-level form widget. //! Redirects to Container::eventFilter(). virtual void contextMenuEvent(QContextMenuEvent *e); void initDataSource(); virtual void setFocusInternal(); /*! Called after loading the form contents (before showing it). */ void updateTabStopsOrder(); /*! @internal */ void deleteQuery(); /*! @internal */ void updateDataSourcePage(); /*! Reimplemented after KexiView. Updates actions (e.g. availability). */ virtual void updateActions(bool activated); //! Updates internal actions specific to forms. //! @todo merge with other "update" routines? void updateActionsInternal(); private: class Private; Private * const d; }; #endif