summaryrefslogtreecommitdiffstats
path: root/kexi/plugins/tables
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/plugins/tables
downloadkoffice-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/tables')
-rw-r--r--kexi/plugins/tables/Makefile.am28
-rw-r--r--kexi/plugins/tables/kexilookupcolumnpage.cpp419
-rw-r--r--kexi/plugins/tables/kexilookupcolumnpage.h88
-rw-r--r--kexi/plugins/tables/kexitabledesigner_dataview.cpp79
-rw-r--r--kexi/plugins/tables/kexitabledesigner_dataview.h49
-rw-r--r--kexi/plugins/tables/kexitabledesignercommands.cpp281
-rw-r--r--kexi/plugins/tables/kexitabledesignercommands.h188
-rw-r--r--kexi/plugins/tables/kexitabledesignerview.cpp1943
-rw-r--r--kexi/plugins/tables/kexitabledesignerview.h258
-rw-r--r--kexi/plugins/tables/kexitabledesignerview_p.cpp294
-rw-r--r--kexi/plugins/tables/kexitabledesignerview_p.h191
-rw-r--r--kexi/plugins/tables/kexitablehandler.desktop118
-rw-r--r--kexi/plugins/tables/kexitablepart.cpp313
-rw-r--r--kexi/plugins/tables/kexitablepart.h100
-rw-r--r--kexi/plugins/tables/kexitablepartinstui.rc18
-rw-r--r--kexi/plugins/tables/kexitablepartui.rc7
16 files changed, 4374 insertions, 0 deletions
diff --git a/kexi/plugins/tables/Makefile.am b/kexi/plugins/tables/Makefile.am
new file mode 100644
index 00000000..0971a64e
--- /dev/null
+++ b/kexi/plugins/tables/Makefile.am
@@ -0,0 +1,28 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_table.la
+
+kexihandler_table_la_SOURCES = kexitablepart.cpp kexitabledesignerview.cpp kexitabledesignerview_p.cpp \
+ kexitabledesigner_dataview.cpp kexitabledesignercommands.cpp kexilookupcolumnpage.cpp
+
+kexihandler_table_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module
+kexihandler_table_la_LIBADD = $(top_builddir)/kexi/core/libkexicore.la \
+ $(top_builddir)/kexi/kexidb/libkexidb.la \
+ $(top_builddir)/kexi/widget/tableview/libkexidatatable.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la
+
+INCLUDES= $(KOFFICE_INCLUDES) \
+ -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/widget/tableview \
+ -I$(top_srcdir)/kexi/kexidb -I$(top_srcdir)/lib $(all_includes)
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexitablehandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexitablepartui.rc kexitablepartinstui.rc
+
+METASOURCES = AUTO
+
+include ../Makefile.common
diff --git a/kexi/plugins/tables/kexilookupcolumnpage.cpp b/kexi/plugins/tables/kexilookupcolumnpage.cpp
new file mode 100644
index 00000000..9df92794
--- /dev/null
+++ b/kexi/plugins/tables/kexilookupcolumnpage.cpp
@@ -0,0 +1,419 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <[email protected]>
+
+ 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 "kexilookupcolumnpage.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 <koproperty/property.h>
+#include <koproperty/utils.h>
+
+QString mimeTypeToType(const QString& mimeType)
+{
+ if (mimeType=="kexi/table")
+ return "table";
+ else if (mimeType=="kexi/query")
+ return "query";
+//! @todo more types
+ return mimeType;
+}
+
+QString typeToMimeType(const QString& type)
+{
+ if (type=="table")
+ return "kexi/table";
+ else if (type=="query")
+ return "kexi/query";
+//! @todo more types
+ return type;
+}
+
+//----------------------------------------------
+
+//! @internal
+class KexiLookupColumnPage::Private
+{
+ public:
+ Private()
+ : currentFieldUid(-1)
+ , insideClearRowSourceSelection(false)
+ , propertySetEnabled(true)
+ {
+ }
+ ~Private()
+ {
+ }
+
+ bool hasPropertySet() const {
+ return propertySet;
+ }
+
+ void setPropertySet(KoProperty::Set* aPropertySet) {
+ propertySet = aPropertySet;
+ }
+
+ QVariant propertyValue(const QCString& propertyName) const {
+ return propertySet ? propertySet->property(propertyName).value() : QVariant();
+ }
+
+ void changeProperty(const QCString &property, const QVariant &value)
+ {
+ if (!propertySetEnabled)
+ return;
+ propertySet->changeProperty(property, value);
+ }
+
+ void updateInfoLabelForPropertySet(const QString& textToDisplayForNullSet) {
+ KexiPropertyEditorView::updateInfoLabelForPropertySet(
+ objectInfoLabel, propertySet, textToDisplayForNullSet);
+ }
+
+ KexiDataSourceComboBox *rowSourceCombo;
+ KexiFieldComboBox *boundColumnCombo, *visibleColumnCombo;
+ KexiObjectInfoLabel *objectInfoLabel;
+ QLabel *rowSourceLabel, *boundColumnLabel, *visibleColumnLabel;
+ QToolButton *clearRowSourceButton, *gotoRowSourceButton, *clearBoundColumnButton,
+ *clearVisibleColumnButton;
+ //! Used only in assignPropertySet() to check whether we already have the set assigned
+ int currentFieldUid;
+
+ bool insideClearRowSourceSelection : 1;
+ //! True is changeProperty() works. Used to block updating properties when within assignPropertySet().
+ bool propertySetEnabled : 1;
+
+ private:
+ //! A property set that is displayed on the page.
+ //! The set is also updated after any change in this page's data.
+ QGuardedPtr<KoProperty::Set> propertySet;
+};
+
+//----------------------------------------------
+
+KexiLookupColumnPage::KexiLookupColumnPage(QWidget *parent)
+ : QWidget(parent)
+ , d(new Private())
+{
+ setName("KexiLookupColumnPage");
+
+ QVBoxLayout *vlyr = new QVBoxLayout(this);
+ d->objectInfoLabel = new KexiObjectInfoLabel(this, "KexiObjectInfoLabel");
+ vlyr->addWidget(d->objectInfoLabel);
+
+//todo d->noDataSourceAvailableSingleText = i18n("No data source could be assigned for this widget.");
+//todo d->noDataSourceAvailableMultiText = i18n("No data source could be assigned for multiple widgets.");
+
+ //-Row Source
+ QWidget *contents = new QWidget(this);
+ vlyr->addWidget(contents);
+ QVBoxLayout *contentsVlyr = new QVBoxLayout(contents);
+
+ QHBoxLayout *hlyr = new QHBoxLayout(contentsVlyr);
+ d->rowSourceLabel = new QLabel(i18n("Row source:"), contents);
+ d->rowSourceLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->rowSourceLabel->setMargin(2);
+ d->rowSourceLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ d->rowSourceLabel->setAlignment(Qt::AlignLeft|Qt::AlignBottom);
+ hlyr->addWidget(d->rowSourceLabel);
+
+ d->gotoRowSourceButton = new KexiSmallToolButton(contents, QString::null, "goto", "gotoRowSourceButton");
+ d->gotoRowSourceButton->setMinimumHeight(d->rowSourceLabel->minimumHeight());
+ QToolTip::add(d->gotoRowSourceButton, i18n("Go to selected row source"));
+ hlyr->addWidget(d->gotoRowSourceButton);
+ connect(d->gotoRowSourceButton, SIGNAL(clicked()), this, SLOT(slotGotoSelectedRowSource()));
+
+ d->clearRowSourceButton = new KexiSmallToolButton(contents, QString::null,
+ "clear_left", "clearRowSourceButton");
+ d->clearRowSourceButton->setMinimumHeight(d->rowSourceLabel->minimumHeight());
+ QToolTip::add(d->clearRowSourceButton, i18n("Clear row source"));
+ hlyr->addWidget(d->clearRowSourceButton);
+ connect(d->clearRowSourceButton, SIGNAL(clicked()), this, SLOT(clearRowSourceSelection()));
+
+ d->rowSourceCombo = new KexiDataSourceComboBox(contents, "rowSourceCombo");
+ d->rowSourceLabel->setBuddy(d->rowSourceCombo);
+ contentsVlyr->addWidget(d->rowSourceCombo);
+
+ contentsVlyr->addSpacing(8);
+
+ //- Bound Column
+ hlyr = new QHBoxLayout(contentsVlyr);
+ d->boundColumnLabel = new QLabel(i18n("Bound column:"), contents);
+ d->boundColumnLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->boundColumnLabel->setMargin(2);
+ d->boundColumnLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ d->boundColumnLabel->setAlignment(Qt::AlignLeft|Qt::AlignBottom);
+ hlyr->addWidget(d->boundColumnLabel);
+
+ d->clearBoundColumnButton = new KexiSmallToolButton(contents, QString::null,
+ "clear_left", "clearBoundColumnButton");
+ d->clearBoundColumnButton->setMinimumHeight(d->boundColumnLabel->minimumHeight());
+ QToolTip::add(d->clearBoundColumnButton, i18n("Clear bound column"));
+ hlyr->addWidget(d->clearBoundColumnButton);
+ connect(d->clearBoundColumnButton, SIGNAL(clicked()), this, SLOT(clearBoundColumnSelection()));
+
+ d->boundColumnCombo = new KexiFieldComboBox(contents, "boundColumnCombo");
+ d->boundColumnLabel->setBuddy(d->boundColumnCombo);
+ contentsVlyr->addWidget(d->boundColumnCombo);
+
+ contentsVlyr->addSpacing(8);
+
+ //- Visible Column
+ hlyr = new QHBoxLayout(contentsVlyr);
+ d->visibleColumnLabel = new QLabel(i18n("Visible column:"), contents);
+ d->visibleColumnLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+ d->visibleColumnLabel->setMargin(2);
+ d->visibleColumnLabel->setMinimumHeight(IconSize(KIcon::Small)+4);
+ d->visibleColumnLabel->setAlignment(Qt::AlignLeft|Qt::AlignBottom);
+ hlyr->addWidget(d->visibleColumnLabel);
+
+ d->clearVisibleColumnButton = new KexiSmallToolButton(contents, QString::null,
+ "clear_left", "clearVisibleColumnButton");
+ d->clearVisibleColumnButton->setMinimumHeight(d->visibleColumnLabel->minimumHeight());
+ QToolTip::add(d->clearVisibleColumnButton, i18n("Clear visible column"));
+ hlyr->addWidget(d->clearVisibleColumnButton);
+ connect(d->clearVisibleColumnButton, SIGNAL(clicked()), this, SLOT(clearVisibleColumnSelection()));
+
+ d->visibleColumnCombo = new KexiFieldComboBox(contents, "visibleColumnCombo");
+ d->visibleColumnLabel->setBuddy(d->visibleColumnCombo);
+ contentsVlyr->addWidget(d->visibleColumnCombo);
+
+ vlyr->addStretch(1);
+
+ connect(d->rowSourceCombo, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotRowSourceTextChanged(const QString &)));
+ connect(d->rowSourceCombo, SIGNAL(dataSourceChanged()), this, SLOT(slotRowSourceChanged()));
+ connect(d->boundColumnCombo, SIGNAL(selected()), this, SLOT(slotBoundColumnSelected()));
+ connect(d->visibleColumnCombo, SIGNAL(selected()), this, SLOT(slotVisibleColumnSelected()));
+
+ clearBoundColumnSelection();
+ clearVisibleColumnSelection();
+}
+
+KexiLookupColumnPage::~KexiLookupColumnPage()
+{
+ delete d;
+}
+
+void KexiLookupColumnPage::setProject(KexiProject *prj)
+{
+ d->rowSourceCombo->setProject(prj,
+ true/*showTables*/, true/*showQueries*/
+ );
+ d->boundColumnCombo->setProject(prj);
+ d->visibleColumnCombo->setProject(prj);
+}
+
+void KexiLookupColumnPage::assignPropertySet(KoProperty::Set* propertySet)
+{
+ if (!d->hasPropertySet() && !propertySet)
+ return;
+ if (propertySet && d->currentFieldUid == (*propertySet)["uid"].value().toInt())
+ return; //already assigned
+
+ d->propertySetEnabled = false;
+ d->setPropertySet( propertySet );
+ d->updateInfoLabelForPropertySet( i18n("No field selected") );
+
+ const bool hasRowSource = d->hasPropertySet() && !d->propertyValue("rowSourceType").isNull()
+ && !d->propertyValue("rowSource").isNull();
+
+ QString rowSource, rowSourceType;
+ if (hasRowSource) {
+ rowSourceType = typeToMimeType( d->propertyValue("rowSourceType").toString() );
+ rowSource = d->propertyValue("rowSource").toString();
+ }
+ d->rowSourceCombo->setDataSource( rowSourceType, rowSource );
+ d->rowSourceLabel->setEnabled( d->hasPropertySet() );
+ d->rowSourceCombo->setEnabled( d->hasPropertySet() );
+ if (!d->hasPropertySet())
+ d->clearRowSourceButton->setEnabled( false );
+
+ int boundColumn = -1, visibleColumn = -1;
+ if (d->rowSourceCombo->isSelectionValid()) {
+ boundColumn = d->propertyValue("boundColumn").toInt();
+ visibleColumn = d->propertyValue("visibleColumn").toInt();
+ }
+ d->boundColumnCombo->setFieldOrExpression(boundColumn);
+ d->visibleColumnCombo->setFieldOrExpression(visibleColumn);
+ updateBoundColumnWidgetsAvailability();
+ d->propertySetEnabled = true;
+}
+
+void KexiLookupColumnPage::clearBoundColumnSelection()
+{
+ d->boundColumnCombo->setCurrentText("");
+ d->boundColumnCombo->setFieldOrExpression(QString::null);
+ slotBoundColumnSelected();
+ d->clearBoundColumnButton->setEnabled(false);
+}
+
+void KexiLookupColumnPage::slotBoundColumnSelected()
+{
+// KexiDB::Field::Type dataType = KexiDB::Field::InvalidType;
+//! @todo this should also work for expressions
+/*disabled KexiDB::Field *field = d->fieldListView->schema()->field( d->boundColumnCombo->fieldOrExpression() );
+ if (field)
+ dataType = field->type();
+*/
+ d->clearBoundColumnButton->setEnabled( !d->boundColumnCombo->fieldOrExpression().isEmpty() );
+ if (!d->boundColumnCombo->fieldOrExpression().isEmpty()) {
+ kdDebug() << endl;
+ }
+
+ // update property set
+ if (d->hasPropertySet()) {
+ d->changeProperty("boundColumn", d->boundColumnCombo->indexOfField());
+ }
+/*
+ emit boundColumnChanged(
+ d->boundColumnCombo->fieldOrExpression(),
+ d->boundColumnCombo->fieldOrExpressionCaption(),
+ dataType
+ );*/
+}
+
+void KexiLookupColumnPage::clearVisibleColumnSelection()
+{
+ d->visibleColumnCombo->setCurrentText("");
+ d->visibleColumnCombo->setFieldOrExpression(QString::null);
+ slotVisibleColumnSelected();
+ d->clearVisibleColumnButton->setEnabled(false);
+}
+
+void KexiLookupColumnPage::slotVisibleColumnSelected()
+{
+// KexiDB::Field::Type dataType = KexiDB::Field::InvalidType;
+//! @todo this should also work for expressions
+ d->clearVisibleColumnButton->setEnabled( !d->visibleColumnCombo->fieldOrExpression().isEmpty() );
+
+ // update property set
+ if (d->hasPropertySet()) {
+//! @todo support expression in special "visibleExpression"
+ d->changeProperty("visibleColumn", d->visibleColumnCombo->indexOfField());
+ }
+}
+
+void KexiLookupColumnPage::slotRowSourceChanged()
+{
+ if (!d->rowSourceCombo->project())
+ return;
+ QString mime = d->rowSourceCombo->selectedMimeType();
+ bool rowSourceFound = false;
+ QString name = d->rowSourceCombo->selectedName();
+ if ((mime=="kexi/table" || mime=="kexi/query") && d->rowSourceCombo->isSelectionValid()) {
+ KexiDB::TableOrQuerySchema *tableOrQuery = new KexiDB::TableOrQuerySchema(
+ d->rowSourceCombo->project()->dbConnection(), name.latin1(), mime=="kexi/table");
+ if (tableOrQuery->table() || tableOrQuery->query()) {
+//disabled d->fieldListView->setSchema( tableOrQuery );
+/*tmp*/ delete tableOrQuery;
+ rowSourceFound = true;
+ d->boundColumnCombo->setTableOrQuery(name, mime=="kexi/table");
+ d->visibleColumnCombo->setTableOrQuery(name, mime=="kexi/table");
+ }
+ else {
+ delete tableOrQuery;
+ }
+ }
+ if (!rowSourceFound) {
+ d->boundColumnCombo->setTableOrQuery("", true);
+ d->visibleColumnCombo->setTableOrQuery("", true);
+ }
+ clearBoundColumnSelection();
+ clearVisibleColumnSelection();
+ d->clearRowSourceButton->setEnabled(rowSourceFound);
+ d->gotoRowSourceButton->setEnabled(rowSourceFound);
+/* disabled
+ if (dataSourceFound) {
+ slotFieldListViewSelectionChanged();
+ } else {
+ d->addField->setEnabled(false);
+ }*/
+ updateBoundColumnWidgetsAvailability();
+
+ //update property set
+ if (d->hasPropertySet()) {
+ d->changeProperty("rowSourceType", mimeTypeToType(mime));
+ d->changeProperty("rowSource", name);
+ }
+
+//disabled emit formDataSourceChanged(mime, name);
+//! @todo update d->propertySet ^^
+}
+
+void KexiLookupColumnPage::slotRowSourceTextChanged(const QString & string)
+{
+ Q_UNUSED(string);
+ const bool enable = d->rowSourceCombo->isSelectionValid();
+ if (enable) {
+ updateBoundColumnWidgetsAvailability();
+ }
+ else {
+ clearRowSourceSelection( d->rowSourceCombo->selectedName().isEmpty()/*alsoClearComboBox*/ );
+ }
+}
+
+void KexiLookupColumnPage::clearRowSourceSelection(bool alsoClearComboBox)
+{
+ if (d->insideClearRowSourceSelection)
+ return;
+ d->insideClearRowSourceSelection = true;
+ if (alsoClearComboBox && !d->rowSourceCombo->selectedName().isEmpty())
+ d->rowSourceCombo->setDataSource("", "");
+ d->clearRowSourceButton->setEnabled(false);
+ d->gotoRowSourceButton->setEnabled(false);
+ d->insideClearRowSourceSelection = false;
+}
+
+void KexiLookupColumnPage::slotGotoSelectedRowSource()
+{
+ QString mime = d->rowSourceCombo->selectedMimeType();
+ if (mime=="kexi/table" || mime=="kexi/query") {
+ if (d->rowSourceCombo->isSelectionValid())
+ emit jumpToObjectRequested(mime.latin1(), d->rowSourceCombo->selectedName().latin1());
+ }
+}
+
+void KexiLookupColumnPage::updateBoundColumnWidgetsAvailability()
+{
+ const bool hasRowSource = d->rowSourceCombo->isSelectionValid();
+ d->boundColumnCombo->setEnabled( hasRowSource );
+ d->boundColumnLabel->setEnabled( hasRowSource );
+ d->clearBoundColumnButton->setEnabled( hasRowSource && !d->boundColumnCombo->fieldOrExpression().isEmpty() );
+ d->visibleColumnCombo->setEnabled( hasRowSource );
+ d->visibleColumnLabel->setEnabled( hasRowSource );
+ d->clearVisibleColumnButton->setEnabled( hasRowSource && !d->visibleColumnCombo->fieldOrExpression().isEmpty() );
+}
+
+#include "kexilookupcolumnpage.moc"
diff --git a/kexi/plugins/tables/kexilookupcolumnpage.h b/kexi/plugins/tables/kexilookupcolumnpage.h
new file mode 100644
index 00000000..457b2e3d
--- /dev/null
+++ b/kexi/plugins/tables/kexilookupcolumnpage.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <[email protected]>
+
+ 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 KEXILOOKUPCOLUMNPAGE_H
+#define KEXILOOKUPCOLUMNPAGE_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 KexiSmallToolButton;
+class QToolButton;
+class QLabel;
+class QFrame;
+
+//! @short A page within table designer's property pane, providing lookup column editor.
+/*! It's data model is basically KexiDB::LookupFieldSchema class, but the page does
+ not create it directly but instead updates a property set that defines
+ the field currently selected in the designer.
+
+ @todo not all features of KexiDB::LookupFieldSchema class are displayed on this page yet
+ */
+class KexiLookupColumnPage : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KexiLookupColumnPage(QWidget *parent);
+ virtual ~KexiLookupColumnPage();
+
+ public slots:
+ void setProject(KexiProject *prj);
+ void clearRowSourceSelection(bool alsoClearComboBox = true);
+ void clearBoundColumnSelection();
+ void clearVisibleColumnSelection();
+
+ //! 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 row sourcesource' is clicked.
+ void jumpToObjectRequested(const QCString& mime, const QCString& name);
+
+// /*! Signal emitted when current bound column has been changed. */
+// void boundColumnChanged(const QString& string, const QString& caption,
+ // KexiDB::Field::Type type);
+
+ protected slots:
+ void slotRowSourceTextChanged(const QString & string);
+ void slotRowSourceChanged();
+ void slotGotoSelectedRowSource();
+ void slotBoundColumnSelected();
+ void slotVisibleColumnSelected();
+
+ protected:
+ void updateBoundColumnWidgetsAvailability();
+
+ //! Used instead of m_propertySet->changeProperty() to honor m_propertySetEnabled
+ void changeProperty(const QCString &property, const QVariant &value);
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesigner_dataview.cpp b/kexi/plugins/tables/kexitabledesigner_dataview.cpp
new file mode 100644
index 00000000..bea2d9f5
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesigner_dataview.cpp
@@ -0,0 +1,79 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Jaroslaw Staniek <[email protected]>
+
+ 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 "kexitabledesigner_dataview.h"
+
+#include <kexidb/connection.h>
+#include <kexidb/cursor.h>
+#include <kexiutils/utils.h>
+#include "kexitableview.h"
+#include "kexidatatableview.h"
+#include "keximainwindow.h"
+
+KexiTableDesigner_DataView::KexiTableDesigner_DataView(KexiMainWindow *win, QWidget *parent)
+ : KexiDataTable(win, parent, "KexiTableDesigner_DataView", true/*db-aware*/)
+{
+}
+
+KexiTableDesigner_DataView::~KexiTableDesigner_DataView()
+{
+ if (dynamic_cast<KexiDataTableView*>(tableView())
+ && dynamic_cast<KexiDataTableView*>(tableView())->cursor())
+ {
+ mainWin()->project()->dbConnection()->deleteCursor(
+ dynamic_cast<KexiDataTableView*>(tableView())->cursor() );
+ }
+}
+
+tristate KexiTableDesigner_DataView::beforeSwitchTo(int mode, bool &dontStore)
+{
+ Q_UNUSED( dontStore );
+
+ if (mode != Kexi::DataViewMode) {
+ //accept editing before switching
+// if (!m_view->acceptRowEdit()) {
+ if (!acceptRowEdit()) {
+ return cancelled;
+ }
+ }
+
+ return true;
+}
+
+tristate KexiTableDesigner_DataView::afterSwitchFrom(int mode)
+{
+ Q_UNUSED( mode );
+
+ if (tempData()->tableSchemaChangedInPreviousView) {
+ KexiUtils::WaitCursor wait;
+ KexiDB::Cursor *c = mainWin()->project()->dbConnection()->prepareQuery(*tempData()->table);
+ if (!c)
+ return false;
+ setData(c);
+ tempData()->tableSchemaChangedInPreviousView = false;
+ }
+ return true;
+}
+
+KexiTablePart::TempData* KexiTableDesigner_DataView::tempData() const
+{
+ return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
+}
+
+#include "kexitabledesigner_dataview.moc"
diff --git a/kexi/plugins/tables/kexitabledesigner_dataview.h b/kexi/plugins/tables/kexitabledesigner_dataview.h
new file mode 100644
index 00000000..59e84ab1
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesigner_dataview.h
@@ -0,0 +1,49 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Jaroslaw Staniek <[email protected]>
+
+ 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 KEXITABLEDESIGNERDATAVIEW_H
+#define KEXITABLEDESIGNERDATAVIEW_H
+
+#include <kexidatatable.h>
+#include "kexitablepart.h"
+
+class KexiTableDesigner_DataView : public KexiDataTable
+{
+ Q_OBJECT
+
+ public:
+ KexiTableDesigner_DataView(KexiMainWindow *win, QWidget *parent);
+
+ virtual ~KexiTableDesigner_DataView();
+
+ KexiTablePart::TempData* tempData() const;
+
+ protected:
+// //! called just once from ctor
+// void init();
+// void initActions();
+// //! called whenever data should be reloaded (on switching to this view mode)
+// void initData();
+
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesignercommands.cpp b/kexi/plugins/tables/kexitabledesignercommands.cpp
new file mode 100644
index 00000000..ccbb181a
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignercommands.cpp
@@ -0,0 +1,281 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <[email protected]>
+
+ 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 <qdom.h>
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qsplitter.h>
+#include <qmetaobject.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <kaccelmanager.h>
+
+#include <koproperty/property.h>
+
+#include "kexitabledesignercommands.h"
+
+using namespace KexiTableDesignerCommands;
+
+
+Command::Command(KexiTableDesignerView* view)
+ : KCommand()
+ , m_view(view)
+{
+}
+
+Command::~Command()
+{
+}
+
+//--------------------------------------------------------
+
+ChangeFieldPropertyCommand::ChangeFieldPropertyCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName, const QVariant& oldValue, const QVariant& newValue,
+ KoProperty::Property::ListData* const oldListData, KoProperty::Property::ListData* const newListData)
+ : Command(view)
+ , m_alterTableAction(
+ propertyName=="name" ? oldValue.toString() : set.property("name").value().toString(),
+ propertyName, newValue, set["uid"].value().toInt())
+ , m_oldValue(oldValue)
+// , m_fieldUID(set["uid"].value().toInt())
+ , m_oldListData( oldListData ? new KoProperty::Property::ListData(*oldListData) : 0 )
+ , m_listData( newListData ? new KoProperty::Property::ListData(*newListData) : 0 )
+{
+ kexipluginsdbg << "ChangeFieldPropertyCommand: " << debugString() << endl;
+}
+
+ChangeFieldPropertyCommand::~ChangeFieldPropertyCommand()
+{
+ delete m_oldListData;
+ delete m_listData;
+}
+
+QString ChangeFieldPropertyCommand::name() const
+{
+ return i18n("Change \"%1\" property for table field from \"%2\" to \"%3\"")
+ .arg(m_alterTableAction.propertyName()).arg(m_oldValue.toString())
+ .arg(m_alterTableAction.newValue().toString());
+}
+
+QString ChangeFieldPropertyCommand::debugString()
+{
+ QString s( name() );
+ if (m_oldListData || m_listData)
+ s += QString("\nAnd list data from [%1]\n to [%2]")
+ .arg( m_oldListData ?
+ QString("%1 -> %2")
+ .arg(m_oldListData->keysAsStringList().join(",")).arg(m_oldListData->names.join(","))
+ : QString("<NONE>"))
+ .arg( m_listData ?
+ QString("%1 -> %2")
+ .arg(m_listData->keysAsStringList().join(",")).arg(m_listData->names.join(","))
+ : QString("<NONE>"));
+ return s + QString(" (UID=%1)").arg(m_alterTableAction.uid());
+}
+
+void ChangeFieldPropertyCommand::execute()
+{
+ m_view->changeFieldProperty(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_alterTableAction.newValue(), m_listData );
+}
+
+void ChangeFieldPropertyCommand::unexecute()
+{
+ m_view->changeFieldProperty(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_oldValue, m_oldListData );
+}
+
+KexiDB::AlterTableHandler::ActionBase* ChangeFieldPropertyCommand::createAction()
+{
+ if (m_alterTableAction.propertyName()=="subType") {//skip these properties
+ return 0;
+ }
+ return new KexiDB::AlterTableHandler::ChangeFieldPropertyAction( m_alterTableAction );
+}
+
+//--------------------------------------------------------
+
+RemoveFieldCommand::RemoveFieldCommand( KexiTableDesignerView* view, int fieldIndex,
+ const KoProperty::Set* set)
+ : Command(view)
+ , m_alterTableAction( set ? (*set)["name"].value().toString() : QString::null,
+ set ? (*set)["uid"].value().toInt() : -1 )
+ , m_set( set ? new KoProperty::Set(*set /*deep copy*/) : 0 )
+ , m_fieldIndex(fieldIndex)
+{
+}
+
+RemoveFieldCommand::~RemoveFieldCommand()
+{
+ delete m_set;
+}
+
+QString RemoveFieldCommand::name() const
+{
+ if (m_set)
+ return i18n("Remove table field \"%1\"").arg(m_alterTableAction.fieldName());
+
+ return QString("Remove empty row at position %1").arg(m_fieldIndex);
+}
+
+void RemoveFieldCommand::execute()
+{
+// m_view->deleteField( m_fieldIndex );
+ m_view->deleteRow( m_fieldIndex );
+}
+
+void RemoveFieldCommand::unexecute()
+{
+ m_view->insertEmptyRow(m_fieldIndex);
+ if (m_set)
+ m_view->insertField( m_fieldIndex, *m_set );
+}
+
+QString RemoveFieldCommand::debugString()
+{
+ if (!m_set)
+ return name();
+
+ return name() + "\nAT ROW " + QString::number(m_fieldIndex)
+ + ", FIELD: " + (*m_set)["caption"].value().toString()
+ + QString(" (UID=%1)").arg(m_alterTableAction.uid());
+}
+
+KexiDB::AlterTableHandler::ActionBase* RemoveFieldCommand::createAction()
+{
+ return new KexiDB::AlterTableHandler::RemoveFieldAction( m_alterTableAction );
+}
+
+//--------------------------------------------------------
+
+InsertFieldCommand::InsertFieldCommand( KexiTableDesignerView* view,
+ int fieldIndex/*, const KexiDB::Field& field*/, const KoProperty::Set& set )
+ : Command(view)
+ , m_alterTableAction(0) //fieldIndex, new KexiDB::Field(field) /*deep copy*/)
+ , m_set( set ) //? new KoProperty::Set(*set) : 0 )
+{
+ KexiDB::Field *f = view->buildField( m_set );
+ if (f)
+ m_alterTableAction = new KexiDB::AlterTableHandler::InsertFieldAction(
+ fieldIndex, f, set["uid"].value().toInt());
+ else //null action
+ m_alterTableAction = new KexiDB::AlterTableHandler::InsertFieldAction(true);
+}
+
+InsertFieldCommand::~InsertFieldCommand()
+{
+ delete m_alterTableAction;
+}
+
+QString InsertFieldCommand::name() const
+{
+ return i18n("Insert table field \"%1\"").arg(m_set["caption"].value().toString());
+}
+
+void InsertFieldCommand::execute()
+{
+ m_view->insertField( m_alterTableAction->index(), /*m_alterTableAction.field(),*/ m_set );
+}
+
+void InsertFieldCommand::unexecute()
+{
+ m_view->clearRow( m_alterTableAction->index() );//m_alterTableAction.index() );
+}
+
+KexiDB::AlterTableHandler::ActionBase* InsertFieldCommand::createAction()
+{
+ return new KexiDB::AlterTableHandler::InsertFieldAction(*m_alterTableAction);
+}
+
+//--------------------------------------------------------
+
+ChangePropertyVisibilityCommand::ChangePropertyVisibilityCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName, bool visible)
+ : Command(view)
+ , m_alterTableAction(set.property("name").value().toString(), propertyName, visible, set["uid"].value().toInt())
+// , m_fieldUID(set["uid"].value().toInt())
+ , m_oldVisibility( set.property(propertyName).isVisible() )
+{
+ kexipluginsdbg << "ChangePropertyVisibilityCommand: " << debugString() << endl;
+}
+
+ChangePropertyVisibilityCommand::~ChangePropertyVisibilityCommand()
+{
+}
+
+QString ChangePropertyVisibilityCommand::name() const
+{
+ return QString("[internal] Change \"%1\" visibility from \"%2\" to \"%3\"")
+ .arg(m_alterTableAction.propertyName())
+ .arg(m_oldVisibility ? "true" : "false")
+ .arg(m_alterTableAction.newValue().toBool() ? "true" : "false");
+}
+
+void ChangePropertyVisibilityCommand::execute()
+{
+ m_view->changePropertyVisibility(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_alterTableAction.newValue().toBool() );
+}
+
+void ChangePropertyVisibilityCommand::unexecute()
+{
+ m_view->changePropertyVisibility(
+ m_alterTableAction.uid(),
+ m_alterTableAction.propertyName().latin1(),
+ m_oldVisibility );
+}
+
+//--------------------------------------------------------
+
+InsertEmptyRowCommand::InsertEmptyRowCommand( KexiTableDesignerView* view, int row )
+ : Command(view)
+ , m_alterTableAction(true) //unused, null action
+ , m_row(row)
+{
+}
+
+InsertEmptyRowCommand::~InsertEmptyRowCommand()
+{
+}
+
+QString InsertEmptyRowCommand::name() const
+{
+ return QString("Insert empty row at position %1").arg(m_row);
+}
+
+void InsertEmptyRowCommand::execute()
+{
+ m_view->insertEmptyRow( m_row );
+}
+
+void InsertEmptyRowCommand::unexecute()
+{
+ // let's assume the row is empty...
+ m_view->deleteRow( m_row );
+}
+
diff --git a/kexi/plugins/tables/kexitabledesignercommands.h b/kexi/plugins/tables/kexitabledesignercommands.h
new file mode 100644
index 00000000..355aabe2
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignercommands.h
@@ -0,0 +1,188 @@
+/* This file is part of the KDE project
+ Copyright (C) 2006 Jaroslaw Staniek <[email protected]>
+
+ 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 KEXITABLEDESIGNER_COMMANDS_H
+#define KEXITABLEDESIGNER_COMMANDS_H
+
+#include <qmap.h>
+#include <qdict.h>
+#include <qptrlist.h>
+#include <qptrdict.h>
+#include <qvariant.h>
+#include <qguardedptr.h>
+
+#include <kcommand.h>
+#include <kexidb/alter.h>
+#include <koproperty/set.h>
+
+#include "kexitabledesignerview.h"
+
+class QWidget;
+class QRect;
+class QPoint;
+class QStringList;
+class QCString;
+
+namespace KexiTableDesignerCommands {
+
+//! @short Base class for all Table Designer's commands
+class Command : public KCommand
+{
+ public:
+ Command(KexiTableDesignerView* view);
+ virtual ~Command();
+
+ //! Used to collect actions data for AlterTableHandler
+ //! Can return 0 if the action should not be passed to AlterTableHandler
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction() { return 0; }
+
+ virtual QString debugString() { return name(); }
+
+ protected:
+ QGuardedPtr<KexiTableDesignerView> m_view;
+};
+
+//! @short Undo/redo command used for when changing a property for a table field
+class ChangeFieldPropertyCommand : public Command
+{
+ public:
+ /*! Creates the ChangeFieldPropertyCommand object.
+ Note: we use internal "uid" property of a field (set["uid"]) to avoid problems with looking
+ for field by name when more than one field exists with the same name
+ (it's invalid but allowed in design time).
+ \a oldlistData and and \a newListData can be specified so Property::setListData() will be called
+ on execute() and unexecute().
+ */
+ ChangeFieldPropertyCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& oldValue, const QVariant& newValue,
+ KoProperty::Property::ListData* const oldListData = 0, KoProperty::Property::ListData* const newListData = 0);
+
+ virtual ~ChangeFieldPropertyCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction();
+ virtual QString debugString();
+
+ protected:
+ KexiDB::AlterTableHandler::ChangeFieldPropertyAction m_alterTableAction;
+ QVariant m_oldValue;
+// int m_fieldUID;
+ KoProperty::Property::ListData* m_oldListData, *m_listData;
+};
+
+//! @short Undo/redo command used when a field is removed from a table
+class RemoveFieldCommand : public Command
+{
+ public:
+ /*! Constructs RemoveFieldCommand object.
+ If \a set is 0, the action only means removing empty row (internal). */
+ RemoveFieldCommand( KexiTableDesignerView* view, int fieldIndex,
+ const KoProperty::Set* set);
+
+ virtual ~RemoveFieldCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction();
+
+ virtual QString debugString();
+
+ protected:
+ KexiDB::AlterTableHandler::RemoveFieldAction m_alterTableAction;
+ KoProperty::Set* m_set;
+ int m_fieldIndex;
+};
+
+//! @short Undo/redo command used when a new field is inserted into a table
+class InsertFieldCommand : public Command
+{
+ public:
+ InsertFieldCommand( KexiTableDesignerView* view,
+ int fieldIndex/*, const KexiDB::Field& field*/, const KoProperty::Set& set );
+ virtual ~InsertFieldCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+ virtual KexiDB::AlterTableHandler::ActionBase* createAction();
+
+ virtual QString debugString() {
+ return name() + "\nAT ROW " + QString::number(m_alterTableAction->index()) //m_alterTableAction.index())
+ + ", FIELD: " + m_set["caption"].value().toString(); //m_alterTableAction.field().debugString();
+ }
+
+ protected:
+ KexiDB::AlterTableHandler::InsertFieldAction *m_alterTableAction;
+ KoProperty::Set m_set;
+};
+
+
+/* ---- Internal commands follow (not used for building performing ALTER TABLE ---- */
+
+//! @short Undo/redo command used when property visibility is changed
+/*! Internal, only used in addition to property change. */
+class ChangePropertyVisibilityCommand : public Command
+{
+ public:
+ /*! Creates the ChangePropertyVisibilityCommand object.
+ Note: we use internal "uid" property of a field (set["uid"]) to avoid problems with looking
+ for field by name when more than one field exists with the same name
+ (it's invalid but allowed in design time).
+ */
+ ChangePropertyVisibilityCommand( KexiTableDesignerView* view,
+ const KoProperty::Set& set, const QCString& propertyName,
+ bool visible);
+
+ virtual ~ChangePropertyVisibilityCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+
+ protected:
+ KexiDB::AlterTableHandler::ChangeFieldPropertyAction m_alterTableAction;
+// int m_fieldUID;
+ bool m_oldVisibility;
+};
+
+//! @short Undo/redo command used when property visibility is changed
+/*! Internal, only used in addition to property change. */
+class InsertEmptyRowCommand : public Command
+{
+ public:
+ /*! Creates the InsertEmptyRowCommand object. */
+ InsertEmptyRowCommand( KexiTableDesignerView* view, int row );
+ virtual ~InsertEmptyRowCommand();
+
+ virtual QString name() const;
+ virtual void execute();
+ virtual void unexecute();
+
+ protected:
+ KexiDB::AlterTableHandler::ChangeFieldPropertyAction m_alterTableAction;
+ int m_row;
+};
+
+}
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesignerview.cpp b/kexi/plugins/tables/kexitabledesignerview.cpp
new file mode 100644
index 00000000..7e3478ed
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview.cpp
@@ -0,0 +1,1943 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2007 Jaroslaw Staniek <[email protected]>
+
+ 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 "kexitabledesignerview.h"
+#include "kexitabledesignerview_p.h"
+#include "kexilookupcolumnpage.h"
+#include "kexitabledesignercommands.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qsplitter.h>
+
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <kiconeffect.h>
+
+#include <koproperty/set.h>
+#include <koproperty/utils.h>
+
+#include <kexidb/cursor.h>
+#include <kexidb/tableschema.h>
+#include <kexidb/connection.h>
+#include <kexidb/utils.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexidb/error.h>
+#include <kexidb/lookupfieldschema.h>
+#include <kexiutils/identifier.h>
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include <widget/tableview/kexidataawarepropertyset.h>
+#include <widget/kexicustompropertyfactory.h>
+#include <kexiutils/utils.h>
+#include <kexidialogbase.h>
+#include <kexitableview.h>
+
+//#define MAX_FIELDS 101 //nice prime number
+
+//! used only for BLOBs
+#define DEFAULT_OBJECT_TYPE_VALUE "image"
+
+//#define KexiTableDesignerView_DEBUG
+
+//! @todo remove this when BLOBs are implemented
+//#define KEXI_NO_BLOB_FIELDS
+
+using namespace KexiTableDesignerCommands;
+
+//! @internal Used in tryCastQVariant() anf canCastQVariant()
+static bool isIntegerQVariant(QVariant::Type t)
+{
+ return t==QVariant::LongLong
+ || t==QVariant::ULongLong
+ || t==QVariant::Int
+ || t==QVariant::UInt;
+}
+
+//! @internal Used in tryCastQVariant()
+static bool canCastQVariant(QVariant::Type fromType, QVariant::Type toType)
+{
+ return (fromType==QVariant::Int && toType==QVariant::UInt)
+ || (fromType==QVariant::CString && toType==QVariant::String)
+ || (fromType==QVariant::LongLong && toType==QVariant::ULongLong)
+ || ((fromType==QVariant::String || fromType==QVariant::CString)
+ && (isIntegerQVariant(toType) || toType==QVariant::Double));
+}
+
+/*! @internal
+ \return a variant value converted from \a fromVal to \a toType type.
+ Null QVariant is returned if \a fromVal's type and \a toType type
+ are incompatible. */
+static QVariant tryCastQVariant( const QVariant& fromVal, QVariant::Type toType )
+{
+ const QVariant::Type fromType = fromVal.type();
+ if (fromType == toType)
+ return fromVal;
+ if (canCastQVariant(fromType, toType) || canCastQVariant(toType, fromType)
+ || (isIntegerQVariant(fromType) && toType==QVariant::Double))
+ {
+ QVariant res( fromVal );
+ if (res.cast(toType))
+ return res;
+ }
+ return QVariant();
+}
+
+
+KexiTableDesignerView::KexiTableDesignerView(KexiMainWindow *win, QWidget *parent)
+ : KexiDataTable(win, parent, "KexiTableDesignerView", false/*not db-aware*/)
+ , KexiTableDesignerInterface()
+ , d( new KexiTableDesignerViewPrivate(this) )
+{
+ //needed for custom "identifier" property editor widget
+ KexiCustomPropertyFactory::init();
+
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ d->view = dynamic_cast<KexiTableView*>(mainWidget());
+
+ d->data = new KexiTableViewData();
+ if (conn->isReadOnly())
+ d->data->setReadOnly(true);
+ d->data->setInsertingEnabled( false );
+
+ KexiTableViewColumn *col = new KexiTableViewColumn("pk", KexiDB::Field::Text, QString::null,
+ i18n("Additional information about the field"));
+ col->setIcon( KexiUtils::colorizeIconToTextColor( SmallIcon("info"), d->view->palette() ) );
+ col->setHeaderTextVisible(false);
+ col->field()->setSubType("KIcon");
+ col->setReadOnly(true);
+ d->data->addColumn( col );
+
+// col = new KexiTableViewColumn("name", KexiDB::Field::Text, i18n("Field Name"),
+ col = new KexiTableViewColumn("caption", KexiDB::Field::Text, i18n("Field Caption"),
+ i18n("Describes caption for the field"));
+// KexiUtils::Validator *vd = new KexiUtils::IdentifierValidator();
+// vd->setAcceptsEmptyValue(true);
+// col->setValidator( vd );
+ d->data->addColumn( col );
+
+ col = new KexiTableViewColumn("type", KexiDB::Field::Enum, i18n("Data Type"),
+ i18n("Describes data type for the field"));
+ d->data->addColumn( col );
+
+#ifdef KEXI_NO_BLOB_FIELDS
+//! @todo remove this later
+ QValueVector<QString> types(KexiDB::Field::LastTypeGroup-1); //don't show last type (BLOB)
+#else
+ QValueVector<QString> types(KexiDB::Field::LastTypeGroup);
+#endif
+ d->maxTypeNameTextWidth = 0;
+ QFontMetrics fm(font());
+ for (uint i=1; i<=types.count(); i++) {
+ types[i-1] = KexiDB::Field::typeGroupName(i);
+ d->maxTypeNameTextWidth = QMAX(d->maxTypeNameTextWidth, fm.width(types[i-1]));
+ }
+ col->field()->setEnumHints(types);
+
+ d->data->addColumn( col = new KexiTableViewColumn("comments", KexiDB::Field::Text, i18n("Comments"),
+ i18n("Describes additional comments for the field")) );
+
+ d->view->setSpreadSheetMode();
+
+ connect(d->data, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
+ this, SLOT(slotBeforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
+ connect(d->data, SIGNAL(rowUpdated(KexiTableItem*)),
+ this, SLOT(slotRowUpdated(KexiTableItem*)));
+ //connect(d->data, SIGNAL(aboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)),
+ // this, SLOT(slotAboutToInsertRow(KexiTableItem*,KexiDB::ResultInfo*,bool)));
+ connect(d->data, SIGNAL(aboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)),
+ this, SLOT(slotAboutToDeleteRow(KexiTableItem&,KexiDB::ResultInfo*,bool)));
+
+ setMinimumSize(d->view->minimumSizeHint().width(), d->view->minimumSizeHint().height());
+ d->view->setFocus();
+
+ d->sets = new KexiDataAwarePropertySet( this, d->view );
+ connect(d->sets, SIGNAL(rowDeleted()), this, SLOT(updateActions()));
+ connect(d->sets, SIGNAL(rowInserted()), this, SLOT(slotRowInserted()));
+
+ d->contextMenuTitle = new KPopupTitle(d->view->contextMenu());
+ d->view->contextMenu()->insertItem(d->contextMenuTitle, -1, 0);
+ connect(d->view->contextMenu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowContextMenu()));
+
+ plugSharedAction("tablepart_toggle_pkey", this, SLOT(slotTogglePrimaryKey()));
+ d->action_toggle_pkey = static_cast<KToggleAction*>( sharedAction("tablepart_toggle_pkey") );
+ d->action_toggle_pkey->plug(d->view->contextMenu(), 1); //add at the beginning
+ d->view->contextMenu()->insertSeparator(2);
+ setAvailable("tablepart_toggle_pkey", !conn->isReadOnly());
+
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+ plugSharedAction("edit_undo", this, SLOT(slotUndo()));
+ plugSharedAction("edit_redo", this, SLOT(slotRedo()));
+ setAvailable("edit_undo", false);
+ setAvailable("edit_redo", false);
+ connect(d->history, SIGNAL(commandExecuted(KCommand*)), this, SLOT(slotCommandExecuted(KCommand*)));
+#endif
+
+#ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString::null); //to create the tab
+ KexiUtils::connectPushButtonActionForDebugWindow(
+ "simulateAlterTableExecution", this, SLOT(slotSimulateAlterTableExecution()));
+ KexiUtils::connectPushButtonActionForDebugWindow(
+ "executeRealAlterTable", this, SLOT(executeRealAlterTable()));
+#endif
+}
+
+KexiTableDesignerView::~KexiTableDesignerView()
+{
+// removeCurrentPropertySet();
+ delete d;
+}
+
+void KexiTableDesignerView::initData()
+{
+ //add column data
+// d->data->clear();
+ d->data->deleteAllRows();
+ int tableFieldCount = 0;
+ d->primaryKeyExists = false;
+
+ if (tempData()->table) {
+ tableFieldCount = tempData()->table->fieldCount();
+//not needed d->sets->clear(tableFieldCount);
+
+ //recreate table data rows
+ for(int i=0; i < tableFieldCount; i++) {
+ KexiDB::Field *field = tempData()->table->field(i);
+ KexiTableItem *item = d->data->createItem(); //new KexiTableItem(0);
+ if (field->isPrimaryKey()) {
+ (*item)[COLUMN_ID_ICON] = "key";
+ d->primaryKeyExists = true;
+ }
+ else {
+ KexiDB::LookupFieldSchema *lookupFieldSchema
+ = field->table() ? field->table()->lookupFieldSchema(*field) : 0;
+ if (lookupFieldSchema && lookupFieldSchema->rowSource().type()!=KexiDB::LookupFieldSchema::RowSource::NoType
+ && !lookupFieldSchema->rowSource().name().isEmpty())
+ {
+ (*item)[COLUMN_ID_ICON] = "combo";
+ }
+ }
+ (*item)[COLUMN_ID_CAPTION] = field->captionOrName();
+ (*item)[COLUMN_ID_TYPE] = field->typeGroup()-1; //-1 because type groups are counted from 1
+ (*item)[COLUMN_ID_DESC] = field->description();
+ d->data->append(item);
+
+//later! createPropertySet( i, field );
+ }
+ }
+// else {
+// d->sets->clear();//default size
+// }
+
+ //add empty space
+// const int columnsCount = d->data->columnsCount();
+ for (int i=tableFieldCount; i<(int)d->sets->size(); i++) {
+// KexiTableItem *item = new KexiTableItem(columnsCount);//3 empty fields
+ d->data->append(d->data->createItem());
+ }
+
+ //set data for our spreadsheet: this will clear our sets
+ d->view->setData(d->data);
+
+ //now recreate property sets
+ if (tempData()->table) {
+ for(int i=0; i < tableFieldCount; i++) {
+ KexiDB::Field *field = tempData()->table->field(i);
+ createPropertySet( i, *field );
+ }
+ }
+
+ //column widths
+ d->view->setColumnWidth(COLUMN_ID_ICON, IconSize( KIcon::Small ) + 10);
+ d->view->adjustColumnWidthToContents(COLUMN_ID_CAPTION); //adjust column width
+ d->view->setColumnWidth(COLUMN_ID_TYPE, d->maxTypeNameTextWidth + 2 * d->view->rowHeight());
+ d->view->setColumnStretchEnabled( true, COLUMN_ID_DESC ); //last column occupies the rest of the area
+ const int minCaptionColumnWidth = d->view->fontMetrics().width("wwwwwwwwwww");
+ if (minCaptionColumnWidth > d->view->columnWidth(COLUMN_ID_CAPTION))
+ d->view->setColumnWidth(COLUMN_ID_CAPTION, minCaptionColumnWidth);
+
+ setDirty(false);
+ d->view->setCursorPosition(0, COLUMN_ID_CAPTION); //set @ name column
+ propertySetSwitched();
+}
+
+//! Gets subtype strings and names for type \a fieldType
+void
+KexiTableDesignerView::getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup,
+ QStringList& stringsList, QStringList& namesList)
+{
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ if (fieldTypeGroup==KexiDB::Field::BLOBGroup) {
+ // special case: BLOB type uses "mime-based" subtypes
+//! @todo hardcoded!
+ stringsList << "image";
+ namesList << i18n("Image object type", "Image");
+ }
+ else {*/
+ stringsList = KexiDB::typeStringsForGroup(fieldTypeGroup);
+ namesList = KexiDB::typeNamesForGroup(fieldTypeGroup);
+// }
+ kexipluginsdbg << "KexiTableDesignerView::getSubTypeListData(): subType strings: " <<
+ stringsList.join("|") << "\nnames: " << namesList.join("|") << endl;
+}
+
+KoProperty::Set *
+KexiTableDesignerView::createPropertySet( int row, const KexiDB::Field& field, bool newOne )
+{
+ QString typeName = "KexiDB::Field::" + field.typeGroupString();
+ KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
+ if (mainWin()->project()->dbConnection()->isReadOnly())
+ set->setReadOnly( true );
+// connect(buff,SIGNAL(propertyChanged(KexiPropertyBuffer&,KexiProperty&)),
+// this, SLOT(slotPropertyChanged(KexiPropertyBuffer&,KexiProperty&)));
+
+ KoProperty::Property *prop;
+
+ set->addProperty(prop = new KoProperty::Property("uid", d->generateUniqueId(), ""));
+ prop->setVisible(false);
+
+ //meta-info for property editor
+ set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Table field")) );
+ prop->setVisible(false);
+ set->addProperty(prop = new KoProperty::Property("this:iconName",
+//! \todo add table_field icon
+ "lineedit" //"table_field"
+ ));
+ prop->setVisible(false);
+ set->addProperty(prop = new KoProperty::Property("this:useCaptionAsObjectName",
+ QVariant(true, 1), QString::null)); //we want "caption" to be displayed in the header, not name
+ prop->setVisible(false);
+
+ //name
+ set->addProperty(prop
+ = new KoProperty::Property("name", QVariant(field.name()), i18n("Name"),
+ QString::null, KexiCustomPropertyFactory::Identifier) );
+
+ //type
+ set->addProperty( prop
+ = new KoProperty::Property("type", QVariant(field.type()), i18n("Type")) );
+#ifndef KexiTableDesignerView_DEBUG
+ prop->setVisible(false);//always hidden
+#endif
+
+ //subtype
+ QStringList typeStringList, typeNameList;
+ getSubTypeListData(field.typeGroup(), typeStringList, typeNameList);
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ QString subTypeValue;
+ if (field.typeGroup()==KexiDB::Field::BLOBGroup) {
+// special case: BLOB type uses "mime-based" subtypes
+//! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
+ subTypeValue = slist.first();
+ }
+ else {*/
+ QString subTypeValue = field.typeString();
+ //}
+ set->addProperty(prop = new KoProperty::Property("subType",
+ typeStringList, typeNameList, subTypeValue, i18n("Subtype")));
+
+ // objectType
+ QStringList objectTypeStringList, objectTypeNameList;
+//! @todo this should be retrieved from KexiDB::Field when BLOB supports many different mimetypes
+ objectTypeStringList << "image";
+ objectTypeNameList << i18n("Image object type", "Image");
+ QString objectTypeValue( field.customProperty("objectType").toString() );
+ if (objectTypeValue.isEmpty())
+ objectTypeValue = DEFAULT_OBJECT_TYPE_VALUE;
+ set->addProperty(prop = new KoProperty::Property("objectType",
+ objectTypeStringList, objectTypeNameList, objectTypeValue, i18n("Subtype")/*todo other i18n string?*/));
+
+ set->addProperty( prop
+ = new KoProperty::Property("caption", QVariant(field.caption()), i18n("Caption") ) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty( prop
+ = new KoProperty::Property("description", QVariant(field.description())) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty(prop
+ = new KoProperty::Property("unsigned", QVariant(field.isUnsigned(), 4), i18n("Unsigned Number")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("length", (int)field.length()/*200?*/, i18n("Length")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("precision", (int)field.precision()/*200?*/, i18n("Precision")));
+#ifdef KEXI_NO_UNFINISHED
+ prop->setVisible(false);
+#endif
+ set->addProperty( prop
+ = new KoProperty::Property("visibleDecimalPlaces", field.visibleDecimalPlaces(), i18n("Visible Decimal Places")));
+ prop->setOption("min", -1);
+ prop->setOption("minValueText", i18n("Auto Decimal Places","Auto"));
+
+//! @todo set reasonable default for column width
+ set->addProperty( prop
+ = new KoProperty::Property("width", (int)field.width()/*200?*/, i18n("Column Width")));
+#ifdef KEXI_NO_UNFINISHED
+ prop->setVisible(false);
+#endif
+
+ set->addProperty( prop
+ = new KoProperty::Property("defaultValue", field.defaultValue(), i18n("Default Value"),
+ QString::null,
+//! @todo use "Variant" type here when supported by KoProperty
+ (KoProperty::PropertyType)field.variantType()) );
+ prop->setOption("3rdState", i18n("None"));
+// prop->setVisible(false);
+
+ set->addProperty( prop
+ = new KoProperty::Property("primaryKey", QVariant(field.isPrimaryKey(), 4), i18n("Primary Key")));
+ prop->setIcon("key");
+
+ set->addProperty( prop
+ = new KoProperty::Property("unique", QVariant(field.isUniqueKey(), 4), i18n("Unique")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("notNull", QVariant(field.isNotNull(), 4), i18n("Required")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("allowEmpty", QVariant(!field.isNotEmpty(), 4), i18n("Allow Zero\nSize")));
+
+ set->addProperty( prop
+ = new KoProperty::Property("autoIncrement", QVariant(field.isAutoIncrement(), 4), i18n("Autonumber")));
+ prop->setIcon("autonumber");
+
+ set->addProperty( prop
+ = new KoProperty::Property("indexed", QVariant(field.isIndexed(), 4), i18n("Indexed")));
+
+ //- properties related to lookup columns (used and set by the "lookup column" tab in the property pane)
+ KexiDB::LookupFieldSchema *lookupFieldSchema = field.table() ? field.table()->lookupFieldSchema(field) : 0;
+ set->addProperty( prop = new KoProperty::Property("rowSource",
+ lookupFieldSchema ? lookupFieldSchema->rowSource().name() : QString::null, i18n("Row Source")));
+ prop->setVisible(false);
+
+ set->addProperty( prop = new KoProperty::Property("rowSourceType",
+ lookupFieldSchema ? lookupFieldSchema->rowSource().typeName() : QString::null, i18n("Row Source\nType")));
+ prop->setVisible(false);
+
+ set->addProperty( prop
+ = new KoProperty::Property("boundColumn",
+ lookupFieldSchema ? lookupFieldSchema->boundColumn() : -1, i18n("Bound Column")));
+ prop->setVisible(false);
+
+//! @todo this is backward-compatible code for "single visible column" implementation
+//! for multiple columns, only the first is displayed, so there is a data loss is GUI is used
+//! -- special koproperty editor needed
+ int visibleColumn = -1;
+ if (lookupFieldSchema && !lookupFieldSchema->visibleColumns().isEmpty())
+ visibleColumn = lookupFieldSchema->visibleColumns().first();
+ set->addProperty( prop
+ = new KoProperty::Property("visibleColumn", visibleColumn, i18n("Visible Column")));
+ prop->setVisible(false);
+
+//! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()
+
+ //----
+ d->updatePropertiesVisibility(field.type(), *set);
+
+ connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
+ this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
+
+ d->sets->insert(row, set, newOne);
+ return set;
+}
+
+void KexiTableDesignerView::updateActions(bool activated)
+{
+ Q_UNUSED(activated);
+/*! \todo check if we can set pkey for this column type (eg. BLOB?) */
+ setAvailable("tablepart_toggle_pkey", propertySet()!=0 && !mainWin()->project()->dbConnection()->isReadOnly());
+ if (!propertySet())
+ return;
+ KoProperty::Set &set = *propertySet();
+ d->slotTogglePrimaryKeyCalled = true;
+ d->action_toggle_pkey->setChecked(set["primaryKey"].value().toBool());
+ d->slotTogglePrimaryKeyCalled = false;
+}
+
+void KexiTableDesignerView::slotUpdateRowActions(int row)
+{
+ KexiDataTable::slotUpdateRowActions(row);
+ updateActions();
+}
+
+void KexiTableDesignerView::slotTogglePrimaryKey()
+{
+ if (d->slotTogglePrimaryKeyCalled)
+ return;
+ d->slotTogglePrimaryKeyCalled = true;
+ if (!propertySet())
+ return;
+ KoProperty::Set &set = *propertySet();
+ bool isSet = !set["primaryKey"].value().toBool();
+ set.changeProperty("primaryKey", QVariant(isSet,1)); //this will update all related properties as well
+/* CommandGroup *setPrimaryKeyCommand;
+ if (isSet) {
+ setPrimaryKeyCommand = new CommandGroup(i18n("Set primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ }
+ else {
+ setPrimaryKeyCommand = new CommandGroup(i18n("Unset primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ }
+ switchPrimaryKey(set, isSet, false, setPrimaryKeyCommand);*/
+ //addHistoryCommand( setPrimaryKeyCommand, false /* !execute */ );
+ d->slotTogglePrimaryKeyCalled = false;
+}
+
+void KexiTableDesignerView::switchPrimaryKey(KoProperty::Set &propertySet,
+ bool set, bool aWasPKey, CommandGroup* commandGroup)
+{
+ const bool was_pkey = aWasPKey || propertySet["primaryKey"].value().toBool();
+// propertySet["primaryKey"] = QVariant(set, 1);
+ d->setPropertyValueIfNeeded( propertySet, "primaryKey", QVariant(set,1), commandGroup );
+ if (&propertySet==this->propertySet()) {
+ //update action and icon @ column 0 (only if we're changing current property set)
+ d->action_toggle_pkey->setChecked(set);
+ if (d->view->selectedItem()) {
+ //show key in the table
+ d->view->data()->clearRowEditBuffer();
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_ICON,
+ QVariant(set ? "key" : ""));
+ d->view->data()->saveRowChanges(*d->view->selectedItem(), true);
+ }
+ if (was_pkey || set) //change flag only if we're setting pk or really clearing it
+ d->primaryKeyExists = set;
+ }
+
+ if (set) {
+ //primary key is set, remove old pkey if exists
+ KoProperty::Set *s = 0;
+ int i;
+ const int count = (int)d->sets->size();
+ for (i=0; i<count; i++) {
+ s = d->sets->at(i);
+ if (s && s!=&propertySet && (*s)["primaryKey"].value().toBool() && i!=d->view->currentRow())
+ break;
+ }
+ if (i<count) {//remove
+ //(*s)["autoIncrement"] = QVariant(false, 0);
+ d->setPropertyValueIfNeeded( *s, "autoIncrement", QVariant(false,0), commandGroup );
+ //(*s)["primaryKey"] = QVariant(false, 0);
+ d->setPropertyValueIfNeeded( *s, "primaryKey", QVariant(false,0), commandGroup );
+ //remove key from table
+ d->view->data()->clearRowEditBuffer();
+ KexiTableItem *item = d->view->itemAt(i);
+ if (item) {
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
+ d->view->data()->saveRowChanges(*item, true);
+ }
+ }
+ //set unsigned big-integer type
+// d->view->data()->saveRowChanges(*d->view->selectedItem());
+ d->slotBeforeCellChanged_enabled = false;
+ d->view->data()->clearRowEditBuffer();
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
+ QVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
+// QVariant(KexiDB::Field::typeGroupName(KexiDB::Field::IntegerGroup)));
+ d->view->data()->saveRowChanges(*d->view->selectedItem(), true);
+ //propertySet["subType"] = KexiDB::Field::typeString(KexiDB::Field::BigInteger);
+ d->setPropertyValueIfNeeded( propertySet, "subType", KexiDB::Field::typeString(KexiDB::Field::BigInteger),
+ commandGroup );
+ //propertySet["unsigned"] = QVariant(true,4);
+ d->setPropertyValueIfNeeded( propertySet, "unsigned", QVariant(true,4), commandGroup );
+/*todo*/
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ updateActions();
+}
+
+/*void KexiTableDesignerView::slotCellSelected(int, int row)
+{
+ kdDebug() << "KexiTableDesignerView::slotCellSelected()" << endl;
+ if(row == m_row)
+ return;
+ m_row = row;
+ propertyBufferSwitched();
+}*/
+
+tristate KexiTableDesignerView::beforeSwitchTo(int mode, bool &dontStore)
+{
+ if (!d->view->acceptRowEdit())
+ return false;
+/* if (mode==Kexi::DesignViewMode) {
+ initData();
+ return true;
+ }
+ else */
+ tristate res = true;
+ if (mode==Kexi::DataViewMode) {
+ if (!dirty() && parentDialog()->neverSaved()) {
+ KMessageBox::sorry(this, i18n("Cannot switch to data view, because table design is empty.\n"
+ "First, please create your design.") );
+ return cancelled;
+ }
+//<temporary>
+ else if (dirty() && !parentDialog()->neverSaved()) {
+// cancelled = (KMessageBox::No == KMessageBox::questionYesNo(this, i18n("Saving changes for existing table design is not yet supported.\nDo you want to discard your changes now?")));
+
+// KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ bool emptyTable;
+ int r = KMessageBox::warningYesNoCancel(this,
+ i18n("Saving changes for existing table design is now required.")
+ + "\n" + d->messageForSavingChanges(emptyTable, /* skip warning? */!isPhysicalAlteringNeeded()),
+ QString::null,
+ KStdGuiItem::save(), KStdGuiItem::discard(), QString::null,
+ KMessageBox::Notify|KMessageBox::Dangerous);
+ if (r == KMessageBox::Cancel)
+ res = cancelled;
+ else
+ res = true;
+ dontStore = (r!=KMessageBox::Yes);
+ if (!dontStore)
+ d->dontAskOnStoreData = true;
+// if (dontStore)
+// setDirty(false);
+ }
+//</temporary>
+ //todo
+ return res;
+ }
+ else if (mode==Kexi::TextViewMode) {
+ //todo
+ }
+ return res;
+}
+
+tristate KexiTableDesignerView::afterSwitchFrom(int mode)
+{
+ if (mode==Kexi::NoViewMode || mode==Kexi::DataViewMode) {
+ initData();
+ }
+ return true;
+}
+
+KoProperty::Set *KexiTableDesignerView::propertySet()
+{
+ return d->sets ? d->sets->currentPropertySet() : 0;
+}
+
+/*
+void KexiTableDesignerView::removeCurrentPropertySet()
+{
+ const int r = d->view->currentRow();
+ KoProperty::Set *buf = d->sets.at(r);
+ if (!buf)
+ return;
+ buf->debug();
+// m_currentBufferCleared = true;
+ d->sets.remove(r);
+ propertysetswitched();
+// delete buf;
+// m_currentBufferCleared = false;
+}
+*/
+
+void KexiTableDesignerView::slotBeforeCellChanged(
+ KexiTableItem *item, int colnum, QVariant& newValue, KexiDB::ResultInfo* /*result*/)
+{
+ if (!d->slotBeforeCellChanged_enabled)
+ return;
+// kdDebug() << d->view->selectedItem() << " " << item
+ //<< " " << d->sets->at( d->view->currentRow() ) << " " << propertySet() << endl;
+ if (colnum==COLUMN_ID_CAPTION) {//'caption'
+// if (!item->at(1).toString().isEmpty() && item->at(1).isNull()) {
+ //if 'type' is not filled yet
+ if (item->at(COLUMN_ID_TYPE).isNull()) {
+ //auto select 1st row of 'type' column
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant((int)0));
+ }
+
+ KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
+ if (propertySetForItem) {
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false; //because we'll add the two changes as one KMacroCommand
+ QString oldName( propertySetForItem->property("name").value().toString() );
+ QString oldCaption( propertySetForItem->property("caption").value().toString() );
+
+ //we need to create the action now as set["name"] will be changed soon..
+ ChangeFieldPropertyCommand *changeCaptionCommand
+ = new ChangeFieldPropertyCommand( this, *propertySetForItem, "caption", oldCaption, newValue);
+
+ //update field caption and name
+ propertySetForItem->changeProperty("caption", newValue);
+ propertySetForItem->changeProperty("name", newValue); // "name" prop. is of custom type Identifier, so this assignment
+ // will automatically convert newValue to an valid identifier
+
+ //remember this action containing 2 subactions
+ CommandGroup *changeCaptionAndNameCommand = new CommandGroup(
+ i18n("Change \"%1\" field's name to \"%2\" and caption from \"%3\" to \"%4\"")
+ .arg(oldName).arg(propertySetForItem->property("name").value().toString())
+ .arg(oldCaption).arg(newValue.toString() ));
+ changeCaptionAndNameCommand->addCommand( changeCaptionCommand );
+// new ChangeFieldPropertyCommand( this, *propertySetForItem,
+ // "caption", oldCaption, newValue)
+ // );
+ changeCaptionAndNameCommand->addCommand(
+ new ChangeFieldPropertyCommand( this, *propertySetForItem,
+ "name", oldName, propertySetForItem->property("name").value().toString())
+ );
+ addHistoryCommand( changeCaptionAndNameCommand, false /* !execute */ );
+
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ }
+ }
+ else if (colnum==COLUMN_ID_TYPE) {//'type'
+ if (newValue.isNull()) {
+ //'type' col will be cleared: clear all other columns as well
+ d->slotBeforeCellChanged_enabled = false;
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, QVariant(QString::null));
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC, QVariant());
+ d->slotBeforeCellChanged_enabled = true;
+ return;
+ }
+
+ KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
+ if (!propertySetForItem)
+ return;
+
+ KoProperty::Set &set = *propertySetForItem; //propertySet();
+
+ //'type' col is changed (existed before)
+ //-get type group number
+ KexiDB::Field::TypeGroup fieldTypeGroup;
+ int i_fieldTypeGroup = newValue.toInt()+1/*counting from 1*/;
+ if (i_fieldTypeGroup < 1 || i_fieldTypeGroup >
+#ifdef KEXI_NO_BLOB_FIELDS
+//! @todo remove this later
+ (int)KexiDB::Field::LastTypeGroup-1) //don't show last (BLOB) type
+#else
+ (int)KexiDB::Field::LastTypeGroup)
+#endif
+ return;
+ fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(i_fieldTypeGroup);
+
+ //-get 1st type from this group, and update 'type' property
+ KexiDB::Field::Type fieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
+ if (fieldType==KexiDB::Field::InvalidType)
+ fieldType = KexiDB::Field::Text;
+//moved down set["type"] = (int)fieldType;
+// set["subType"] = KexiDB::Field::typeName(fieldType);
+
+ //-get subtypes for this type: keys (slist) and names (nlist)
+ QStringList slist, nlist;
+ getSubTypeListData(fieldTypeGroup, slist, nlist);
+
+ QString subTypeValue;
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ if (fieldType==KexiDB::Field::BLOB) {
+ // special case: BLOB type uses "mime-based" subtypes
+ subTypeValue = slist.first();
+ }
+ else {*/
+ subTypeValue = KexiDB::Field::typeString(fieldType);
+ //}
+ KoProperty::Property *subTypeProperty = &set["subType"];
+ kexipluginsdbg << subTypeProperty->value() << endl;
+
+ // *** this action contains subactions ***
+ CommandGroup *changeDataTypeCommand = new CommandGroup(
+ i18n("Change data type for field \"%1\" to \"%2\"")
+ .arg(set["name"].value().toString()).arg( KexiDB::Field::typeName( fieldType ) ) );
+
+//kexipluginsdbg << "++++++++++" << slist << nlist << endl;
+
+ //update subtype list and value
+ const bool forcePropertySetReload
+ = KexiDB::Field::typeGroup( KexiDB::Field::typeForString(subTypeProperty->value().toString()) )
+ != fieldTypeGroup; //<-- ?????
+// const bool forcePropertySetReload = set["type"].value().toInt() != (int)fieldTypeGroup;
+ const bool useListData = slist.count() > 1; //disabled-> || fieldType==KexiDB::Field::BLOB;
+
+ if (!useListData) {
+ slist.clear(); //empty list will be passed
+ nlist.clear();
+ }
+ d->setPropertyValueIfNeeded( set, "type", (int)fieldType, changeDataTypeCommand,
+ false /*!forceAddCommand*/, true /*rememberOldValue*/);
+
+ // notNull and defaultValue=false is reasonable for boolean type
+ if (fieldType == KexiDB::Field::Boolean) {
+//! @todo maybe this is good for other data types as well?
+ d->setPropertyValueIfNeeded( set, "notNull", QVariant(true, 1), changeDataTypeCommand,
+ false /*!forceAddCommand*/, false /*!rememberOldValue*/);
+ d->setPropertyValueIfNeeded( set, "defaultValue", QVariant(false, 1), changeDataTypeCommand,
+ false /*!forceAddCommand*/, false /*!rememberOldValue*/);
+ }
+
+/* if (useListData) {
+ {
+ subTypeProperty->setListData( slist, nlist );
+ }
+ else {
+ subTypeProperty->setListData( 0 );
+ }*/
+ if (set["primaryKey"].value().toBool()==true) {
+ //primary keys require big int, so if selected type is not integer- remove PK
+ if (fieldTypeGroup != KexiDB::Field::IntegerGroup) {
+ /*not needed, line below will do the work
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_ICON, QVariant());
+ d->view->data()->saveRowChanges(*item); */
+ //set["primaryKey"] = QVariant(false, 1);
+ d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(false, 1), changeDataTypeCommand );
+//! @todo should we display (passive?) dialog informing about cleared pkey?
+ }
+ }
+// if (useListData)
+// subTypeProperty->setValue( subTypeValue, false/*!rememberOldValue*/ );
+ d->setPropertyValueIfNeeded( set, "subType", subTypeValue,
+ changeDataTypeCommand, false, false /*!rememberOldValue*/,
+ &slist, &nlist );
+
+ if (d->updatePropertiesVisibility(fieldType, set, changeDataTypeCommand) || forcePropertySetReload) {
+ //properties' visiblility changed: refresh prop. set
+ propertySetReloaded(true);
+ }
+
+ addHistoryCommand( changeDataTypeCommand, false /* !execute */ );
+ }
+ else if (colnum==COLUMN_ID_DESC) {//'description'
+ KoProperty::Set *propertySetForItem = d->sets->findPropertySetForItem(*item);
+ if (!propertySetForItem)
+ return;
+ //update field desc.
+ QVariant oldValue((*propertySetForItem)["description"].value());
+ kexipluginsdbg << oldValue << endl;
+ propertySetForItem->changeProperty("description", newValue);
+ /*moved addHistoryCommand(
+ new ChangeFieldPropertyCommand( this, *propertySetForItem,
+ "description", oldValue, newValue ), false);*/
+ }
+}
+
+void KexiTableDesignerView::slotRowUpdated(KexiTableItem *item)
+{
+ const int row = d->view->data()->findRef(item);
+ if (row < 0)
+ return;
+
+ setDirty();
+
+ //-check if the row was empty before updating
+ //if yes: we want to add a property set for this new row (field)
+ QString fieldCaption( item->at(COLUMN_ID_CAPTION).toString() );
+ const bool prop_set_allowed = !item->at(COLUMN_ID_TYPE).isNull();
+
+ if (!prop_set_allowed && d->sets->at(row)/*propertySet()*/) {
+ //there is a property set, but it's not allowed - remove it:
+ d->sets->remove( row ); //d->sets->removeCurrentPropertySet();
+
+ //clear 'type' column:
+ d->view->data()->clearRowEditBuffer();
+// d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE, QVariant());
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant());
+ d->view->data()->saveRowChanges(*item);
+
+ } else if (prop_set_allowed && !d->sets->at(row)/*propertySet()*/) {
+ //-- create a new field:
+ KexiDB::Field::TypeGroup fieldTypeGroup = static_cast<KexiDB::Field::TypeGroup>(
+ item->at(COLUMN_ID_TYPE).toInt()+1/*counting from 1*/ );
+ int intFieldType = KexiDB::defaultTypeForGroup( fieldTypeGroup );
+ if (intFieldType==0)
+ return;
+
+ QString description( item->at(COLUMN_ID_DESC).toString() );
+
+//todo: check uniqueness:
+ QString fieldName( KexiUtils::string2Identifier(fieldCaption) );
+
+ KexiDB::Field::Type fieldType = KexiDB::intToFieldType( intFieldType );
+ KexiDB::Field field( //tmp
+ fieldName,
+ fieldType,
+ KexiDB::Field::NoConstraints,
+ KexiDB::Field::NoOptions,
+ /*length*/0,
+ /*precision*/0,
+ /*defaultValue*/QVariant(),
+ fieldCaption,
+ description,
+ /*width*/0);
+// m_newTable->addField( field );
+
+ // reasonable case for boolean type: set notNull flag and "false" as default value
+ if (fieldType == KexiDB::Field::Boolean) {
+ field.setNotNull( true );
+ field.setDefaultValue( QVariant(false, 0) );
+ }
+
+ kexipluginsdbg << "KexiTableDesignerView::slotRowUpdated(): " << field.debugString() << endl;
+
+ //create a new property set:
+ KoProperty::Set *newSet = createPropertySet( row, field, true );
+//moved
+ //add a special property indicating that this is brand new buffer,
+ //not just changed
+// KoProperty::Property* prop = new KoProperty::Property("newrow", QVariant());
+// prop->setVisible(false);
+// newbuff->addProperty( prop );
+
+ //refresh property editor:
+ propertySetSwitched();
+
+ if (row>=0) {
+ if (d->addHistoryCommand_in_slotRowUpdated_enabled) {
+ addHistoryCommand( new InsertFieldCommand( this, row, *newSet /*propertySet()*/ ), //, field /*will be copied*/
+ false /* !execute */ );
+ }
+ }
+ else {
+ kexipluginswarn << "KexiTableDesignerView::slotRowUpdated() row # not found !" << endl;
+ }
+ }
+}
+
+void KexiTableDesignerView::updateActions()
+{
+ updateActions(false);
+}
+
+void KexiTableDesignerView::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
+{
+// if (!d->slotPropertyChanged_enabled)
+// return;
+ const QCString pname = property.name();
+ kexipluginsdbg << "KexiTableDesignerView::slotPropertyChanged(): " << pname << " = " << property.value()
+ << " (oldvalue = " << property.oldValue() << ")" << endl;
+
+ // true is PK should be altered
+ bool changePrimaryKey = false;
+ // true is PK should be set to true, otherwise unset
+ bool setPrimaryKey = false;
+
+ if (pname=="primaryKey" && d->slotPropertyChanged_primaryKey_enabled) {
+ changePrimaryKey = true;
+ setPrimaryKey = property.value().toBool();
+ }
+
+ // update "lookup column" icon
+ if (pname=="rowSource" || pname=="rowSourceType") {
+//! @todo indicate invalid definitions of lookup columns as well using a special icon
+//! (e.g. due to missing data source)
+ const int row = d->sets->findRowForPropertyValue("uid", set["uid"].value().toInt());
+ KexiTableItem *item = d->view->itemAt(row);
+ if (item)
+ d->updateIconForItem(*item, set);
+ }
+
+ //setting autonumber requires setting PK as well
+ CommandGroup *setAutonumberCommand = 0;
+ CommandGroup *toplevelCommand = 0;
+ if (pname=="autoIncrement" && property.value().toBool()==true) {
+ if (set["primaryKey"].value().toBool()==false) {//we need PKEY here!
+ QString msg = QString("<p>")
+ +i18n("Setting autonumber requires primary key to be set for current field.")+"</p>";
+ if (d->primaryKeyExists)
+ msg += (QString("<p>")+ i18n("Previous primary key will be removed.")+"</p>");
+ msg += (QString("<p>")
+ +i18n("Do you want to create primary key for current field? "
+ "Click \"Cancel\" to cancel setting autonumber.")+"</p>");
+
+ if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
+ i18n("Setting Autonumber Field"),
+ KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
+ {
+ changePrimaryKey = true;
+ setPrimaryKey = true;
+ //switchPrimaryKey(set, true);
+ // this will be toplevel command
+ setAutonumberCommand = new CommandGroup(
+ i18n("Assign autonumber for field \"%1\"").arg(set["name"].value().toString()) );
+ toplevelCommand = setAutonumberCommand;
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(true,1), setAutonumberCommand );
+ }
+ else {
+ setAutonumberCommand = new CommandGroup(
+ i18n("Remove autonumber from field \"%1\"").arg(set["name"].value().toString()) );
+ //d->slotPropertyChanged_enabled = false;
+// set["autoIncrement"].setValue( QVariant(false,1), false/*don't save old*/);
+// d->slotPropertyChanged_enabled = true;
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setAutonumberCommand,
+ true /*forceAddCommand*/, false/*rememberOldValue*/ );
+ addHistoryCommand( setAutonumberCommand, false /* !execute */ );
+ return;
+ }
+ }
+ }
+
+ //clear PK when these properties were set to false:
+ if ((pname=="indexed" || pname=="unique" || pname=="notNull")
+ && set["primaryKey"].value().toBool() && property.value().toBool()==false)
+ {
+//! @todo perhaps show a hint in help panel telling what happens?
+ changePrimaryKey = true;
+ setPrimaryKey = false;
+ // this will be toplevel command
+ CommandGroup *unsetIndexedOrUniquOrNotNullCommand = new CommandGroup(
+ i18n("Set \"%1\" property for field \"%2\"").arg(property.caption()).arg(set["name"].value().toString()) );
+ toplevelCommand = unsetIndexedOrUniquOrNotNullCommand;
+ d->setPropertyValueIfNeeded( set, pname, QVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
+ if (pname=="notNull") {
+//? d->setPropertyValueIfNeeded( set, "notNull", QVariant(true,1), unsetIndexedOrUniquOrNotNullCommand );
+ d->setPropertyValueIfNeeded( set, "unique", QVariant(false,1), unsetIndexedOrUniquOrNotNullCommand );
+ }
+ }
+
+ if (pname=="defaultValue") {
+ KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
+ set["defaultValue"].setType((KoProperty::PropertyType)KexiDB::Field::variantType(type));
+ }
+
+ if (pname=="subType" && d->slotPropertyChanged_subType_enabled) {
+ d->slotPropertyChanged_subType_enabled = false;
+ if (set["primaryKey"].value().toBool()==true
+ && property.value().toString()!=KexiDB::Field::typeString(KexiDB::Field::BigInteger))
+ {
+ kexipluginsdbg << "INVALID " << property.value().toString() << endl;
+// if (KMessageBox::Yes == KMessageBox::questionYesNo(this, msg,
+// i18n("This field has promary key assigned. Setting autonumber field"),
+// KGuiItem(i18n("Create &Primary Key"), "key"), KStdGuiItem::cancel() ))
+
+ }
+ KexiDB::Field::Type type = KexiDB::intToFieldType( set["type"].value().toInt() );
+ QString typeName;
+/* disabled - "mime" is moved from subType to "objectType" custom property
+ if (type==KexiDB::Field::BLOB) { //special case
+ //find i18n'd text
+ QStringList stringsList, namesList;
+ getSubTypeListData(KexiDB::Field::BLOBGroup, stringsList, namesList);
+ const int stringIndex = stringsList.findIndex( property.value().toString() );
+ if (-1 == stringIndex || stringIndex>=(int)namesList.count())
+ typeName = property.value().toString(); //for sanity
+ else
+ typeName = namesList[stringIndex];
+ }
+ else {*/
+ typeName = KexiDB::Field::typeName( KexiDB::Field::typeForString(property.value().toString()) );
+// }
+// kdDebug() << property.value().toString() << endl;
+// kdDebug() << set["type"].value() << endl;
+// if (KexiDB::Field::typeGroup( set["type"].value().toInt() ) == (int)KexiDB::Field::TextGroup) {
+ CommandGroup* changeFieldTypeCommand = new CommandGroup(
+ i18n("Change type for field \"%1\" to \"%2\"").arg(set["name"].value().toString())
+ .arg(typeName) );
+ d->setPropertyValueIfNeeded( set, "subType", property.value(), property.oldValue(),
+ changeFieldTypeCommand );
+
+ kexipluginsdbg << set["type"].value() << endl;
+ const KexiDB::Field::Type newType = KexiDB::Field::typeForString(property.value().toString());
+ set["type"].setValue( newType );
+
+ // cast "defaultValue" property value to a new type
+ QVariant oldDefVal( set["defaultValue"].value() );
+ QVariant newDefVal( tryCastQVariant(oldDefVal, KexiDB::Field::variantType(type)) );
+ if (oldDefVal.type()!=newDefVal.type())
+ set["defaultValue"].setType( newDefVal.type() );
+ d->setPropertyValueIfNeeded( set, "defaultValue", newDefVal, newDefVal,
+ changeFieldTypeCommand );
+
+ d->updatePropertiesVisibility(newType, set);
+ //properties' visiblility changed: refresh prop. set
+ propertySetReloaded(true);
+ d->slotPropertyChanged_subType_enabled = true;
+
+ addHistoryCommand( changeFieldTypeCommand, false /* !execute */ );
+ return;
+// }
+// d->slotPropertyChanged_subType_enabled = true;
+// return;
+ }
+
+ if (d->addHistoryCommand_in_slotPropertyChanged_enabled && !changePrimaryKey/*we'll add multiple commands for PK*/) {
+ addHistoryCommand( new ChangeFieldPropertyCommand(this, set,
+ property.name(), property.oldValue() /* ??? */, property.value()),
+ false /* !execute */ );
+ }
+
+ if (changePrimaryKey) {
+ d->slotPropertyChanged_primaryKey_enabled = false;
+ if (setPrimaryKey) {
+ //primary key implies some rules
+ //const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled = d->addHistoryCommand_in_slotPropertyChanged_enabled;
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+
+ //this action contains subactions
+ CommandGroup *setPrimaryKeyCommand = new CommandGroup(
+ i18n("Set primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ if (toplevelCommand)
+ toplevelCommand->addCommand( setPrimaryKeyCommand );
+ else
+ toplevelCommand = setPrimaryKeyCommand;
+
+ d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(true,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
+ d->setPropertyValueIfNeeded( set, "unique", QVariant(true,1), setPrimaryKeyCommand );
+ d->setPropertyValueIfNeeded( set, "notNull", QVariant(true,1), setPrimaryKeyCommand );
+ d->setPropertyValueIfNeeded( set, "allowEmpty", QVariant(false,1), setPrimaryKeyCommand );
+ d->setPropertyValueIfNeeded( set, "indexed", QVariant(true,1), setPrimaryKeyCommand );
+//! \todo: add setting for this: "Integer PKeys have autonumber set by default"
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(true,1), setPrimaryKeyCommand );
+
+/* set["unique"] = QVariant(true,1);
+ set["notNull"] = QVariant(true,1);
+ set["allowEmpty"] = QVariant(false,1);
+ set["indexed"] = QVariant(true,1);
+ set["autoIncrement"] = QVariant(true,1);*/
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = prev_addHistoryCommand_in_slotPropertyChanged_enabled;
+//down addHistoryCommand( toplevelCommand, false /* !execute */ );
+ }
+ else {//! set PK to false
+ //remember this action containing 2 subactions
+ CommandGroup *setPrimaryKeyCommand = new CommandGroup(
+ i18n("Unset primary key for field \"%1\"")
+ .arg(set["name"].value().toString()) );
+ if (toplevelCommand)
+ toplevelCommand->addCommand( setPrimaryKeyCommand );
+ else
+ toplevelCommand = setPrimaryKeyCommand;
+
+ d->setPropertyValueIfNeeded( set, "primaryKey", QVariant(false,1), setPrimaryKeyCommand, true /*forceAddCommand*/ );
+ d->setPropertyValueIfNeeded( set, "autoIncrement", QVariant(false,1), setPrimaryKeyCommand );
+// set["autoIncrement"] = QVariant(false,1);
+
+//down addHistoryCommand( toplevelCommand, false /* !execute */ );
+ }
+ switchPrimaryKey(set, setPrimaryKey, true/*wasPKey*/, toplevelCommand);
+ d->updatePropertiesVisibility(
+ KexiDB::Field::typeForString( set["subType"].value().toString() ), set, toplevelCommand);
+ addHistoryCommand( toplevelCommand, false /* !execute */ );
+ //properties' visiblility changed: refresh prop. set
+ propertySetReloaded(true/*preservePrevSelection*/);
+ d->slotPropertyChanged_primaryKey_enabled = true;
+ }
+}
+
+void KexiTableDesignerView::slotRowInserted()
+{
+ updateActions();
+
+ if (d->addHistoryCommand_in_slotRowInserted_enabled) {
+ const int row = d->view->currentRow();
+ if (row>=0) {
+ addHistoryCommand( new InsertEmptyRowCommand( this, row ), false /* !execute */ );
+ }
+ }
+ //TODO?
+}
+
+void KexiTableDesignerView::slotAboutToDeleteRow(
+ KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint)
+{
+ Q_UNUSED(result)
+ Q_UNUSED(repaint)
+ if (item[COLUMN_ID_ICON].toString()=="key")
+ d->primaryKeyExists = false;
+
+ if (d->addHistoryCommand_in_slotAboutToDeleteRow_enabled) {
+ const int row = d->view->data()->findRef(&item);
+ KoProperty::Set *set = row >=0 ? d->sets->at(row) : 0;
+ //set can be 0 here, what means "removing empty row"
+ addHistoryCommand(
+ new RemoveFieldCommand( this, row, set ),
+ false /* !execute */
+ );
+ }
+}
+
+KexiDB::Field * KexiTableDesignerView::buildField( const KoProperty::Set &set ) const
+{
+ //create a map of property values
+ kexipluginsdbg << set["type"].value() << endl;
+ QMap<QCString, QVariant> values = KoProperty::propertyValues(set);
+ //remove internal values, to avoid creating custom field's properties
+ QMap<QCString, QVariant>::Iterator it = values.begin();
+ KexiDB::Field *field = new KexiDB::Field();
+
+ while (it!=values.end()) {
+ const QString propName( it.key() );
+ if (d->internalPropertyNames.find(propName.latin1()) || propName.startsWith("this:")
+ || (/*sanity*/propName=="objectType" && KexiDB::Field::BLOB != KexiDB::intToFieldType( set["type"].value().toInt() )))
+ {
+ QMap<QCString, QVariant>::Iterator it_tmp = it;
+ ++it;
+ values.remove(it_tmp);
+ }
+ else
+ ++it;
+ }
+ //assign properties to the field
+ // (note that "objectType" property will be saved as custom property)
+ if (!KexiDB::setFieldProperties( *field, values )) {
+ delete field;
+ return 0;
+ }
+ return field;
+}
+
+tristate KexiTableDesignerView::buildSchema(KexiDB::TableSchema &schema, bool beSilent)
+{
+ if (!d->view->acceptRowEdit())
+ return cancelled;
+
+ tristate res = true;
+ //check for pkey; automatically add a pkey if user wanted
+ if (!d->primaryKeyExists) {
+ if (beSilent) {
+ kexipluginsdbg << "KexiTableDesignerView::buildSchema(): no primay key defined..." << endl;
+ }
+ else {
+ const int questionRes = KMessageBox::questionYesNoCancel(this,
+ i18n("<p>Table \"%1\" has no <b>primary key</b> defined.</p>"
+ "<p>Although a primary key is not required, it is needed "
+ "for creating relations between database tables. "
+ "Do you want to add primary key automatically now?</p>"
+ "<p>If you want to add a primary key by hand, press \"Cancel\" "
+ "to cancel saving table design.</p>").arg(schema.name()),
+ QString::null, KGuiItem(i18n("&Add Primary Key"), "key"), KStdGuiItem::no(),
+ "autogeneratePrimaryKeysOnTableDesignSaving");
+ if (questionRes==KMessageBox::Cancel) {
+ return cancelled;
+ }
+ else if (questionRes==KMessageBox::Yes) {
+ //-find unique name, starting with, "id", "id2", ....
+ int i=0;
+ int idIndex = 1; //means "id"
+ QString pkFieldName("id%1");
+ QString pkFieldCaption(i18n("Identifier%1", "Id%1"));
+ while (i<(int)d->sets->size()) {
+ KoProperty::Set *set = d->sets->at(i);
+ if (set) {
+ if ((*set)["name"].value().toString()
+ == pkFieldName.arg(idIndex==1?QString::null : QString::number(idIndex))
+ || (*set)["caption"].value().toString()
+ == pkFieldCaption.arg(idIndex==1?QString::null : QString::number(idIndex)))
+ {
+ //try next id index
+ i = 0;
+ idIndex++;
+ continue;
+ }
+ }
+ i++;
+ }
+ pkFieldName = pkFieldName.arg(idIndex==1?QString::null : QString::number(idIndex));
+ pkFieldCaption = pkFieldCaption.arg(idIndex==1?QString::null : QString::number(idIndex));
+ //ok, add PK with such unique name
+ d->view->insertEmptyRow(0);
+ d->view->setCursorPosition(0, COLUMN_ID_CAPTION);
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_CAPTION,
+ QVariant(pkFieldCaption));
+ d->view->data()->updateRowEditBuffer(d->view->selectedItem(), COLUMN_ID_TYPE,
+ QVariant(KexiDB::Field::IntegerGroup-1/*counting from 0*/));
+ if (!d->view->data()->saveRowChanges(*d->view->selectedItem(), true)) {
+ return cancelled;
+ }
+ slotTogglePrimaryKey();
+ }
+ }
+ }
+
+ //check for duplicates
+ KoProperty::Set *b = 0;
+ bool no_fields = true;
+ int i;
+ QDict<char> names(101, false);
+ char dummy;
+ for (i=0;i<(int)d->sets->size();i++) {
+ b = d->sets->at(i);
+ if (b) {
+ no_fields = false;
+ const QString name = (*b)["name"].value().toString();
+ if (name.isEmpty()) {
+ if (beSilent) {
+ kexipluginswarn <<
+ QString("KexiTableDesignerView::buildSchema(): no field caption entered at row %1...")
+ .arg(i+1) << endl;
+ }
+ else {
+ d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
+ d->view->startEditCurrentCell();
+ KMessageBox::information(this, i18n("You should enter field caption.") );
+ }
+ res = cancelled;
+ break;
+ }
+ if (names[name]) {
+ break;
+ }
+ names.insert( name, &dummy ); //remember
+ }
+ }
+ if (res == true && no_fields) {//no fields added
+ if (beSilent) {
+ kexipluginswarn <<
+ "KexiTableDesignerView::buildSchema(): no field defined..." << endl;
+ }
+ else {
+ KMessageBox::sorry(this,
+ i18n("You have added no fields.\nEvery table should have at least one field.") );
+ }
+ res = cancelled;
+ }
+ if (res == true && b && i<(int)d->sets->size()) {//found a duplicate
+ if (beSilent) {
+ kexipluginswarn <<
+ QString("KexiTableDesignerView::buildSchema(): duplicated field name '%1'")
+ .arg((*b)["name"].value().toString()) << endl;
+ }
+ else {
+ d->view->setCursorPosition(i, COLUMN_ID_CAPTION);
+ d->view->startEditCurrentCell();
+//! @todo for "names hidden" mode we won't get this error because user is unable to change names
+ KMessageBox::sorry(this,
+ i18n("You have added \"%1\" field name twice.\nField names cannot be repeated. "
+ "Correct name of the field.")
+ .arg((*b)["name"].value().toString()) );
+ }
+ res = cancelled;
+ }
+ if (res == true) {
+ //for every field, create KexiDB::Field definition
+ for (i=0;i<(int)d->sets->size();i++) {
+ KoProperty::Set *s = d->sets->at(i);
+ if (!s)
+ continue;
+ KexiDB::Field * f = buildField( *s );
+ if (!f)
+ continue; //hmm?
+ schema.addField(f);
+ if (!(*s)["rowSource"].value().toString().isEmpty() && !(*s)["rowSourceType"].value().toString().isEmpty()) {
+ //add lookup column
+ KexiDB::LookupFieldSchema *lookupFieldSchema = new KexiDB::LookupFieldSchema();
+ lookupFieldSchema->rowSource().setTypeByName( (*s)["rowSourceType"].value().toString() );
+ lookupFieldSchema->rowSource().setName( (*s)["rowSource"].value().toString() );
+ lookupFieldSchema->setBoundColumn( (*s)["boundColumn"].value().toInt() );
+//! @todo this is backward-compatible code for "single visible column" implementation
+//! for multiple columns, only the first is displayed, so there is a data loss is GUI is used
+//! -- special koproperty editor needed
+ QValueList<uint> visibleColumns;
+ const int visibleColumn = (*s)["visibleColumn"].value().toInt();
+ if (visibleColumn >= 0)
+ visibleColumns.append( (uint)visibleColumn );
+ lookupFieldSchema->setVisibleColumns( visibleColumns );
+//! @todo support columnWidths(), columnHeadersVisible(), maximumListRows(), limitToList(), displayWidget()
+ if (!schema.setLookupFieldSchema(f->name(), lookupFieldSchema)) {
+ kexipluginswarn <<
+ "KexiTableDesignerView::buildSchema(): !schema.setLookupFieldSchema()" << endl;
+ delete lookupFieldSchema;
+ return false;
+ }
+ }
+ }
+ }
+ return res;
+}
+
+//! @internal
+//! A recursive function for copying alter table actions from undo/redo commands.
+static void copyAlterTableActions(KCommand* command, KexiDB::AlterTableHandler::ActionList &actions)
+{
+ CommandGroup* cmdGroup = dynamic_cast<CommandGroup*>( command );
+ if (cmdGroup) {//command group: flatten it
+ for (QPtrListIterator<KCommand> it(cmdGroup->commands()); it.current(); ++it)
+ copyAlterTableActions(it.current(), actions);
+ return;
+ }
+ Command* cmd = dynamic_cast<Command*>( command );
+ if (!cmd) {
+ kexipluginswarn << "KexiTableDesignerView::copyAlterTableActions(): cmd is not of type 'Command'!" << endl;
+ return;
+ }
+ KexiDB::AlterTableHandler::ActionBase* action = cmd->createAction();
+ //some commands can contain null actions, e.g. "set visibility" command
+ if (action)
+ actions.append( action );
+}
+
+tristate KexiTableDesignerView::buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions)
+{
+ actions.clear();
+ kexipluginsdbg << "KexiTableDesignerView::buildAlterTableActions(): " << d->history->commands().count()
+ << " top-level command(s) to process..." << endl;
+ for (QPtrListIterator<KCommand> it(d->history->commands()); it.current(); ++it) {
+ copyAlterTableActions(it.current(), actions);
+ }
+ return true;
+}
+
+KexiDB::SchemaData* KexiTableDesignerView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ if (tempData()->table || m_dialog->schemaData()) //must not be
+ return 0;
+
+ //create table schema definition
+ tempData()->table = new KexiDB::TableSchema(sdata.name());
+ tempData()->table->setName( sdata.name() );
+ tempData()->table->setCaption( sdata.caption() );
+ tempData()->table->setDescription( sdata.description() );
+
+ tristate res = buildSchema(*tempData()->table);
+ cancel = ~res;
+
+ //FINALLY: create table:
+ if (res == true) {
+ //todo
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ res = conn->createTable(tempData()->table);
+ if (res!=true)
+ parentDialog()->setStatus(conn, "");
+ }
+
+ if (res == true) {
+ //we've current schema
+ tempData()->tableSchemaChangedInPreviousView = true;
+//not needed; KexiProject emits newItemStored signal //let project know the table is created
+// mainWin()->project()->emitTableCreated(*tempData()->table);
+ }
+ else {
+ delete tempData()->table;
+ tempData()->table = 0;
+ }
+ return tempData()->table;
+}
+
+tristate KexiTableDesignerView::storeData(bool dontAsk)
+{
+ if (!tempData()->table || !m_dialog->schemaData()) {
+ d->recentResultOfStoreData = false;
+ return false;
+ }
+
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ KexiDB::AlterTableHandler *alterTableHandler = 0;
+ KexiDB::TableSchema *newTable = 0;
+
+ //- create action list for the alter table handler
+ KexiDB::AlterTableHandler::ActionList actions;
+ tristate res = buildAlterTableActions( actions );
+ bool realAlterTableCanBeUsed = false; //!< @todo this is temporary flag before we switch entirely to real alter table
+ if (res == true) {
+ alterTableHandler = new KexiDB::AlterTableHandler( *conn );
+ alterTableHandler->setActions(actions);
+
+ if (!d->tempStoreDataUsingRealAlterTable) {
+ //only compute requirements
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ args.onlyComputeRequirements = true;
+ (void)alterTableHandler->execute(tempData()->table->name(), args);
+ res = args.result;
+ if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
+ realAlterTableCanBeUsed = true;
+ }
+ }
+
+ if (res == true) {
+ res = KexiTablePart::askForClosingObjectsUsingTableSchema(
+ this, *conn, *tempData()->table,
+ i18n("You are about to change the design of table \"%1\" "
+ "but following objects using this table are opened:")
+ .arg(tempData()->table->name()));
+ }
+
+ if (res == true) {
+ if (!d->tempStoreDataUsingRealAlterTable && !realAlterTableCanBeUsed) {
+//! @todo temp; remove this case:
+ delete alterTableHandler;
+ alterTableHandler = 0;
+ // - inform about removing the current table and ask for confirmation
+ if (!d->dontAskOnStoreData && !dontAsk) {
+ bool emptyTable;
+ const QString msg = d->messageForSavingChanges(emptyTable);
+ if (!emptyTable) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(this, msg))
+ res = cancelled;
+ }
+ }
+ d->dontAskOnStoreData = false; //one-time use
+ if (~res) {
+ d->recentResultOfStoreData = res;
+ return res;
+ }
+ // keep old behaviour:
+ newTable = new KexiDB::TableSchema();
+ // copy the schema data
+ static_cast<KexiDB::SchemaData&>(*newTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
+ res = buildSchema(*newTable);
+ kexipluginsdbg << "KexiTableDesignerView::storeData() : BUILD SCHEMA:" << endl;
+ newTable->debug();
+
+ res = conn->alterTable(*tempData()->table, *newTable);
+ if (res != true)
+ parentDialog()->setStatus(conn, "");
+ }
+ else {
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ newTable = alterTableHandler->execute(tempData()->table->name(), args);
+ res = args.result;
+ kexipluginsdbg << "KexiTableDesignerView::storeData() : ALTER TABLE EXECUTE: "
+ << res.toString() << endl;
+ if (true != res) {
+ alterTableHandler->debugError();
+ parentDialog()->setStatus(alterTableHandler, "");
+ }
+ }
+ }
+ if (res == true) {
+ //change current schema
+ tempData()->table = newTable;
+ tempData()->tableSchemaChangedInPreviousView = true;
+ d->history->clear();
+ }
+ else {
+ delete newTable;
+ }
+ delete alterTableHandler;
+ d->recentResultOfStoreData = res;
+ return res;
+}
+
+tristate KexiTableDesignerView::simulateAlterTableExecution(QString *debugTarget)
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ if (mainWin()->activeWindow() != parentDialog()) //to avoid executing for multiple alter table views
+ return false;
+ if (!tempData()->table || !m_dialog->schemaData())
+ return false;
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ KexiDB::AlterTableHandler::ActionList actions;
+ tristate res = buildAlterTableActions( actions );
+//todo: result?
+ KexiDB::AlterTableHandler alterTableHandler( *conn );
+ alterTableHandler.setActions(actions);
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ if (debugTarget) {
+ args.debugString = debugTarget;
+ }
+ else {
+ args.simulate = true;
+ }
+ (void)alterTableHandler.execute(tempData()->table->name(), args);
+ return args.result;
+# else
+ return false;
+# endif
+#else
+ return false;
+#endif
+}
+
+void KexiTableDesignerView::slotSimulateAlterTableExecution()
+{
+ (void)simulateAlterTableExecution(0);
+}
+
+tristate KexiTableDesignerView::executeRealAlterTable()
+{
+ QSignal signal;
+ signal.connect( mainWin(), SLOT(slotProjectSave()) );
+ d->tempStoreDataUsingRealAlterTable = true;
+ d->recentResultOfStoreData = false;
+ signal.activate(); //will call KexiMainWindowImpl::slotProjectSaveAs() and thus storeData()
+ d->tempStoreDataUsingRealAlterTable = false;
+ return d->recentResultOfStoreData;
+}
+
+KexiTablePart::TempData* KexiTableDesignerView::tempData() const
+{
+ return static_cast<KexiTablePart::TempData*>(parentDialog()->tempData());
+}
+
+/*void KexiTableDesignerView::slotAboutToUpdateRow(
+ KexiTableItem* item, KexiDB::RowEditBuffer* buffer, KexiDB::ResultInfo* result)
+{
+ KexiDB::RowEditBuffer::SimpleMap map = buffer->simpleBuffer();
+ buffer->debug();
+
+ QVariant old_type = item->at(1);
+ QVariant *buf_type = buffer->at( d->view->field(1)->name() );
+
+ //check if there is a type specified
+// if ((old_type.isNull() && !buf_type) || (buf_type && buf_type->isNull())) {
+ //kdDebug() << "err" << endl;
+ //}
+// allow = true;
+// m_dirty = m_dirty | result->success;
+}*/
+
+#ifdef KEXI_DEBUG_GUI
+void KexiTableDesignerView::debugCommand( KCommand* command, int nestingLevel )
+{
+ if (dynamic_cast<Command*>(command))
+ KexiUtils::addAlterTableActionDebug(dynamic_cast<Command*>(command)->debugString(), nestingLevel);
+ else
+ KexiUtils::addAlterTableActionDebug(command->name(), nestingLevel);
+ //show subcommands
+ if (dynamic_cast<CommandGroup*>(command)) {
+ for (QPtrListIterator<KCommand> it(dynamic_cast<CommandGroup*>(command)->commands()); it.current(); ++it) {
+ debugCommand(it.current(), nestingLevel + 1);
+ }
+ }
+}
+#endif
+
+void KexiTableDesignerView::addHistoryCommand( KCommand* command, bool execute )
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ debugCommand( command, 0 );
+# endif
+ d->history->addCommand( command, execute );
+ updateUndoRedoActions();
+#endif
+}
+
+void KexiTableDesignerView::updateUndoRedoActions()
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+ setAvailable("edit_undo", d->historyActionCollection->action("edit_undo")->isEnabled());
+ setAvailable("edit_redo", d->historyActionCollection->action("edit_redo")->isEnabled());
+#endif
+}
+
+void KexiTableDesignerView::slotUndo()
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("UNDO:"));
+# endif
+ d->history->undo();
+ updateUndoRedoActions();
+#endif
+}
+
+void KexiTableDesignerView::slotRedo()
+{
+#ifndef KEXI_NO_UNDOREDO_ALTERTABLE
+# ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("REDO:"));
+# endif
+ d->history->redo();
+ updateUndoRedoActions();
+#endif
+}
+
+void KexiTableDesignerView::slotCommandExecuted(KCommand *command)
+{
+#ifdef KEXI_DEBUG_GUI
+ debugCommand( command, 1 );
+#endif
+}
+
+void KexiTableDesignerView::slotAboutToShowContextMenu()
+{
+ //update title
+ if (propertySet()) {
+ const KoProperty::Set &set = *propertySet();
+ QString captionOrName(set["caption"].value().toString());
+ if (captionOrName.isEmpty())
+ captionOrName = set["name"].value().toString();
+//! @todo show "field" icon
+ d->contextMenuTitle->setTitle( i18n("Table field \"%1\"").arg(captionOrName) );
+ }
+ else {
+ d->contextMenuTitle->setTitle( i18n("Empty table row", "Empty Row") );
+ }
+}
+
+QString KexiTableDesignerView::debugStringForCurrentTableSchema(tristate& result)
+{
+ KexiDB::TableSchema tempTable;
+ //copy schema data
+ static_cast<KexiDB::SchemaData&>(tempTable) = static_cast<KexiDB::SchemaData&>(*tempData()->table);
+ result = buildSchema(tempTable, true /*beSilent*/);
+ if (true!=result)
+ return QString::null;
+ return tempTable.debugString(false /*without name*/);
+}
+
+// -- low-level actions used by undo/redo framework
+
+void KexiTableDesignerView::clearRow(int row, bool addCommand)
+{
+ if (!d->view->acceptRowEdit())
+ return;
+ KexiTableItem *item = d->view->itemAt(row);
+ if (!item)
+ return;
+ //remove from prop. set
+ d->sets->remove( row );
+ //clear row in table view (just clear value in COLUMN_ID_TYPE column)
+// for (int i=0; i < (int)d->view->data()->columnsCount(); i++) {
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE, QVariant());
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ d->view->data()->saveRowChanges(*item, true);
+}
+
+void KexiTableDesignerView::insertField(int row, const QString& caption, bool addCommand)
+{
+ insertFieldInternal(row, 0, caption, addCommand);
+}
+
+void KexiTableDesignerView::insertField(int row, KoProperty::Set& set, bool addCommand)
+{
+ insertFieldInternal(row, &set, QString::null, addCommand);
+}
+
+void KexiTableDesignerView::insertFieldInternal(int row, KoProperty::Set* set, //const KexiDB::Field& field,
+ const QString& caption, bool addCommand)
+{
+ if (set && (!set->contains("type") || !set->contains("caption"))) {
+ kexipluginswarn << "KexiTableDesignerView::insertField(): no 'type' or 'caption' property in set!" << endl;
+ return;
+ }
+ if (!d->view->acceptRowEdit())
+ return;
+ KexiTableItem *item = d->view->itemAt(row);
+ if (!item)
+ return;
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION,
+ set ? (*set)["caption"].value() : QVariant(caption));//field.caption());
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
+ set ? (int)KexiDB::Field::typeGroup( (*set)["type"].value().toInt() )-1/*counting from 0*/
+ : (((int)KexiDB::Field::TextGroup)-1)/*default type, counting from 0*/
+ );
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC,
+ set ? (*set)["description"].value() : QVariant());//field.description());
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ //this will create a new property set:
+ d->view->data()->saveRowChanges(*item);
+ if (set) {
+ KoProperty::Set *newSet = d->sets->at(row);
+ if (newSet) {
+ *newSet = *set; //deep copy
+ }
+ else {
+ kexipluginswarn << "KexiTableDesignerView::insertField() !newSet, row==" << row << endl;
+ }
+ }
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+ }
+ d->view->updateRow( row );
+ propertySetReloaded(true);
+}
+
+void KexiTableDesignerView::insertEmptyRow( int row, bool addCommand )
+{
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowInserted_enabled = false;
+ }
+ d->view->insertEmptyRow( row );
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowInserted_enabled = true;
+ }
+}
+
+/*void KexiTableDesignerView::deleteRow( int row )
+{
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
+ d->view->deleteItem( d->view->data()->at(row) );
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
+}*/
+
+void KexiTableDesignerView::deleteRow( int row, bool addCommand )
+{
+ KexiTableItem *item = d->view->itemAt( row );
+ if (!item)
+ return;
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = false;
+ }
+ const bool res = d->view->deleteItem(item);
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotAboutToDeleteRow_enabled = true;
+ }
+ if (!res)
+ return;
+}
+
+void KexiTableDesignerView::changeFieldPropertyForRow( int row,
+ const QCString& propertyName, const QVariant& newValue,
+ KoProperty::Property::ListData* const listData, bool addCommand )
+{
+#ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("** changeFieldProperty: \"")
+ + QString(propertyName) + "\" to \"" + newValue.toString() + "\"", 2/*nestingLevel*/);
+#endif
+ if (!d->view->acceptRowEdit())
+ return;
+
+ KoProperty::Set* set = d->sets->at( row );
+ if (!set || !set->contains(propertyName))
+ return;
+ KoProperty::Property &property = set->property(propertyName);
+ if (listData) {
+ if (listData->keys.isEmpty())
+ property.setListData( 0 );
+ else
+ property.setListData( new KoProperty::Property::ListData(*listData) );
+ }
+ if (propertyName != "type") //delayed type update (we need to have subtype set properly)
+ property.setValue(newValue);
+ KexiTableItem *item = d->view->itemAt(row);
+ Q_ASSERT(item);
+
+ if (propertyName == "type") {
+ // d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotPropertyChanged_subType_enabled = false;
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_TYPE,
+ int( KexiDB::Field::typeGroup( newValue.toInt() ) )-1);
+ d->view->data()->saveRowChanges(*item);
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+// d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ // d->slotPropertyChanged_subType_enabled = true;
+ property.setValue(newValue); //delayed type update (we needed to have subtype set properly)
+ }
+
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotRowUpdated_enabled = false;
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ d->slotPropertyChanged_subType_enabled = false;
+ }
+ //special cases: properties displayed within the data grid:
+ if (propertyName == "caption") {
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_CAPTION, newValue);
+ d->view->data()->saveRowChanges(*item);
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ }
+ else if (propertyName == "description") {
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = false;
+ }
+ d->view->data()->updateRowEditBuffer(item, COLUMN_ID_DESC, newValue);
+ if (!addCommand) {
+ d->slotBeforeCellChanged_enabled = true;
+ }
+ d->view->data()->saveRowChanges(*item);
+ }
+ if (!addCommand) {
+ d->addHistoryCommand_in_slotPropertyChanged_enabled = true;
+ d->addHistoryCommand_in_slotRowUpdated_enabled = true;
+ d->slotPropertyChanged_subType_enabled = true;
+ }
+ d->view->updateRow( row );
+}
+
+void KexiTableDesignerView::changeFieldProperty( int fieldUID,
+ const QCString& propertyName, const QVariant& newValue,
+ KoProperty::Property::ListData* const listData, bool addCommand )
+{
+ //find a property by UID
+ const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
+ if (row<0) {
+ kexipluginswarn << "KexiTableDesignerView::changeFieldProperty(): field with uid="<<fieldUID<<" not found!"<<endl;
+ return;
+ }
+ changeFieldPropertyForRow(row, propertyName, newValue, listData, addCommand);
+}
+
+void KexiTableDesignerView::changePropertyVisibility(
+ int fieldUID, const QCString& propertyName, bool visible )
+{
+#ifdef KEXI_DEBUG_GUI
+ KexiUtils::addAlterTableActionDebug(QString("** changePropertyVisibility: \"")
+ + QString(propertyName) + "\" to \"" + (visible ? "true" : "false") + "\"", 2/*nestingLevel*/);
+#endif
+ if (!d->view->acceptRowEdit())
+ return;
+
+ //find a property by name
+ const int row = d->sets->findRowForPropertyValue("uid", fieldUID);
+ if (row<0)
+ return;
+ KoProperty::Set* set = d->sets->at( row );
+ if (!set || !set->contains(propertyName))
+ return;
+
+ KoProperty::Property &property = set->property(propertyName);
+ if (property.isVisible() != visible) {
+ property.setVisible(visible);
+ propertySetReloaded(true);
+ }
+}
+
+void KexiTableDesignerView::propertySetSwitched()
+{
+ KexiDataTable::propertySetSwitched();
+
+ //if (parentDialog()!=parentDialog()->mainWin()->currentDialog())
+ // return; //this is not the current dialog's view
+
+ static_cast<KexiTablePart*>(parentDialog()->part())->lookupColumnPage()
+ ->assignPropertySet(propertySet());
+}
+
+bool KexiTableDesignerView::isPhysicalAlteringNeeded()
+{
+ //- create action list for the alter table handler
+ KexiDB::AlterTableHandler::ActionList actions;
+ tristate res = buildAlterTableActions( actions );
+ if (res != true)
+ return true;
+
+ KexiDB::Connection *conn = mainWin()->project()->dbConnection();
+ KexiDB::AlterTableHandler *alterTableHandler = new KexiDB::AlterTableHandler( *conn );
+ alterTableHandler->setActions(actions);
+
+ //only compute requirements
+ KexiDB::AlterTableHandler::ExecutionArguments args;
+ args.onlyComputeRequirements = true;
+ (void)alterTableHandler->execute(tempData()->table->name(), args);
+ res = args.result;
+ delete alterTableHandler;
+ if (res == true && 0 == (args.requirements & (0xffff ^ KexiDB::AlterTableHandler::SchemaAlteringRequired)))
+ return false;
+ return true;
+}
+
+#include "kexitabledesignerview.moc"
diff --git a/kexi/plugins/tables/kexitabledesignerview.h b/kexi/plugins/tables/kexitabledesignerview.h
new file mode 100644
index 00000000..773163b6
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview.h
@@ -0,0 +1,258 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2007 Jaroslaw Staniek <[email protected]>
+
+ 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 KEXITABLEDESIGNERINTERVIEW_H
+#define KEXITABLEDESIGNERINTERVIEW_H
+
+#include <koproperty/property.h>
+#include <kexidb/alter.h>
+#include <core/kexitabledesignerinterface.h>
+
+#include <kexidatatable.h>
+#include "kexitablepart.h"
+
+class KexiTableItem;
+class KexiTableDesignerViewPrivate;
+class KCommand;
+class CommandGroup;
+
+namespace KoProperty {
+ class Set;
+}
+
+//! Design view of the Table Designer
+/*! Contains a spreadsheet-like space for entering field definitions.
+ Property editor is provided for altering field definitions.
+
+ The view also supports Undo and Redo operations.
+ These are connected to a factility creating a list of actions used
+ by AlterTableHandler to perform required operation of altering the table.
+
+ Altering itself is performed upon design saving (storeData()).
+ Saving unstored designs just creates a new table.
+ Saving changes made to empty (not filled with data) table is performed
+ by physically deleting the previous table schema and recreating it
+ TODO: this will be not quite when we have db relationships supported.
+
+ Saving changes made to table containing data requires use of the AlterTableHandler
+ functionality.
+*/
+class KexiTableDesignerView : public KexiDataTable, public KexiTableDesignerInterface
+{
+ Q_OBJECT
+
+ public:
+ /*! Creates a new alter table dialog. */
+ KexiTableDesignerView(KexiMainWindow *win, QWidget *parent);
+
+ virtual ~KexiTableDesignerView();
+
+ KexiTablePart::TempData* tempData() const;
+
+ /*! Clears field information entered for row.
+ This is performed by removing values from caption and data type columns.
+ Used by InsertFieldCommand to undo inserting a new field. */
+ virtual void clearRow(int row, bool addCommand = false);
+
+ /*! Inserts a new field with \a caption for \a row.
+ Property set is also created. */
+ virtual void insertField(int row, const QString& caption, bool addCommand = false);
+
+ /*! Inserts a new \a field for \a row.
+ Property set is also created. \a set will be deeply-copied into the new set.
+ Used by InsertFieldCommand to insert a new field. */
+ virtual void insertField(int row, KoProperty::Set& set, bool addCommand = false);
+
+ /*! Inserts a new empty row at position \a row.
+ Used by RemoveFieldCommand as a part of undo inserting a new field;
+ also used by InsertEmptyRowCommand. */
+ virtual void insertEmptyRow( int row, bool addCommand = false );
+
+ /*! Deletes \a row from the table view. Property set is also deleted.
+ All the subsequent fields are moved up. Used for undoing InsertEmptyRowCommand
+ and by RemoveFieldCommand to remove a field. */
+ virtual void deleteRow( int row, bool addCommand = false );
+
+ /*! Deletes a field for \a row. Property set is also deleted.
+ Used by RemoveFieldCommand to remove a field. */
+// virtual void deleteField( int row );
+
+ /*! Changes property \a propertyName to \a newValue for a field at row \a row.
+ If \a listData is not NULL and not empty, a deep copy of it is passed to Property::setListData().
+ If \a listData \a nlist if not NULL but empty, Property::setListData(0) is called. */
+ virtual void changeFieldPropertyForRow( int row,
+ const QCString& propertyName, const QVariant& newValue,
+ KoProperty::Property::ListData* const listData, bool addCommand );
+
+ /*! Changes property \a propertyName to \a newValue.
+ Works exactly like changeFieldPropertyForRow() except the field is pointed by \a fieldUID.
+ Used by ChangeFieldPropertyCommand to change field's property. */
+ void changeFieldProperty( int fieldUID, const QCString& propertyName,
+ const QVariant& newValue, KoProperty::Property::ListData* const listData = 0,
+ bool addCommand = false );
+
+ /*! Changes visibility of property \a propertyName to \a visible for a field pointed by \a fieldUID.
+ Used by ChangePropertyVisibilityCommand. */
+ void changePropertyVisibility( int fieldUID, const QCString& propertyName, bool visible );
+
+ /*! Builds table field's schema by looking at the \a set. */
+ KexiDB::Field * buildField( const KoProperty::Set &set ) const;
+
+ /*! Creates temporary table for the current design and returns debug string for it. */
+ virtual QString debugStringForCurrentTableSchema(tristate& result);
+
+ /*! Simulates execution of alter table, and puts debug into \a debugTarget.
+ A case when debugTarget is not 0 is true for the alter table test suite. */
+ virtual tristate simulateAlterTableExecution(QString *debugTarget);
+
+ public slots:
+ /*! Real execution of the Alter Table. For debugging of the real alter table.
+ \return true on success, false on failure and cancelled if user has cancelled
+ execution. */
+ virtual tristate executeRealAlterTable();
+
+ protected slots:
+ /*! Equivalent to updateActions(false). Called on row insert/delete
+ in a KexiDataAwarePropertySet. */
+ void updateActions();
+
+ virtual void slotUpdateRowActions(int row);
+
+ void slotAboutToShowContextMenu();
+
+ //! Called before cell change in tableview.
+ void slotBeforeCellChanged(KexiTableItem *item, int colnum,
+ QVariant& newValue, KexiDB::ResultInfo* result);
+
+ //! Called on row change in a tableview.
+ void slotRowUpdated(KexiTableItem *item);
+
+ //! Called before row inserting in tableview.
+ void slotRowInserted();
+// void slotAboutToInsertRow(KexiTableItem* item, KexiDB::ResultInfo* result, bool repaint);
+
+ //! Called before row deleting in tableview.
+ void slotAboutToDeleteRow(KexiTableItem& item, KexiDB::ResultInfo* result, bool repaint);
+
+ /*! Called after any property has been changed in the current property set,
+ to perform some actions (like updating other dependent properties) */
+ void slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property);
+
+ /*! Toggles primary key for currently selected field.
+ Does nothing for empty row. */
+ void slotTogglePrimaryKey();
+
+ /*! Undoes the recently performed action. */
+ void slotUndo();
+
+ /*! Redoes the recently undoed action. */
+ void slotRedo();
+
+ /*! Reaction on command execution from the command history */
+ void slotCommandExecuted(KCommand *command);
+
+ /*! Simulates real execution of the Alter Table. For debugging. */
+ void slotSimulateAlterTableExecution();
+
+ protected:
+ virtual void updateActions(bool activated);
+
+ //! called whenever data should be reloaded (on switching to this view mode)
+ void initData();
+
+ /*! Creates a new property set for \a field.
+ The property set will be asigned to \a row, and owned by this dialog.
+ If \a newOne is true, the property set will be marked as newly created.
+ \return newly created property set. */
+ KoProperty::Set* createPropertySet( int row, const KexiDB::Field& field, bool newOne = false );
+
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+
+ virtual tristate afterSwitchFrom(int mode);
+
+ /*! \return property set associated with currently selected row (i.e. field)
+ or 0 if current row is empty. */
+ virtual KoProperty::Set *propertySet();
+
+// void removeCurrentPropertySet();
+
+ /*! Reimplemented from KexiViewBase, because tables creation is more complex.
+ No table schema altering is required, so just buildSchema() is used to create a new schema.
+ */
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+
+ /*! Reimplemented from KexiViewBase, because table storage is more complex.
+ Table schema altering may be required, so just buildSchema() is used to create a new schema.
+ */
+ virtual tristate storeData(bool dontAsk = false);
+
+ /*! Builds table schema by looking at the current design. Used in storeNewData()
+ and storeData().
+ If \a beSilent is true, no message boxes are used to show questions or warnings.
+ This is used in the altertable test suite (kexi/tests/altertable).
+ \return true on successful schema creating, false on failure and cancelled when there
+ was a problem with user's design (and user has been informed about it). */
+ tristate buildSchema(KexiDB::TableSchema &schema, bool beSilent = false);
+
+ /*! Builds action list usable for KexiDB::AlterTableHandler by looking at undo buffer
+ of commands' history. Used in storeData() */
+ tristate buildAlterTableActions(KexiDB::AlterTableHandler::ActionList &actions);
+
+ /*! Helper, used for slotTogglePrimaryKey() and slotPropertyChanged().
+ Assigns primary key icon and value for property set \a propertySet,
+ and deselects it from previous pkey's row.
+ \a aWasPKey is internal.
+ If \a commandGroup is not 0, it is used as parent group for storing actions' history. */
+ void switchPrimaryKey(KoProperty::Set &propertySet, bool set, bool aWasPKey = false,
+ CommandGroup* commandGroup = 0);
+
+ //! Gets subtype strings and names for type \a fieldType.
+ void getSubTypeListData(KexiDB::Field::TypeGroup fieldTypeGroup,
+ QStringList& stringsList, QStringList& namesList);
+
+ /*! Adds history command \a command to the undo/redo buffer.
+ If \a execute is true, the command is executed afterwards. */
+ void addHistoryCommand( KCommand* command, bool execute );
+
+ //! Updates undo/redo shared actions availability by looking at command history's action
+ void updateUndoRedoActions();
+
+#ifdef KEXI_DEBUG_GUI
+ void debugCommand( KCommand* command, int nestingLevel );
+#endif
+
+ /*! Inserts a new \a field for \a row.
+ Property set is also created. If \a set is not 0 (the default),
+ it will be copied into the new set. Used by insertField(). */
+ void insertFieldInternal(int row, KoProperty::Set* set, const QString& caption, bool addCommand);
+
+ //! Reimplemented to pass the information also to the "Lookup" tab
+ virtual void propertySetSwitched();
+
+ /*! \return true if physical altering is needed for the current list of actions.
+ Used in KexiTableDesignerView::beforeSwitchTo() to avoid warning about removinf
+ table data if table recreating is not needed.
+ True is also returned if there is any trouble with getting the answer. */
+ bool isPhysicalAlteringNeeded();
+
+ private:
+ KexiTableDesignerViewPrivate *d;
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitabledesignerview_p.cpp b/kexi/plugins/tables/kexitabledesignerview_p.cpp
new file mode 100644
index 00000000..56ef997d
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview_p.cpp
@@ -0,0 +1,294 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2007 Jaroslaw Staniek <[email protected]>
+
+ 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 "kexitabledesignerview_p.h"
+#include "kexitabledesignerview.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qsplitter.h>
+
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+
+#include <koproperty/set.h>
+
+#include <kexidb/cursor.h>
+#include <kexidb/tableschema.h>
+#include <kexidb/connection.h>
+#include <kexidb/utils.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexidb/error.h>
+#include <kexiutils/identifier.h>
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include <widget/tableview/kexidataawarepropertyset.h>
+#include <widget/kexicustompropertyfactory.h>
+#include <kexiutils/utils.h>
+#include <kexidialogbase.h>
+#include <kexitableview.h>
+#include "kexitabledesignercommands.h"
+
+using namespace KexiTableDesignerCommands;
+
+//----------------------------------------------
+
+CommandHistory::CommandHistory(KActionCollection *actionCollection, bool withMenus)
+ : KCommandHistory(actionCollection, withMenus)
+{
+ // We need ALL the commands because we'll collect reuse their
+ // data before performing alter table, so set that to the maximum,
+ // as KCommandHistory has default = 50.
+ setUndoLimit(INT_MAX);
+ setRedoLimit(INT_MAX);
+}
+
+void CommandHistory::addCommand(KCommand *command, bool execute)
+{
+ KCommandHistory::addCommand(command, execute);
+ m_commandsToUndo.append(command);
+}
+
+void CommandHistory::undo()
+{
+ if (!m_commandsToUndo.isEmpty()) {
+ KCommand * cmd = m_commandsToUndo.take( m_commandsToUndo.count()-1 );
+ m_commandsToRedo.append( cmd );
+ }
+ KCommandHistory::undo();
+}
+
+void CommandHistory::redo()
+{
+ if (!m_commandsToRedo.isEmpty()) {
+ KCommand * cmd = m_commandsToRedo.take( m_commandsToRedo.count()-1 );
+ m_commandsToUndo.append( cmd );
+ }
+ KCommandHistory::redo();
+}
+
+void CommandHistory::clear() {
+ KCommandHistory::clear(); m_commandsToUndo.clear();
+}
+
+//----------------------------------------------
+
+KexiTableDesignerViewPrivate::KexiTableDesignerViewPrivate(KexiTableDesignerView* aDesignerView)
+ : designerView(aDesignerView)
+ , sets(0)
+ , uniqueIdCounter(0)
+ , dontAskOnStoreData(false)
+ , slotTogglePrimaryKeyCalled(false)
+ , primaryKeyExists(false)
+ , slotPropertyChanged_primaryKey_enabled(true)
+ , slotPropertyChanged_subType_enabled(true)
+ , addHistoryCommand_in_slotPropertyChanged_enabled(true)
+ , addHistoryCommand_in_slotRowUpdated_enabled(true)
+ , addHistoryCommand_in_slotAboutToDeleteRow_enabled(true)
+ , addHistoryCommand_in_slotRowInserted_enabled(true)
+ , slotBeforeCellChanged_enabled(true)
+ , tempStoreDataUsingRealAlterTable(false)
+{
+ historyActionCollection = new KActionCollection((QWidget*)0,"");
+ history = new CommandHistory(historyActionCollection, true);
+
+ internalPropertyNames.insert("subType",(char*)1);
+ internalPropertyNames.insert("uid",(char*)1);
+ internalPropertyNames.insert("newrow",(char*)1);
+ internalPropertyNames.insert("rowSource",(char*)1);
+ internalPropertyNames.insert("rowSourceType",(char*)1);
+ internalPropertyNames.insert("boundColumn",(char*)1);
+ internalPropertyNames.insert("visibleColumn",(char*)1);
+}
+
+KexiTableDesignerViewPrivate::~KexiTableDesignerViewPrivate() {
+ delete sets;
+ delete historyActionCollection;
+ delete history;
+}
+
+int KexiTableDesignerViewPrivate::generateUniqueId()
+{
+ return ++uniqueIdCounter;
+}
+
+void KexiTableDesignerViewPrivate::setPropertyValueIfNeeded(
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, const QVariant& oldValue, CommandGroup* commandGroup,
+ bool forceAddCommand, bool rememberOldValue,
+ QStringList* const slist, QStringList* const nlist)
+{
+ KoProperty::Property& property = set[propertyName];
+
+ KoProperty::Property::ListData *oldListData = property.listData() ?
+ new KoProperty::Property::ListData(*property.listData()) : 0; //remember because we'll change list data soon
+ if (slist && nlist) {
+ if (slist->isEmpty() || nlist->isEmpty()) {
+ property.setListData(0);
+ }
+ else {
+ property.setListData(*slist, *nlist);
+ }
+ }
+ if (oldValue.type() == newValue.type()
+ && (oldValue == newValue || (!oldValue.isValid() && !newValue.isValid()))
+ && !forceAddCommand)
+ {
+ return;
+ }
+
+ const bool prev_addHistoryCommand_in_slotPropertyChanged_enabled
+ = addHistoryCommand_in_slotPropertyChanged_enabled; //remember
+ addHistoryCommand_in_slotPropertyChanged_enabled = false;
+ if (property.value() != newValue)
+ property.setValue( newValue, rememberOldValue );
+ if (commandGroup) {
+ commandGroup->addCommand(
+ new ChangeFieldPropertyCommand( designerView, set, propertyName, oldValue, newValue,
+ oldListData, property.listData()) );
+ }
+ delete oldListData;
+ addHistoryCommand_in_slotPropertyChanged_enabled
+ = prev_addHistoryCommand_in_slotPropertyChanged_enabled; //restore
+}
+
+void KexiTableDesignerViewPrivate::setPropertyValueIfNeeded(
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, CommandGroup* commandGroup,
+ bool forceAddCommand, bool rememberOldValue,
+ QStringList* const slist, QStringList* const nlist)
+{
+ KoProperty::Property& property = set[propertyName];
+ QVariant oldValue( property.value() );
+ setPropertyValueIfNeeded( set, propertyName, newValue, property.value(),
+ commandGroup, forceAddCommand, rememberOldValue, slist, nlist);
+}
+
+void KexiTableDesignerViewPrivate::setVisibilityIfNeeded( const KoProperty::Set& set, KoProperty::Property* prop,
+ bool visible, bool &changed, CommandGroup *commandGroup )
+{
+ if (prop->isVisible() != visible) {
+ if (commandGroup) {
+ commandGroup->addCommand(
+ new ChangePropertyVisibilityCommand( designerView, set, prop->name(), visible ) );
+ }
+ prop->setVisible( visible );
+ changed = true;
+ }
+}
+
+bool KexiTableDesignerViewPrivate::updatePropertiesVisibility(KexiDB::Field::Type fieldType, KoProperty::Set &set,
+ CommandGroup *commandGroup)
+{
+ bool changed = false;
+ KoProperty::Property *prop;
+ bool visible;
+
+ prop = &set["subType"];
+ kexipluginsdbg << "subType=" << prop->value().toInt() << " type=" << set["type"].value().toInt()<< endl;
+
+ //if there is no more than 1 subType name or it's a PK: hide the property
+ visible = (prop->listData() && prop->listData()->keys.count() > 1 /*disabled || isObjectTypeGroup*/)
+ && set["primaryKey"].value().toBool()==false;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["objectType"];
+ const bool isObjectTypeGroup = set["type"].value().toInt() == (int)KexiDB::Field::BLOB; // used only for BLOBs
+ visible = isObjectTypeGroup;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["unsigned"];
+ visible = KexiDB::Field::isNumericType(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["length"];
+ visible = (fieldType == KexiDB::Field::Text);
+ if (prop->isVisible()!=visible) {
+// prop->setVisible( visible );
+ //update the length when it makes sense
+ const int lengthToSet = visible ? KexiDB::Field::defaultTextLength() : 0;
+ setPropertyValueIfNeeded( set, "length", lengthToSet,
+ commandGroup, false, false /*!rememberOldValue*/ );
+// if (lengthToSet != prop->value().toInt())
+// prop->setValue( lengthToSet, false );
+// changed = true;
+ }
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+#ifndef KEXI_NO_UNFINISHED
+ prop = &set["precision"];
+ visible = KexiDB::Field::isFPNumericType(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+#endif
+ prop = &set["visibleDecimalPlaces"];
+ visible = KexiDB::supportsVisibleDecimalPlacesProperty(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["unique"];
+ visible = fieldType != KexiDB::Field::BLOB;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["indexed"];
+ visible = fieldType != KexiDB::Field::BLOB;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["allowEmpty"];
+ visible = KexiDB::Field::hasEmptyProperty(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+ prop = &set["autoIncrement"];
+ visible = KexiDB::Field::isAutoIncrementAllowed(fieldType);
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+
+//! @todo remove this when BLOB supports default value
+#ifdef KEXI_NO_UNFINISHED
+ prop = &set["defaultValue"];
+ visible = !isObjectTypeGroup;
+ setVisibilityIfNeeded( set, prop, visible, changed, commandGroup );
+#endif
+
+ return changed;
+}
+
+QString KexiTableDesignerViewPrivate::messageForSavingChanges(bool &emptyTable, bool skipWarning)
+{
+ KexiDB::Connection *conn = designerView->mainWin()->project()->dbConnection();
+ bool ok;
+ emptyTable = conn->isEmpty( *designerView->tempData()->table, ok ) && ok;
+ return i18n("Do you want to save the design now?")
+ + ( (emptyTable || skipWarning) ? QString::null :
+ (QString("\n\n") + designerView->part()->i18nMessage(":additional message before saving design",
+ designerView->parentDialog())) );
+}
+
+void KexiTableDesignerViewPrivate::updateIconForItem(KexiTableItem &item, KoProperty::Set& set)
+{
+ QVariant icon;
+ if (!set["rowSource"].value().toString().isEmpty() && !set["rowSourceType"].value().toString().isEmpty())
+ icon = "combo";
+ //show/hide icon in the table
+ view->data()->clearRowEditBuffer();
+ view->data()->updateRowEditBuffer(&item, COLUMN_ID_ICON, icon);
+ view->data()->saveRowChanges(item, true);
+}
+
+#include "kexitabledesignerview_p.moc"
diff --git a/kexi/plugins/tables/kexitabledesignerview_p.h b/kexi/plugins/tables/kexitabledesignerview_p.h
new file mode 100644
index 00000000..f5650e74
--- /dev/null
+++ b/kexi/plugins/tables/kexitabledesignerview_p.h
@@ -0,0 +1,191 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004-2006 Jaroslaw Staniek <[email protected]>
+
+ 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 KEXIALTERTABLEDIALOG_P_H
+#define KEXIALTERTABLEDIALOG_P_H
+
+#include "kexitabledesignerview.h"
+#include <kcommand.h>
+
+class KexiDataAwarePropertySet;
+
+//! @internal indices for table columns
+#define COLUMN_ID_ICON 0
+#define COLUMN_ID_CAPTION 1
+#define COLUMN_ID_TYPE 2
+#define COLUMN_ID_DESC 3
+
+/*! @internal
+ Command group, reimplemented to get access to commands().
+ We need it to iterate through commands so we can perform a set of ALTER TABLE atomic actions. */
+class CommandGroup : public KMacroCommand
+{
+ public:
+ CommandGroup( const QString & name )
+ : KMacroCommand(name)
+ {}
+ virtual ~CommandGroup() {}
+ const QPtrList<KCommand>& commands() const { return m_commands; }
+};
+
+/*! @internal
+ Command history, reimplemented to get access to commands().
+ We need it to iterate through commands so we can perform a set of ALTER TABLE atomic actions. */
+class CommandHistory : public KCommandHistory
+{
+ Q_OBJECT
+ public:
+ CommandHistory(KActionCollection *actionCollection, bool withMenus = true);
+
+ const QPtrList<KCommand>& commands() const { return m_commandsToUndo; }
+
+ void addCommand(KCommand *command, bool execute = true);
+
+ void clear();
+
+ public slots:
+ virtual void undo();
+ virtual void redo();
+
+ protected:
+ QPtrList<KCommand> m_commandsToUndo, m_commandsToRedo;
+};
+
+//----------------------------------------------
+
+//! @internal
+class KexiTableDesignerViewPrivate
+{
+ public:
+ KexiTableDesignerViewPrivate(KexiTableDesignerView* aDesignerView);
+ ~KexiTableDesignerViewPrivate();
+
+ int generateUniqueId();
+
+ /*! @internal
+ Sets property \a propertyName in property set \a set to \a newValue.
+ If \a commandGroup is not 0, a new ChangeFieldPropertyCommand object is added there as well.
+ While setting the new value, addHistoryCommand_in_slotPropertyChanged_enabled is set to false,
+ so addHistoryCommand() wont be executed in slotPropertyChanged() as an answer to setting
+ the property.
+
+ If \a forceAddCommand is false (the default) and \a newValue does not differ from curent property value
+ (set[propertyName].value()), ChangeFieldPropertyCommand command is not added to the \a commandGroup.
+ Otherwise, command is always added.
+
+ \a rememberOldValue argument is passed to Property::setValue()
+
+ If \a slist and \a nlist if not NULL and not empty, these are passed to Property::setListData().
+ If \a slist and \a nlist if not NULL but empty, Property::setListData(0) is called.
+
+ addHistoryCommand_in_slotPropertyChanged_enabled is then set back to the original state.
+ */
+ void setPropertyValueIfNeeded( const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, CommandGroup* commandGroup,
+ bool forceAddCommand = false, bool rememberOldValue = true,
+ QStringList* const slist = 0, QStringList* const nlist = 0);
+
+ /*! Like above but allows to specify \a oldValue. */
+ void setPropertyValueIfNeeded(
+ const KoProperty::Set& set, const QCString& propertyName,
+ const QVariant& newValue, const QVariant& oldValue, CommandGroup* commandGroup,
+ bool forceAddCommand = false, bool rememberOldValue = true,
+ QStringList* const slist = 0, QStringList* const nlist = 0);
+
+ /*! @internal
+ Used in updatePropertiesVisibility().
+ Does nothing if visibility should not be changed, i.e. when prop->isVisible()==visible,
+ otherwise sets changed to true and sets visibility of property \a prop to \a visible.
+ */
+ void setVisibilityIfNeeded( const KoProperty::Set& set, KoProperty::Property* prop,
+ bool visible, bool &changed, CommandGroup *commandGroup );
+
+ bool updatePropertiesVisibility(KexiDB::Field::Type fieldType, KoProperty::Set &set,
+ CommandGroup *commandGroup = 0);
+
+ /*! \return message used to ask user for accepting saving the design.
+ \a emptyTable is set to true if the table designed contains no rows.
+ If \a skipWarning is true, no warning about data loss is appended (useful when
+ only non-physical altering actions will be performed). */
+ QString messageForSavingChanges(bool &emptyTable, bool skipWarning = false);
+
+ /*! Updates icon in the first column, depending on property set \a set.
+ For example, when "rowSource" and "rowSourceType" propertiesa are not empty,
+ "combo" icon appears. */
+ void updateIconForItem(KexiTableItem &item, KoProperty::Set& set);
+
+ KexiTableDesignerView* designerView;
+
+ KexiTableView *view; //!< helper
+
+ KexiTableViewData *data;
+
+ KexiDataAwarePropertySet *sets;
+
+ int row; //!< used to know if a new row is selected in slotCellSelected()
+
+ KToggleAction *action_toggle_pkey;
+
+ KPopupTitle *contextMenuTitle;
+
+ int uniqueIdCounter;
+
+ //! internal
+ int maxTypeNameTextWidth;
+ //! Set to true in beforeSwitchTo() to avoid asking again in storeData()
+ bool dontAskOnStoreData : 1;
+
+ bool slotTogglePrimaryKeyCalled : 1;
+
+ bool primaryKeyExists : 1;
+ //! Used in slotPropertyChanged() to avoid infinite recursion
+ bool slotPropertyChanged_primaryKey_enabled : 1;
+ //! Used in slotPropertyChanged() to avoid infinite recursion
+ bool slotPropertyChanged_subType_enabled : 1;
+ //! used in slotPropertyChanged() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotPropertyChanged_enabled : 1;
+ //! used in slotRowUpdated() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotRowUpdated_enabled : 1;
+ //! used in slotAboutToDeleteRow() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotAboutToDeleteRow_enabled : 1;
+ //! used in slotRowInserted() to disable addHistoryCommand()
+ bool addHistoryCommand_in_slotRowInserted_enabled : 1;
+
+ //! used to disable slotBeforeCellChanged()
+ bool slotBeforeCellChanged_enabled : 1;
+
+//! @tood temp; remove this:
+ //! Temporary flag, used for testingu the Alter Table machinery. Affects storeData()
+ //! Used in slotExecuteRealAlterTable() to switch on real alter table for a while.
+ bool tempStoreDataUsingRealAlterTable : 1;
+
+ /*! Set to a recent result of calling \ref tristate KexiTableDesignerView::storeData(bool dontAsk).
+ Then, it is used in \ref void KexiTableDesignerView::executeRealAlterTable()
+ to know what return value should be. */
+ tristate recentResultOfStoreData;
+
+ KActionCollection* historyActionCollection;
+ CommandHistory* history;
+
+ //! A cache used in KexiTableDesignerView::buildField() to quickly identify
+ //! properties internal to the designer
+ QAsciiDict<char> internalPropertyNames;
+};
+
+#endif
diff --git a/kexi/plugins/tables/kexitablehandler.desktop b/kexi/plugins/tables/kexitablehandler.desktop
new file mode 100644
index 00000000..8491b7a3
--- /dev/null
+++ b/kexi/plugins/tables/kexitablehandler.desktop
@@ -0,0 +1,118 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Tables
+GenericName[bg]=Таблици
+GenericName[br]=Taolennoù
+GenericName[ca]=Taules
+GenericName[cs]=Tabulky
+GenericName[cy]=Tablau
+GenericName[da]=Tabeller
+GenericName[de]=Tabellen
+GenericName[el]=Πίνακες
+GenericName[eo]=Tabeloj
+GenericName[es]=Tablas
+GenericName[et]=Tabelid
+GenericName[eu]=Taulak
+GenericName[fa]=جدولها
+GenericName[fi]=Taulukot
+GenericName[fr]=Tableaux
+GenericName[fy]=Tabellen
+GenericName[ga]=Táblaí
+GenericName[gl]=Táboas
+GenericName[he]=טבלאות
+GenericName[hi]=तालिका
+GenericName[hr]=Tablice
+GenericName[hu]=Táblák
+GenericName[is]=Töflur
+GenericName[it]=Tabelle
+GenericName[ja]=テーブル
+GenericName[km]=តារាង​
+GenericName[lt]=Lentelės
+GenericName[lv]=Tabulas
+GenericName[ms]=Jadual
+GenericName[nb]=Tabeller
+GenericName[nds]=Tabellen
+GenericName[ne]=तालिका
+GenericName[nl]=Tabellen
+GenericName[nn]=Tabellar
+GenericName[pl]=Tabele
+GenericName[pt]=Tabelas
+GenericName[pt_BR]=Tabelas
+GenericName[ru]=Таблицы
+GenericName[se]=Tabeallat
+GenericName[sk]=Tabuľky
+GenericName[sl]=Tabele
+GenericName[sr]=Табеле
+GenericName[sr@Latn]=Tabele
+GenericName[sv]=Tabeller
+GenericName[ta]=அட்டவணைகள்
+GenericName[tr]=Tablolar
+GenericName[uk]=Таблиці
+GenericName[uz]=Jadvallar
+GenericName[uz@cyrillic]=Жадваллар
+GenericName[zh_CN]=表
+GenericName[zh_TW]=表格
+Name=Tables
+Name[bg]=Таблици
+Name[br]=Taolennoù
+Name[ca]=Taules
+Name[cs]=Tabulky
+Name[cy]=Tablau
+Name[da]=Tabeller
+Name[de]=Tabellen
+Name[el]=Πίνακες
+Name[eo]=Tabeloj
+Name[es]=Tablas
+Name[et]=Tabelid
+Name[eu]=Taulak
+Name[fa]=جدولها
+Name[fi]=Taulukot
+Name[fr]=Tableaux
+Name[fy]=Tabellen
+Name[ga]=Táblaí
+Name[gl]=Táboas
+Name[he]=טבלאות
+Name[hi]=टेबल्स
+Name[hr]=Tablice
+Name[hu]=Táblák
+Name[is]=Töflur
+Name[it]=Tabelle
+Name[ja]=テーブル
+Name[km]=តារាង​
+Name[lt]=Lentelės
+Name[lv]=Tabulas
+Name[ms]=Jadual
+Name[nb]=Tabeller
+Name[nds]=Tabellen
+Name[ne]=तालिकाहरू
+Name[nl]=Tabellen
+Name[nn]=Tabellar
+Name[pl]=Tabele
+Name[pt]=Tabelas
+Name[pt_BR]=Tabelas
+Name[ru]=Таблицы
+Name[se]=Tabeallat
+Name[sk]=Tabuľky
+Name[sl]=Tabele
+Name[sr]=Табеле
+Name[sr@Latn]=Tabele
+Name[sv]=Tabeller
+Name[ta]=அட்டவணை
+Name[tg]=Ҷадвалҳо
+Name[tr]=Tablolar
+Name[uk]=Таблиці
+Name[uz]=Jadvallar
+Name[uz@cyrillic]=Жадваллар
+Name[wa]=Tåvleas
+Name[zh_CN]=表
+Name[zh_TW]=表格
+X-KDE-Library=kexihandler_table
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=table
+X-Kexi-TypeMime=kexi/table
+X-Kexi-ItemIcon=table
+X-Kexi-SupportsDataExport=true
+X-Kexi-SupportsPrinting=true
diff --git a/kexi/plugins/tables/kexitablepart.cpp b/kexi/plugins/tables/kexitablepart.cpp
new file mode 100644
index 00000000..3d09a81e
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepart.cpp
@@ -0,0 +1,313 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <[email protected]>
+ Copyright (C) 2002, 2003 Joseph Wenninger <[email protected]>
+ Copyright (C) 2004 Jaroslaw Staniek <[email protected]>
+
+ 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 "kexitablepart.h"
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <ktabwidget.h>
+#include <kiconloader.h>
+
+#include "keximainwindow.h"
+#include "kexiproject.h"
+#include "kexipartinfo.h"
+#include "widget/kexidatatable.h"
+#include "widget/tableview/kexidatatableview.h"
+#include "kexitabledesignerview.h"
+#include "kexitabledesigner_dataview.h"
+#include "kexilookupcolumnpage.h"
+
+#include <kexidb/connection.h>
+#include <kexidb/cursor.h>
+#include <kexidialogbase.h>
+
+//! @internal
+class KexiTablePart::Private
+{
+ public:
+ Private()
+ {
+ }
+ ~Private()
+ {
+ delete static_cast<KexiLookupColumnPage*>(lookupColumnPage);
+ }
+ QGuardedPtr<KexiLookupColumnPage> lookupColumnPage;
+};
+
+KexiTablePart::KexiTablePart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+ , d(new Private())
+{
+ // REGISTERED ID:
+ m_registeredPartID = (int)KexiPart::TableObjectType;
+
+ kdDebug() << "KexiTablePart::KexiTablePart()" << 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.",
+ "table");
+ m_names["instanceCaption"] = i18n("Table");
+ m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode;
+//js TODO: also add Kexi::TextViewMode when we'll have SQL ALTER TABLE EDITOR!!!
+}
+
+KexiTablePart::~KexiTablePart()
+{
+ delete d;
+}
+
+void KexiTablePart::initPartActions()
+{
+}
+
+void KexiTablePart::initInstanceActions()
+{
+//moved to main window createSharedAction(Kexi::DataViewMode, i18n("Filter"), "filter", 0, "tablepart_filter");
+
+ KAction *a = createSharedToggleAction(
+ Kexi::DesignViewMode, i18n("Primary Key"), "key", 0, "tablepart_toggle_pkey");
+// Kexi::DesignViewMode, i18n("Toggle Primary Key"), "key", 0, "tablepart_toggle_pkey");
+ a->setWhatsThis(i18n("Sets or removes primary key for currently selected field."));
+}
+
+KexiDialogTempData* KexiTablePart::createTempData(KexiDialogBase* dialog)
+{
+ return new KexiTablePart::TempData(dialog);
+}
+
+KexiViewBase* KexiTablePart::createView(QWidget *parent, KexiDialogBase* dialog,
+ KexiPart::Item &item, int viewMode, QMap<QString,QString>*)
+{
+ KexiMainWindow *win = dialog->mainWin();
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return 0;
+
+
+ KexiTablePart::TempData *temp = static_cast<KexiTablePart::TempData*>(dialog->tempData());
+ if (!temp->table) {
+ temp->table = win->project()->dbConnection()->tableSchema(item.name());
+ kdDebug() << "KexiTablePart::execute(): schema is " << temp->table << endl;
+ }
+
+ if (viewMode == Kexi::DesignViewMode) {
+ KexiTableDesignerView *t = new KexiTableDesignerView(win, parent);
+ return t;
+ }
+ else if (viewMode == Kexi::DataViewMode) {
+ if(!temp->table)
+ return 0; //todo: message
+ //we're not setting table schema here -it will be forced to set
+ // in KexiTableDesigner_DataView::afterSwitchFrom()
+ KexiTableDesigner_DataView *t = new KexiTableDesigner_DataView(win, parent);
+ return t;
+ }
+ return 0;
+}
+
+bool KexiTablePart::remove(KexiMainWindow *win, KexiPart::Item &item)
+{
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return false;
+
+ KexiDB::Connection *conn = win->project()->dbConnection();
+ KexiDB::TableSchema *sch = conn->tableSchema(item.identifier());
+
+ if (sch) {
+ tristate res = KexiTablePart::askForClosingObjectsUsingTableSchema(
+ win, *conn, *sch,
+ i18n("You are about to remove table \"%1\" but following objects using this table are opened:")
+ .arg(sch->name()));
+ return true == conn->dropTable( sch );
+ }
+ //last chance: just remove item
+ return conn->removeObject( item.identifier() );
+}
+
+tristate KexiTablePart::rename(KexiMainWindow *win, KexiPart::Item & item,
+ const QString& newName)
+{
+//TODO: what about objects (queries/forms) that use old name?
+ KexiDB::Connection *conn = win->project()->dbConnection();
+ KexiDB::TableSchema *sch = conn->tableSchema(item.identifier());
+ if (!sch)
+ return false;
+ return conn->alterTableName(*sch, newName);
+}
+
+KexiDB::SchemaData*
+KexiTablePart::loadSchemaData(KexiDialogBase *dlg, const KexiDB::SchemaData& sdata, int viewMode)
+{
+ Q_UNUSED( viewMode );
+
+ return dlg->mainWin()->project()->dbConnection()->tableSchema( sdata.name() );
+}
+
+#if 0
+KexiPart::DataSource *
+KexiTablePart::dataSource()
+{
+ return new KexiTableDataSource(this);
+}
+#endif
+
+tristate KexiTablePart::askForClosingObjectsUsingTableSchema(QWidget *parent, KexiDB::Connection& conn,
+ KexiDB::TableSchema& table, const QString& msg)
+{
+ QPtrList<KexiDB::Connection::TableSchemaChangeListenerInterface>* listeners
+ = conn.tableSchemaChangeListeners(table);
+ if (!listeners || listeners->isEmpty())
+ return true;
+
+ QString openedObjectsStr = "<ul>";
+ for (QPtrListIterator<KexiDB::Connection::TableSchemaChangeListenerInterface> it(*listeners);
+ it.current(); ++it) {
+ openedObjectsStr += QString("<li>%1</li>").arg(it.current()->listenerInfoString);
+ }
+ openedObjectsStr += "</ul>";
+ int r = KMessageBox::questionYesNo(parent,
+ "<p>"+msg+"</p><p>"+openedObjectsStr+"</p><p>"
+ +i18n("Do you want to close all windows for these objects?"),
+ QString::null, KGuiItem(i18n("Close windows"),"fileclose"), KStdGuiItem::cancel());
+ tristate res;
+ if (r == KMessageBox::Yes) {
+ //try to close every window
+ res = conn.closeAllTableSchemaChangeListeners(table);
+ if (res!=true) //do not expose closing errors twice; just cancel
+ res = cancelled;
+ }
+ else
+ res = cancelled;
+
+ return res;
+}
+
+QString
+KexiTablePart::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const
+{
+ if (englishMessage=="Design of object \"%1\" has been modified.")
+ return i18n("Design of table \"%1\" has been modified.");
+
+ if (englishMessage=="Object \"%1\" already exists.")
+ return i18n("Table \"%1\" already exists.");
+
+ if (dlg->currentViewMode()==Kexi::DesignViewMode && !dlg->neverSaved()
+ && englishMessage==":additional message before saving design")
+ return i18n("Warning! Any data in this table will be removed upon design's saving!");
+
+ return englishMessage;
+}
+
+void KexiTablePart::setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin)
+{
+ if (!d->lookupColumnPage) {
+ d->lookupColumnPage = new KexiLookupColumnPage(0);
+ connect(d->lookupColumnPage, SIGNAL(jumpToObjectRequested(const QCString&, const QCString&)),
+ mainWin, SLOT(highlightObject(const QCString&, const QCString&)));
+
+//! @todo add "Table" tab
+
+ /*
+ 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->lookupColumnPage->setProject(prj);
+
+//! @todo add lookup field icon
+ tab->addTab( d->lookupColumnPage, SmallIconSet("combo"), "");
+ tab->setTabToolTip( d->lookupColumnPage, i18n("Lookup column"));
+}
+
+KexiLookupColumnPage* KexiTablePart::lookupColumnPage() const
+{
+ return d->lookupColumnPage;
+}
+
+//----------------
+
+#if 0
+KexiTableDataSource::KexiTableDataSource(KexiPart::Part *part)
+ : KexiPart::DataSource(part)
+{
+}
+
+KexiTableDataSource::~KexiTableDataSource()
+{
+}
+
+KexiDB::FieldList *
+KexiTableDataSource::fields(KexiProject *project, const KexiPart::Item &it)
+{
+ kdDebug() << "KexiTableDataSource::fields(): " << it.name() << endl;
+ return project->dbConnection()->tableSchema(it.name());
+}
+
+KexiDB::Cursor *
+KexiTableDataSource::cursor(KexiProject * /*project*/,
+ const KexiPart::Item &/*it*/, bool /*buffer*/)
+{
+ return 0;
+}
+#endif
+
+//----------------
+
+KexiTablePart::TempData::TempData(QObject* parent)
+ : KexiDialogTempData(parent)
+ , table(0)
+ , tableSchemaChangedInPreviousView(true /*to force reloading on startup*/ )
+{
+}
+
+//----------------
+
+/**
+TODO
+*/
+/*
+AboutData( const char *programName,
+ const char *version,
+ const char *i18nShortDescription = 0,
+ int licenseType = License_Unknown,
+ const char *i18nCopyrightStatement = 0,
+ const char *i18nText = 0,
+ const char *homePageAddress = 0,
+ const char *bugsEmailAddress = "[email protected]"
+);
+
+#define KEXIPART_EXPORT_FACTORY( libname, partClass, aboutData ) \
+ static KexiPart::AboutData * libname ## updateAD(KexiPart::AboutData *ad) \
+ { ad->setAppName( #libname ); return ad; } \
+ K_EXPORT_COMPONENT_FACTORY( libname, KGenericFactory<partClass>(libname ## updateAD(#libname)) )
+*/
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_table, KGenericFactory<KexiTablePart>("kexihandler_table") )
+
+#include "kexitablepart.moc"
+
diff --git a/kexi/plugins/tables/kexitablepart.h b/kexi/plugins/tables/kexitablepart.h
new file mode 100644
index 00000000..e4b060ad
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepart.h
@@ -0,0 +1,100 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <[email protected]>
+ Copyright (C) 2002, 2003 Joseph Wenninger <[email protected]>
+ Copyright (C) 2004 Jaroslaw Staniek <[email protected]>
+
+ 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 KEXITABLEPART_H
+#define KEXITABLEPART_H
+
+#include <kexi.h>
+#include <kexipart.h>
+#include <kexidialogbase.h>
+//#include <kexipartdatasource.h>
+#include <kexipartitem.h>
+#include <kexidb/fieldlist.h>
+
+class KexiMainWin;
+class KexiLookupColumnPage;
+
+class KexiTablePart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+ KexiTablePart(QObject *parent, const char *name, const QStringList &);
+ virtual ~KexiTablePart();
+
+ virtual bool remove(KexiMainWindow *win, KexiPart::Item &item);
+
+ virtual tristate rename(KexiMainWindow *win, KexiPart::Item &item,
+ const QString& newName);
+
+// virtual KexiPart::DataSource *dataSource();
+
+ class TempData : public KexiDialogTempData
+ {
+ public:
+ TempData(QObject* parent);
+ KexiDB::TableSchema *table;
+ /*! true, if \a table member has changed in previous view. Used on view switching.
+ We're checking this flag to see if we should refresh data for DataViewMode. */
+ bool tableSchemaChangedInPreviousView : 1;
+ };
+
+ static tristate askForClosingObjectsUsingTableSchema(
+ QWidget *parent, KexiDB::Connection& conn,
+ KexiDB::TableSchema& table, const QString& msg);
+
+ virtual QString i18nMessage(const QCString& englishMessage,
+ KexiDialogBase* dlg) const;
+
+ KexiLookupColumnPage* lookupColumnPage() 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);
+
+ virtual KexiDB::SchemaData* loadSchemaData(KexiDialogBase *dlg, const KexiDB::SchemaData& sdata, int viewMode);
+
+ private:
+ class Private;
+ Private* d;
+};
+
+#if 0
+class KexiTableDataSource : public KexiPart::DataSource
+{
+ public:
+ KexiTableDataSource(KexiPart::Part *part);
+ ~KexiTableDataSource();
+
+ virtual KexiDB::FieldList *fields(KexiProject *project, const KexiPart::Item &item);
+ virtual KexiDB::Cursor *cursor(KexiProject *project, const KexiPart::Item &item, bool buffer);
+};
+#endif
+
+#endif
+
diff --git a/kexi/plugins/tables/kexitablepartinstui.rc b/kexi/plugins/tables/kexitablepartinstui.rc
new file mode 100644
index 00000000..e96a5976
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepartinstui.rc
@@ -0,0 +1,18 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexitablepartinst" version="6">
+
+<MenuBar>
+ <Menu name="edit" noMerge="0">
+ <text>&amp;Edit</text>
+ <Separator/>
+ <Action name="tablepart_toggle_pkey"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="designToolBar" fullWidth="false" noMerge="0">
+ <text>Design</text>
+ <!-- Design View -->
+ <!-- TODO: reenable after shared toggle actions fix: Action name="tablepart_toggle_pkey"/ -->
+</ToolBar>
+
+</kpartgui>
diff --git a/kexi/plugins/tables/kexitablepartui.rc b/kexi/plugins/tables/kexitablepartui.rc
new file mode 100644
index 00000000..c78b2587
--- /dev/null
+++ b/kexi/plugins/tables/kexitablepartui.rc
@@ -0,0 +1,7 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexitablepart" version="5">
+
+<MenuBar>
+</MenuBar>
+
+</kpartgui>