summaryrefslogtreecommitdiffstats
path: root/kexi/plugins/queries
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/queries
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/queries')
-rw-r--r--kexi/plugins/queries/Makefile.am29
-rw-r--r--kexi/plugins/queries/kexiaddparamdialog.cpp47
-rw-r--r--kexi/plugins/queries/kexiaddparamdialog.h40
-rw-r--r--kexi/plugins/queries/kexiaddparamwidget.ui135
-rw-r--r--kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp63
-rw-r--r--kexi/plugins/queries/kexidynamicqueryparameterdialog.h45
-rw-r--r--kexi/plugins/queries/kexiparameterlisteditor.ui88
-rw-r--r--kexi/plugins/queries/kexiquerydesignerguieditor.cpp1803
-rw-r--r--kexi/plugins/queries/kexiquerydesignerguieditor.h170
-rw-r--r--kexi/plugins/queries/kexiquerydesignersql.cpp542
-rw-r--r--kexi/plugins/queries/kexiquerydesignersql.h82
-rw-r--r--kexi/plugins/queries/kexiquerydesignersqlhistory.cpp373
-rw-r--r--kexi/plugins/queries/kexiquerydesignersqlhistory.h104
-rw-r--r--kexi/plugins/queries/kexiqueryhandler.desktop111
-rw-r--r--kexi/plugins/queries/kexiquerypart.cpp310
-rw-r--r--kexi/plugins/queries/kexiquerypart.h118
-rw-r--r--kexi/plugins/queries/kexiquerypartinstui.rc24
-rw-r--r--kexi/plugins/queries/kexiquerypartui.rc11
-rw-r--r--kexi/plugins/queries/kexiqueryview.cpp154
-rw-r--r--kexi/plugins/queries/kexiqueryview.h58
20 files changed, 4307 insertions, 0 deletions
diff --git a/kexi/plugins/queries/Makefile.am b/kexi/plugins/queries/Makefile.am
new file mode 100644
index 00000000..c0b620d4
--- /dev/null
+++ b/kexi/plugins/queries/Makefile.am
@@ -0,0 +1,29 @@
+include $(top_srcdir)/kexi/Makefile.global
+
+kde_module_LTLIBRARIES = kexihandler_query.la
+
+kexihandler_query_la_SOURCES = kexiquerypart.cpp kexiquerydesignersql.cpp \
+ kexiquerydesignersqlhistory.cpp kexiquerydesignerguieditor.cpp \
+ kexiqueryview.cpp
+kexihandler_query_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module -no-undefined
+kexihandler_query_la_LIBADD = ../../core/libkexicore.la \
+ $(top_builddir)/kexi/kexidb/libkexidb.la \
+ $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \
+ $(top_builddir)/kexi/widget/tableview/libkexidatatable.la \
+ $(top_builddir)/kexi/widget/relations/libkexirelationsview.la \
+ $(top_builddir)/lib/koproperty/libkoproperty.la
+
+INCLUDES= -I$(top_srcdir)/kexi/core -I$(top_srcdir)/kexi \
+ -I$(top_srcdir)/kexi/widget -I$(top_srcdir)/kexi/widget/tableview \
+ -I$(top_srcdir)/lib -I$(top_srcdir)/lib/kofficecore \
+ -I$(top_srcdir)/kexi/kexidb $(all_includes)
+
+servicesdir=$(kde_servicesdir)/kexi
+services_DATA=kexiqueryhandler.desktop
+
+rcdir = $(kde_datadir)/kexi
+rc_DATA = kexiquerypartui.rc kexiquerypartinstui.rc
+
+METASOURCES = AUTO
+
+include ../Makefile.common
diff --git a/kexi/plugins/queries/kexiaddparamdialog.cpp b/kexi/plugins/queries/kexiaddparamdialog.cpp
new file mode 100644
index 00000000..fb40f9a2
--- /dev/null
+++ b/kexi/plugins/queries/kexiaddparamdialog.cpp
@@ -0,0 +1,47 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ 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 <klocale.h>
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <qvbox.h>
+#include <kexidataprovider.h>
+#include "kexiaddparamdialog.h"
+#include "kexiaddparamdialog.moc"
+#include "kexiaddparamwidget.h"
+
+KexiAddParamDialog::KexiAddParamDialog(QWidget *parent)
+ : KDialogBase(parent, "kexiaddparamdialog", true, i18n("Add Parameter"), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true)
+{
+ m_wid=new KexiAddParamWidget(makeVBoxMainWidget());
+ for (int i=1;i<=KexiDataProvider::Parameter::maxType;i++)
+ m_wid->typecombo->insertItem(KexiDataProvider::Parameter::typeDescription[i]);
+}
+
+KexiAddParamDialog::~KexiAddParamDialog()
+{
+}
+
+QString KexiAddParamDialog::parameterName() {
+ return m_wid->paramname->text();
+}
+
+int KexiAddParamDialog::parameterType() {
+ return m_wid->typecombo->currentItem()+1;
+}
diff --git a/kexi/plugins/queries/kexiaddparamdialog.h b/kexi/plugins/queries/kexiaddparamdialog.h
new file mode 100644
index 00000000..79558a7c
--- /dev/null
+++ b/kexi/plugins/queries/kexiaddparamdialog.h
@@ -0,0 +1,40 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ 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 KEXIADDPARAMDIALOG_H
+#define KEXIADDPARAMDIALOG_H
+
+#include <kdialogbase.h>
+
+class KexiAddParamWidget;
+
+class KexiAddParamDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ KexiAddParamDialog(QWidget *parent);
+ virtual ~KexiAddParamDialog();
+ QString parameterName();
+ int parameterType();
+ private:
+ KexiAddParamWidget *m_wid;
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiaddparamwidget.ui b/kexi/plugins/queries/kexiaddparamwidget.ui
new file mode 100644
index 00000000..43ec25f1
--- /dev/null
+++ b/kexi/plugins/queries/kexiaddparamwidget.ui
@@ -0,0 +1,135 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>KexiAddParamWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KexiAddParamWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>496</width>
+ <height>205</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Parameter</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>kexi_</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>paramname</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Message:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>message</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>typecombo</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp b/kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp
new file mode 100644
index 00000000..4a77f37c
--- /dev/null
+++ b/kexi/plugins/queries/kexidynamicqueryparameterdialog.cpp
@@ -0,0 +1,63 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
+
+ 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 "kexidynamicqueryparameterdialog.h"
+#include "kexidynamicqueryparameterdialog.moc"
+
+#include <qvbox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <qlineedit.h>
+#include <qobjectlist.h>
+
+KexiDynamicQueryParameterDialog::KexiDynamicQueryParameterDialog(QWidget *parent,
+ KexiDataProvider::Parameters *values, const KexiDataProvider::ParameterList &list):
+ KDialogBase(parent, "paramddialog", true, i18n("Query Parameters"),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true )
+{
+ m_values=values;
+ int y;
+ m_mainView=new QVBox(this);
+
+ for (KexiDataProvider::ParameterList::const_iterator it=list.begin();
+ it!=list.end();++it) {
+ QLineEdit *le=new QLineEdit(m_mainView,(*it).name.utf8());
+ le->setText((*values)[(*it).name]);
+ }
+
+ setMainWidget(m_mainView);
+}
+
+KexiDynamicQueryParameterDialog::~KexiDynamicQueryParameterDialog() {}
+
+void KexiDynamicQueryParameterDialog::slotOk() {
+ QObjectList *l=queryList(0,"kexi_.*",true,true);
+ QObjectListIt it(*l);
+ QObject *obj;
+ kdDebug()<<"KexiDynamicQueryParameterDialog::slotOk()"<<endl;
+ while ((obj=it.current())!=0) {
+ kdDebug()<<"KexiDynamicQueryParameterDialog::slotOk()::loop"<<endl;
+ (*m_values)[QString().fromUtf8(obj->name())]=
+ (dynamic_cast<QLineEdit*>(obj))->text();
+ ++it;
+ }
+ delete l;
+ KDialogBase::slotOk();
+}
diff --git a/kexi/plugins/queries/kexidynamicqueryparameterdialog.h b/kexi/plugins/queries/kexidynamicqueryparameterdialog.h
new file mode 100644
index 00000000..b315e4f9
--- /dev/null
+++ b/kexi/plugins/queries/kexidynamicqueryparameterdialog.h
@@ -0,0 +1,45 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
+
+ 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 _KEXI_DYNAMIC_QUERY_PARAMETER_DIALOG_H_
+#define _KEXI_DYNAMIC_QUERY_PARAMETER_DIALOG_H_
+
+
+#include <kdialogbase.h>
+#include <kexidataprovider.h>
+
+class QVBox;
+
+class KexiDynamicQueryParameterDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KexiDynamicQueryParameterDialog(QWidget *parent,KexiDataProvider::Parameters *, const KexiDataProvider::ParameterList &);
+ virtual ~KexiDynamicQueryParameterDialog();
+
+protected:
+ virtual void slotOk();
+private:
+//temporary only. Later a different widget will be used
+ QVBox *m_mainView;
+ KexiDataProvider::Parameters *m_values;
+
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiparameterlisteditor.ui b/kexi/plugins/queries/kexiparameterlisteditor.ui
new file mode 100644
index 00000000..ac4a3230
--- /dev/null
+++ b/kexi/plugins/queries/kexiparameterlisteditor.ui
@@ -0,0 +1,88 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>KexiParameterListEditor</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KexiParameterListEditor</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>190</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Parameters:</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>list</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>addParameter</cstring>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>deleteParameter</cstring>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kexi/plugins/queries/kexiquerydesignerguieditor.cpp b/kexi/plugins/queries/kexiquerydesignerguieditor.cpp
new file mode 100644
index 00000000..d67573e8
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignerguieditor.cpp
@@ -0,0 +1,1803 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiquerydesignerguieditor.h"
+
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qdom.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <kexidb/field.h>
+#include <kexidb/queryschema.h>
+#include <kexidb/connection.h>
+#include <kexidb/parser/parser.h>
+#include <kexidb/parser/sqlparser.h>
+#include <kexidb/utils.h>
+#include <kexidb/roweditbuffer.h>
+#include <kexiutils/identifier.h>
+#include <kexiproject.h>
+#include <keximainwindow.h>
+#include <kexiinternalpart.h>
+#include <kexitableview.h>
+#include <kexitableitem.h>
+#include <kexitableviewdata.h>
+#include <kexidragobjects.h>
+#include <kexidialogbase.h>
+#include <kexidatatable.h>
+#include <kexi.h>
+#include <kexisectionheader.h>
+#include <widget/tableview/kexidataawarepropertyset.h>
+#include <widget/relations/kexirelationwidget.h>
+#include <widget/relations/kexirelationviewtable.h>
+#include <koproperty/property.h>
+#include <koproperty/set.h>
+
+#include "kexiquerypart.h"
+
+//! @todo remove KEXI_NO_QUERY_TOTALS later
+#define KEXI_NO_QUERY_TOTALS
+
+//! indices for table columns
+#define COLUMN_ID_COLUMN 0
+#define COLUMN_ID_TABLE 1
+#define COLUMN_ID_VISIBLE 2
+#ifdef KEXI_NO_QUERY_TOTALS
+# define COLUMN_ID_SORTING 3
+# define COLUMN_ID_CRITERIA 4
+#else
+# define COLUMN_ID_TOTALS 3
+# define COLUMN_ID_SORTING 4
+# define COLUMN_ID_CRITERIA 5
+#endif
+
+/*! @internal */
+class KexiQueryDesignerGuiEditor::Private
+{
+public:
+ Private()
+ : fieldColumnIdentifiers(101, false/*case insens.*/)
+ {
+ droppedNewItem = 0;
+ slotTableAdded_enabled = true;
+ }
+
+ bool changeSingleCellValue(KexiTableItem &item, int columnNumber,
+ const QVariant& value, KexiDB::ResultInfo* result)
+ {
+ data->clearRowEditBuffer();
+ if (!data->updateRowEditBuffer(&item, columnNumber, value)
+ || !data->saveRowChanges(item, true))
+ {
+ if (result)
+ *result = *data->result();
+ return false;
+ }
+ return true;
+ }
+
+ KexiTableViewData *data;
+ KexiDataTable *dataTable;
+ QGuardedPtr<KexiDB::Connection> conn;
+
+ KexiRelationWidget *relations;
+ KexiSectionHeader *head;
+ QSplitter *spl;
+
+ /*! Used to remember in slotDroppedAtRow() what data was dropped,
+ so we can create appropriate prop. set in slotRowInserted()
+ This information is cached and entirely refreshed on updateColumnsData(). */
+ KexiTableViewData *fieldColumnData, *tablesColumnData;
+
+ /*! Collects identifiers selected in 1st (field) column,
+ so we're able to distinguish between table identifiers selected from
+ the dropdown list, and strings (e.g. expressions) entered by hand.
+ This information is cached and entirely refreshed on updateColumnsData().
+ The dict is filled with (char*)1 values (doesn't matter what it is);
+ */
+ QDict<char> fieldColumnIdentifiers;
+
+ KexiDataAwarePropertySet* sets;
+ KexiTableItem *droppedNewItem;
+
+ QString droppedNewTable, droppedNewField;
+
+ bool slotTableAdded_enabled : 1;
+};
+
+static bool isAsterisk(const QString& tableName, const QString& fieldName)
+{
+ return tableName=="*" || fieldName.endsWith("*");
+}
+
+//! @internal \return true if sorting is allowed for \a fieldName and \a tableName
+static bool sortingAllowed(const QString& fieldName, const QString& tableName) {
+ return ! (fieldName=="*" || (fieldName.isEmpty() && tableName=="*"));
+}
+
+//=========================================================
+
+KexiQueryDesignerGuiEditor::KexiQueryDesignerGuiEditor(
+ KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiViewBase(mainWin, parent, name)
+ , d( new Private() )
+{
+ d->conn = mainWin->project()->dbConnection();
+
+ d->spl = new QSplitter(Vertical, this);
+ d->spl->setChildrenCollapsible(false);
+ d->relations = new KexiRelationWidget(mainWin, d->spl, "relations");
+ connect(d->relations, SIGNAL(tableAdded(KexiDB::TableSchema&)),
+ this, SLOT(slotTableAdded(KexiDB::TableSchema&)));
+ connect(d->relations, SIGNAL(tableHidden(KexiDB::TableSchema&)),
+ this, SLOT(slotTableHidden(KexiDB::TableSchema&)));
+ connect(d->relations, SIGNAL(tableFieldDoubleClicked(KexiDB::TableSchema*,const QString&)),
+ this, SLOT(slotTableFieldDoubleClicked(KexiDB::TableSchema*,const QString&)));
+
+ d->head = new KexiSectionHeader(i18n("Query Columns"), Vertical, d->spl);
+ d->dataTable = new KexiDataTable(mainWin, d->head, "guieditor_dataTable", false);
+ d->dataTable->dataAwareObject()->setSpreadSheetMode();
+
+ d->data = new KexiTableViewData(); //just empty data
+ d->sets = new KexiDataAwarePropertySet( this, d->dataTable->dataAwareObject() );
+ initTableColumns();
+ initTableRows();
+
+ QValueList<int> c;
+ c << COLUMN_ID_COLUMN << COLUMN_ID_TABLE << COLUMN_ID_CRITERIA;
+ if (d->dataTable->tableView()/*sanity*/) {
+ d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_VISIBLE);
+ d->dataTable->tableView()->adjustColumnWidthToContents(COLUMN_ID_SORTING);
+ d->dataTable->tableView()->maximizeColumnsWidth( c );
+ d->dataTable->tableView()->setDropsAtRowEnabled(true);
+ connect(d->dataTable->tableView(), SIGNAL(dragOverRow(KexiTableItem*,int,QDragMoveEvent*)),
+ this, SLOT(slotDragOverTableRow(KexiTableItem*,int,QDragMoveEvent*)));
+ connect(d->dataTable->tableView(), SIGNAL(droppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&)),
+ this, SLOT(slotDroppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&)));
+ connect(d->dataTable->tableView(), SIGNAL(newItemAppendedForAfterDeletingInSpreadSheetMode()),
+ this, SLOT(slotNewItemAppendedForAfterDeletingInSpreadSheetMode()));
+ }
+ connect(d->data, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)),
+ this, SLOT(slotBeforeCellChanged(KexiTableItem*,int,QVariant&,KexiDB::ResultInfo*)));
+ connect(d->data, SIGNAL(rowInserted(KexiTableItem*,uint,bool)),
+ this, SLOT(slotRowInserted(KexiTableItem*,uint,bool)));
+ connect(d->relations, SIGNAL(tablePositionChanged(KexiRelationViewTableContainer*)),
+ this, SLOT(slotTablePositionChanged(KexiRelationViewTableContainer*)));
+ connect(d->relations, SIGNAL(aboutConnectionRemove(KexiRelationViewConnection*)),
+ this, SLOT(slotAboutConnectionRemove(KexiRelationViewConnection*)));
+
+ QVBoxLayout *l = new QVBoxLayout(this);
+ l->addWidget(d->spl);
+
+ addChildView(d->relations);
+ addChildView(d->dataTable);
+ setViewWidget(d->dataTable, true);
+ d->relations->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+ d->head->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+ updateGeometry();
+ d->spl->setSizes(QValueList<int>()<< 800<<400);
+}
+
+KexiQueryDesignerGuiEditor::~KexiQueryDesignerGuiEditor()
+{
+}
+
+void
+KexiQueryDesignerGuiEditor::initTableColumns()
+{
+ KexiTableViewColumn *col1 = new KexiTableViewColumn("column", KexiDB::Field::Enum, i18n("Column"),
+ i18n("Describes field name or expression for the designed query."));
+ col1->setRelatedDataEditable(true);
+
+ d->fieldColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text);
+ col1->setRelatedData( d->fieldColumnData );
+ d->data->addColumn(col1);
+
+ KexiTableViewColumn *col2 = new KexiTableViewColumn("table", KexiDB::Field::Enum, i18n("Table"),
+ i18n("Describes table for a given field. Can be empty."));
+ d->tablesColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text);
+ col2->setRelatedData( d->tablesColumnData );
+ d->data->addColumn(col2);
+
+ KexiTableViewColumn *col3 = new KexiTableViewColumn("visible", KexiDB::Field::Boolean, i18n("Visible"),
+ i18n("Describes visibility for a given field or expression."));
+ col3->field()->setDefaultValue( QVariant(false, 0) );
+ col3->field()->setNotNull( true );
+ d->data->addColumn(col3);
+
+#ifndef KEXI_NO_QUERY_TOTALS
+ KexiTableViewColumn *col4 = new KexiTableViewColumn("totals", KexiDB::Field::Enum, i18n("Totals"),
+ i18n("Describes a way of computing totals for a given field or expression."));
+ QValueVector<QString> totalsTypes;
+ totalsTypes.append( i18n("Group by") );
+ totalsTypes.append( i18n("Sum") );
+ totalsTypes.append( i18n("Average") );
+ totalsTypes.append( i18n("Min") );
+ totalsTypes.append( i18n("Max") );
+ //todo: more like this
+ col4->field()->setEnumHints(totalsTypes);
+ d->data->addColumn(col4);
+#endif
+
+ KexiTableViewColumn *col5 = new KexiTableViewColumn("sort", KexiDB::Field::Enum, i18n("Sorting"),
+ i18n("Describes a way of sorting for a given field."));
+ QValueVector<QString> sortTypes;
+ sortTypes.append( "" );
+ sortTypes.append( i18n("Ascending") );
+ sortTypes.append( i18n("Descending") );
+ col5->field()->setEnumHints(sortTypes);
+ d->data->addColumn(col5);
+
+ KexiTableViewColumn *col6 = new KexiTableViewColumn("criteria", KexiDB::Field::Text, i18n("Criteria"),
+ i18n("Describes the criteria for a given field or expression."));
+ d->data->addColumn(col6);
+
+// KexiTableViewColumn *col7 = new KexiTableViewColumn(i18n("Or"), KexiDB::Field::Text);
+// d->data->addColumn(col7);
+}
+
+void KexiQueryDesignerGuiEditor::initTableRows()
+{
+ d->data->deleteAllRows();
+ //const int columns = d->data->columnsCount();
+ for (int i=0; i<(int)d->sets->size(); i++) {
+ KexiTableItem* item;
+ d->data->append(item = d->data->createItem());
+ item->at(COLUMN_ID_VISIBLE) = QVariant(false, 0);
+ }
+ d->dataTable->dataAwareObject()->setData(d->data);
+
+ updateColumnsData();
+}
+
+void KexiQueryDesignerGuiEditor::updateColumnsData()
+{
+ d->dataTable->dataAwareObject()->acceptRowEdit();
+
+ QStringList sortedTableNames;
+ for (TablesDictIterator it(*d->relations->tables());it.current();++it)
+ sortedTableNames += it.current()->schema()->name();
+ qHeapSort( sortedTableNames );
+
+ //several tables can be hidden now, so remove rows for these tables
+ QValueList<int> rowsToDelete;
+ for (int r = 0; r<(int)d->sets->size(); r++) {
+ KoProperty::Set *set = d->sets->at(r);
+ if (set) {
+ QString tableName = (*set)["table"].value().toString();
+ QString fieldName = (*set)["field"].value().toString();
+ const bool allTablesAsterisk = tableName=="*" && d->relations->tables()->isEmpty();
+ const bool fieldNotFound = tableName!="*"
+ && !(*set)["isExpression"].value().toBool()
+ && sortedTableNames.end() == qFind( sortedTableNames.begin(), sortedTableNames.end(), tableName );
+
+ if (allTablesAsterisk || fieldNotFound) {
+ //table not found: mark this line for later removal
+ rowsToDelete += r;
+ }
+ }
+ }
+ d->data->deleteRows( rowsToDelete );
+
+ //update 'table' and 'field' columns
+ d->tablesColumnData->deleteAllRows();
+ d->fieldColumnData->deleteAllRows();
+ d->fieldColumnIdentifiers.clear();
+
+ KexiTableItem *item = d->fieldColumnData->createItem(); //new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]="*";
+ (*item)[COLUMN_ID_TABLE]="*";
+ d->fieldColumnData->append( item );
+ d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
+
+// tempData()->clearQuery();
+ tempData()->unregisterForTablesSchemaChanges();
+ for (QStringList::const_iterator it = sortedTableNames.constBegin();
+ it!=sortedTableNames.constEnd(); ++it)
+ {
+ //table
+/*! @todo what about query? */
+ KexiDB::TableSchema *table = d->relations->tables()->find(*it)->schema()->table();
+ d->conn->registerForTableSchemaChanges(*tempData(), *table); //this table will be used
+ item = d->tablesColumnData->createItem(); //new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]=table->name();
+ (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
+ d->tablesColumnData->append( item );
+ //fields
+ item = d->fieldColumnData->createItem(); //new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]=table->name()+".*";
+ (*item)[COLUMN_ID_TABLE]=(*item)[COLUMN_ID_COLUMN];
+ d->fieldColumnData->append( item );
+ d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
+ for (KexiDB::Field::ListIterator t_it = table->fieldsIterator();t_it.current();++t_it) {
+ item = d->fieldColumnData->createItem(); // new KexiTableItem(2);
+ (*item)[COLUMN_ID_COLUMN]=table->name()+"."+t_it.current()->name();
+ (*item)[COLUMN_ID_TABLE]=QString(" ") + t_it.current()->name();
+ d->fieldColumnData->append( item );
+ d->fieldColumnIdentifiers.insert((*item)[COLUMN_ID_COLUMN].toString(), (char*)1); //cache
+ }
+ }
+//TODO
+}
+
+KexiRelationWidget *KexiQueryDesignerGuiEditor::relationView() const
+{
+ return d->relations;
+}
+
+KexiQueryPart::TempData *
+KexiQueryDesignerGuiEditor::tempData() const
+{
+ return static_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
+}
+
+static QString msgCannotSwitch_EmptyDesign() {
+ return i18n("Cannot switch to data view, because query design is empty.\n"
+ "First, please create your design.");
+}
+
+bool
+KexiQueryDesignerGuiEditor::buildSchema(QString *errMsg)
+{
+ //build query schema
+ KexiQueryPart::TempData * temp = tempData();
+ if (temp->query()) {
+ temp->clearQuery();
+ } else {
+ temp->setQuery( new KexiDB::QuerySchema() );
+ }
+
+ //add tables
+ for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
+/*! @todo what about query? */
+ temp->query()->addTable( it.current()->schema()->table() );
+ }
+
+ //add fields, also build:
+ // -WHERE expression
+ // -ORDER BY list
+ KexiDB::BaseExpr *whereExpr = 0;
+ const uint count = QMIN(d->data->count(), d->sets->size());
+ bool fieldsFound = false;
+ KexiTableViewData::Iterator it(d->data->iterator());
+ for (uint i=0; i<count && it.current(); ++it, i++) {
+ if (!it.current()->at(COLUMN_ID_TABLE).isNull() && it.current()->at(COLUMN_ID_COLUMN).isNull()) {
+ //show message about missing field name, and set focus to that cell
+ kexipluginsdbg << "no field provided!" << endl;
+ d->dataTable->dataAwareObject()->setCursorPosition(i,0);
+ if (errMsg)
+ *errMsg = i18n("Select column for table \"%1\"")
+ .arg(it.current()->at(COLUMN_ID_TABLE).toString());
+ return false;
+ }
+
+ KoProperty::Set *set = d->sets->at(i);
+ if (set) {
+ QString tableName = (*set)["table"].value().toString().stripWhiteSpace();
+ QString fieldName = (*set)["field"].value().toString();
+ QString fieldAndTableName = fieldName;
+ KexiDB::Field *currentField = 0; // will be set if this column is a single field
+ KexiDB::QueryColumnInfo* currentColumn = 0;
+ if (!tableName.isEmpty())
+ fieldAndTableName.prepend(tableName+".");
+ const bool fieldVisible = (*set)["visible"].value().toBool();
+ QString criteriaStr = (*set)["criteria"].value().toString();
+ QCString alias = (*set)["alias"].value().toCString();
+ if (!criteriaStr.isEmpty()) {
+ int token;
+ KexiDB::BaseExpr *criteriaExpr = parseExpressionString(criteriaStr, token,
+ true/*allowRelationalOperator*/);
+ if (!criteriaExpr) {//for sanity
+ if (errMsg)
+ *errMsg = i18n("Invalid criteria \"%1\"").arg(criteriaStr);
+ delete whereExpr;
+ return false;
+ }
+ //build relational expression for column variable
+ KexiDB::VariableExpr *varExpr = new KexiDB::VariableExpr(fieldAndTableName);
+ criteriaExpr = new KexiDB::BinaryExpr(KexiDBExpr_Relational, varExpr, token, criteriaExpr);
+ //critera ok: add it to WHERE section
+ if (whereExpr)
+ whereExpr = new KexiDB::BinaryExpr(KexiDBExpr_Logical, whereExpr, AND, criteriaExpr);
+ else //first expr.
+ whereExpr = criteriaExpr;
+ }
+ if (tableName.isEmpty()) {
+ if ((*set)["isExpression"].value().toBool()==true) {
+ //add expression column
+ int dummyToken;
+ KexiDB::BaseExpr *columnExpr = parseExpressionString(fieldName, dummyToken,
+ false/*!allowRelationalOperator*/);
+ if (!columnExpr) {
+ if (errMsg)
+ *errMsg = i18n("Invalid expression \"%1\"").arg(fieldName);
+ return false;
+ }
+ temp->query()->addExpression(columnExpr, fieldVisible);
+ if (fieldVisible)
+ fieldsFound = true;
+ if (!alias.isEmpty())
+ temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
+ }
+ //TODO
+ }
+ else if (tableName=="*") {
+ //all tables asterisk
+ temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), 0 ), fieldVisible );
+ if (fieldVisible)
+ fieldsFound = true;
+ continue;
+ }
+ else {
+ KexiDB::TableSchema *t = d->conn->tableSchema(tableName);
+ if (fieldName=="*") {
+ //single-table asterisk: <tablename> + ".*" + number
+ temp->query()->addAsterisk( new KexiDB::QueryAsterisk( temp->query(), t ), fieldVisible );
+ if (fieldVisible)
+ fieldsFound = true;
+ } else {
+ if (!t) {
+ kexipluginswarn << "query designer: NO TABLE '"
+ << (*set)["table"].value().toString() << "'" << endl;
+ continue;
+ }
+ currentField = t->field( fieldName );
+ if (!currentField) {
+ kexipluginswarn << "query designer: NO FIELD '" << fieldName << "'" << endl;
+ continue;
+ }
+ if (!fieldVisible && criteriaStr.isEmpty() && (*set)["isExpression"]
+ && (*set)["sorting"].value().toString()!="nosorting")
+ {
+ kexipluginsdbg << "invisible field with sorting: do not add it to the fields list" << endl;
+ continue;
+ }
+ temp->query()->addField(currentField, fieldVisible);
+ currentColumn = temp->query()->expandedOrInternalField(
+ temp->query()->fieldsExpanded().count() - 1 );
+ if (fieldVisible)
+ fieldsFound = true;
+ if (!alias.isEmpty())
+ temp->query()->setColumnAlias( temp->query()->fieldCount()-1, alias );
+ }
+ }
+ }
+ else {//!set
+ kexipluginsdbg << it.current()->at(COLUMN_ID_TABLE).toString() << endl;
+ }
+ }
+ if (!fieldsFound) {
+ if (errMsg)
+ *errMsg = msgCannotSwitch_EmptyDesign();
+ return false;
+ }
+ if (whereExpr)
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::buildSchema(): setting CRITERIA: "
+ << whereExpr->debugString() << endl;
+
+ //set always, because if whereExpr==NULL,
+ //this will clear prev. expr
+ temp->query()->setWhereExpression( whereExpr );
+
+ //add relations (looking for connections)
+ for (ConnectionListIterator it(*d->relations->connections()); it.current(); ++it) {
+ KexiRelationViewTableContainer *masterTable = it.current()->masterTable();
+ KexiRelationViewTableContainer *detailsTable = it.current()->detailsTable();
+
+/*! @todo what about query? */
+ temp->query()->addRelationship(
+ masterTable->schema()->table()->field(it.current()->masterField()),
+ detailsTable->schema()->table()->field(it.current()->detailsField()) );
+ }
+
+ // Add sorting information (ORDER BY) - we can do that only now
+ // after all QueryColumnInfo items are instantiated
+ KexiDB::OrderByColumnList orderByColumns;
+ it = d->data->iterator();
+ int fieldNumber = -1; //field number (empty rows are omitted)
+ for (uint i=0/*row number*/; i<count && it.current(); ++it, i++) {
+ KoProperty::Set *set = d->sets->at(i);
+ if (!set)
+ continue;
+ fieldNumber++;
+ KexiDB::Field *currentField = 0;
+ KexiDB::QueryColumnInfo *currentColumn = 0;
+ QString sortingString( (*set)["sorting"].value().toString() );
+ if (sortingString!="ascending" && sortingString!="descending")
+ continue;
+ if (!(*set)["visible"].value().toBool()) {
+ // this row defines invisible field but contains sorting information,
+ // what means KexiDB::Field should be used as a reference for this sorting
+ // Note1: alias is not supported here.
+
+ // Try to find a field (not mentioned after SELECT):
+ currentField = temp->query()->findTableField( (*set)["field"].value().toString() );
+ if (!currentField) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::buildSchema(): NO FIELD '"
+ << (*set)["field"].value().toString()
+ << " available for sorting" << endl;
+ continue;
+ }
+ orderByColumns.appendField(*currentField, sortingString=="ascending");
+ continue;
+ }
+ currentField = temp->query()->field( (uint)fieldNumber );
+ if (!currentField || currentField->isExpression() || currentField->isQueryAsterisk())
+//! @todo support expressions here
+ continue;
+//! @todo ok, but not for expressions
+ QString aliasString( (*set)["alias"].value().toString() );
+ currentColumn = temp->query()->columnInfo(
+ (*set)["table"].value().toString() + "."
+ + (aliasString.isEmpty() ? currentField->name() : aliasString) );
+ if (currentField && currentColumn) {
+ if (currentColumn->visible)
+ orderByColumns.appendColumn(*currentColumn, sortingString=="ascending");
+ else if (currentColumn->field)
+ orderByColumns.appendField(*currentColumn->field, sortingString=="ascending");
+ }
+ }
+ temp->query()->setOrderByColumnList( orderByColumns );
+
+ temp->query()->debug();
+ temp->registerTableSchemaChanges(temp->query());
+ //TODO?
+ return true;
+}
+
+tristate
+KexiQueryDesignerGuiEditor::beforeSwitchTo(int mode, bool &dontStore)
+{
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::beforeSwitch()" << mode << endl;
+
+ if (!d->dataTable->dataAwareObject()->acceptRowEdit())
+ return cancelled;
+
+ if (mode==Kexi::DesignViewMode) {
+ return true;
+ }
+ else if (mode==Kexi::DataViewMode) {
+// if (!d->dataTable->dataAwareObject()->acceptRowEdit())
+ // return cancelled;
+
+ if (!dirty() && parentDialog()->neverSaved()) {
+ KMessageBox::information(this, msgCannotSwitch_EmptyDesign());
+ return cancelled;
+ }
+ if (dirty() || !tempData()->query()) {
+ //remember current design in a temporary structure
+ dontStore=true;
+ QString errMsg;
+ //build schema; problems are not allowed
+ if (!buildSchema(&errMsg)) {
+ KMessageBox::sorry(this, errMsg);
+ return cancelled;
+ }
+ }
+ //TODO
+ return true;
+ }
+ else if (mode==Kexi::TextViewMode) {
+ dontStore=true;
+ //build schema; ignore problems
+ buildSchema();
+/* if (tempData()->query && tempData()->query->fieldCount()==0) {
+ //no fields selected: let's add "*" (all-tables asterisk),
+ // otherwise SQL statement will be invalid
+ tempData()->query->addAsterisk( new KexiDB::QueryAsterisk( tempData()->query ) );
+ }*/
+ //todo
+ return true;
+ }
+
+ return false;
+}
+
+tristate
+KexiQueryDesignerGuiEditor::afterSwitchFrom(int mode)
+{
+ const bool was_dirty = dirty();
+ KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection();
+ if (mode==Kexi::NoViewMode || (mode==Kexi::DataViewMode && !tempData()->query())) {
+ //this is not a SWITCH but a fresh opening in this view mode
+ if (!m_dialog->neverSaved()) {
+ if (!loadLayout()) {
+ //err msg
+ parentDialog()->setStatus(conn,
+ i18n("Query definition loading failed."),
+ i18n("Query design may be corrupted so it could not be opened even in text view.\n"
+ "You can delete the query and create it again."));
+ return false;
+ }
+ // Invalid queries case:
+ // KexiDialogBase::switchToViewMode() first opens DesignViewMode,
+ // and then KexiQueryPart::loadSchemaData() doesn't allocate QuerySchema object
+ // do we're carefully looking at parentDialog()->schemaData()
+ KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ if (q) {
+ KexiDB::ResultInfo result;
+ showFieldsForQuery( q, result );
+ if (!result.success) {
+ parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
+ tempData()->proposeOpeningInTextViewModeBecauseOfProblems = true;
+ return false;
+ }
+ }
+//! @todo load global query properties
+ }
+ }
+ else if (mode==Kexi::TextViewMode || mode==Kexi::DataViewMode) {
+ // Switch from text or data view. In the second case, the design could be changed as well
+ // because there could be changes made in the text view before switching to the data view.
+ if (tempData()->queryChangedInPreviousView) {
+ //previous view changed query data
+ //-clear and regenerate GUI items
+ initTableRows();
+ //todo
+ if (tempData()->query()) {
+ //there is a query schema to show
+ showTablesForQuery( tempData()->query() );
+ //-show fields
+ KexiDB::ResultInfo result;
+ showFieldsAndRelationsForQuery( tempData()->query(), result );
+ if (!result.success) {
+ parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
+ return false;
+ }
+ }
+ else {
+ d->relations->clear();
+ }
+ }
+//! @todo load global query properties
+ }
+
+ if (mode==Kexi::DataViewMode) {
+ //this is just a SWITCH from data view
+ //set cursor if needed:
+ if (d->dataTable->dataAwareObject()->currentRow()<0
+ || d->dataTable->dataAwareObject()->currentColumn()<0)
+ {
+ d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
+ d->dataTable->dataAwareObject()->setCursorPosition(0,0);
+ }
+ }
+ tempData()->queryChangedInPreviousView = false;
+ setFocus(); //to allow shared actions proper update
+ if (!was_dirty)
+ setDirty(false);
+ return true;
+}
+
+
+KexiDB::SchemaData*
+KexiQueryDesignerGuiEditor::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ if (!d->dataTable->dataAwareObject()->acceptRowEdit()) {
+ cancel = true;
+ return 0;
+ }
+ QString errMsg;
+ KexiQueryPart::TempData * temp = tempData();
+ if (!temp->query() || !(viewMode()==Kexi::DesignViewMode && !temp->queryChangedInPreviousView)) {
+ //only rebuild schema if it has not been rebuilt previously
+ if (!buildSchema(&errMsg)) {
+ KMessageBox::sorry(this, errMsg);
+ cancel = true;
+ return 0;
+ }
+ }
+ (KexiDB::SchemaData&)*temp->query() = sdata; //copy main attributes
+
+ bool ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *temp->query(), true /*newObject*/ );
+ m_dialog->setId( temp->query()->id() );
+
+ if (ok)
+ ok = storeLayout();
+
+// temp->query = 0; //will be returned, so: don't keep it
+ if (!ok) {
+ temp->setQuery( 0 );
+// delete query;
+ return 0;
+ }
+ return temp->takeQuery(); //will be returned, so: don't keep it in temp
+}
+
+tristate KexiQueryDesignerGuiEditor::storeData(bool dontAsk)
+{
+ if (!d->dataTable->dataAwareObject()->acceptRowEdit())
+ return cancelled;
+
+ const bool was_dirty = dirty();
+ tristate res = KexiViewBase::storeData(dontAsk); //this clears dirty flag
+ if (true == res)
+ res = buildSchema();
+ if (true == res)
+ res = storeLayout();
+ if (true != res) {
+ if (was_dirty)
+ setDirty(true);
+ }
+ return res;
+}
+
+void KexiQueryDesignerGuiEditor::showTablesForQuery(KexiDB::QuerySchema *query)
+{
+//replaced by code below that preserves geometries d->relations->clear();
+
+ // instead of hiding all tables and showing some tables,
+ // show only these new and hide these unncecessary; the same for connections)
+ d->slotTableAdded_enabled = false; //speedup
+ d->relations->removeAllConnections(); //connections will be recreated
+ d->relations->hideAllTablesExcept( query->tables() );
+ for (KexiDB::TableSchema::ListIterator it(*query->tables()); it.current(); ++it) {
+ d->relations->addTable( it.current() );
+ }
+
+ d->slotTableAdded_enabled = true;
+ updateColumnsData();
+}
+
+void KexiQueryDesignerGuiEditor::addConnection(
+ KexiDB::Field *masterField, KexiDB::Field *detailsField)
+{
+ SourceConnection conn;
+ conn.masterTable = masterField->table()->name(); //<<<TODO
+ conn.masterField = masterField->name();
+ conn.detailsTable = detailsField->table()->name();
+ conn.detailsField = detailsField->name();
+ d->relations->addConnection( conn );
+}
+
+void KexiQueryDesignerGuiEditor::showFieldsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result)
+{
+ showFieldsOrRelationsForQueryInternal(query, true, false, result);
+}
+
+void KexiQueryDesignerGuiEditor::showRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result)
+{
+ showFieldsOrRelationsForQueryInternal(query, false, true, result);
+}
+
+void KexiQueryDesignerGuiEditor::showFieldsAndRelationsForQuery(KexiDB::QuerySchema *query,
+ KexiDB::ResultInfo& result)
+{
+ showFieldsOrRelationsForQueryInternal(query, true, true, result);
+}
+
+void KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(
+ KexiDB::QuerySchema *query, bool showFields, bool showRelations, KexiDB::ResultInfo& result)
+{
+ result.clear();
+ const bool was_dirty = dirty();
+
+ //1. Show explicitly declared relations:
+ if (showRelations) {
+ KexiDB::Relationship *rel;
+ for (KexiDB::Relationship::ListIterator it(*query->relationships());
+ (rel=it.current()); ++it)
+ {
+//! @todo: now only sigle-field relationships are implemented!
+ KexiDB::Field *masterField = rel->masterIndex()->fields()->first();
+ KexiDB::Field *detailsField = rel->detailsIndex()->fields()->first();
+ addConnection(masterField, detailsField);
+ }
+ }
+
+ //2. Collect information about criterias
+ // --this must be top level chain of AND's
+ // --this will also show joins as: [table1.]field1 = [table2.]field2
+ QDict<KexiDB::BaseExpr> criterias(101, false);
+ KexiDB::BaseExpr* e = query->whereExpression();
+ KexiDB::BaseExpr* eItem = 0;
+ while (e) {
+ //eat parentheses because the expression can be (....) AND (... AND ... )
+ while (e && e->toUnary() && e->token()=='(')
+ e = e->toUnary()->arg();
+
+ if (e->toBinary() && e->token()==AND) {
+ eItem = e->toBinary()->left();
+ e = e->toBinary()->right();
+ }
+ else {
+ eItem = e;
+ e = 0;
+ }
+
+ //eat parentheses
+ while (eItem && eItem->toUnary() && eItem->token()=='(')
+ eItem = eItem->toUnary()->arg();
+
+ if (!eItem)
+ continue;
+
+ kexidbg << eItem->toString() << endl;
+ KexiDB::BinaryExpr* binary = eItem->toBinary();
+ if (binary && eItem->exprClass()==KexiDBExpr_Relational) {
+ KexiDB::Field *leftField = 0, *rightField = 0;
+ if (eItem->token()=='='
+ && binary->left()->toVariable()
+ && binary->right()->toVariable()
+ && (leftField = query->findTableField( binary->left()->toString() ))
+ && (rightField = query->findTableField( binary->right()->toString() )))
+ {
+//! @todo move this check to parser on QuerySchema creation
+//! or to QuerySchema creation (WHERE expression should be then simplified
+//! by removing joins
+
+ //this is relationship defined as following JOIN: [table1.]field1 = [table2.]field2
+ if (showRelations) {
+//! @todo testing primary key here is too simplified; maybe look ar isForeignKey() or indices..
+//! @todo what about multifield joins?
+ if (leftField->isPrimaryKey())
+ addConnection(leftField /*master*/, rightField /*details*/);
+ else
+ addConnection(rightField /*master*/, leftField /*details*/);
+//! @todo addConnection() should have "bool oneToOne" arg, for 1-to-1 relations
+ }
+ }
+ else if (binary->left()->toVariable()) {
+ //this is: variable , op , argument
+ //store variable -> argument:
+ criterias.insert(binary->left()->toVariable()->name, binary->right());
+ }
+ else if (binary->right()->toVariable()) {
+ //this is: argument , op , variable
+ //store variable -> argument:
+ criterias.insert(binary->right()->toVariable()->name, binary->left());
+ }
+ }
+ } //while
+
+ if (!showFields)
+ return;
+
+ //3. show fields (including * and table.*)
+ uint row_num = 0;
+ KexiDB::Field *field;
+ QPtrDict<char> usedCriterias(101); // <-- used criterias will be saved here
+ // so in step 4. we will be able to add
+ // remaining invisible columns with criterias
+ for (KexiDB::Field::ListIterator it(*query->fields());
+ (field = it.current()); ++it, row_num++)
+ {
+ //append a new row
+ QString tableName, fieldName, columnAlias, criteriaString;
+ KexiDB::BinaryExpr *criteriaExpr = 0;
+ KexiDB::BaseExpr *criteriaArgument = 0;
+ if (field->isQueryAsterisk()) {
+ if (field->table()) {//single-table asterisk
+ tableName = field->table()->name();
+ fieldName = "*";
+ }
+ else {//all-tables asterisk
+ tableName = "*";
+ fieldName = "";
+ }
+ }
+ else {
+ columnAlias = query->columnAlias(row_num);
+ if (field->isExpression()) {
+// if (columnAlias.isEmpty()) {
+// columnAlias = i18n("expression", "expr%1").arg(row_num); //TODO
+// }
+// if (columnAlias.isEmpty())
+//TODO: ok? perhaps do not allow to omit aliases?
+ fieldName = field->expression()->toString();
+// else
+// fieldName = columnAlias + ": " + field->expression()->toString();
+ }
+ else {
+ tableName = field->table()->name();
+ fieldName = field->name();
+ criteriaArgument = criterias[fieldName];
+ if (!criteriaArgument) {//try table.field
+ criteriaArgument = criterias[tableName+"."+fieldName];
+ }
+ if (criteriaArgument) {//criteria expression is just a parent of argument
+ criteriaExpr = criteriaArgument->parent()->toBinary();
+ usedCriterias.insert(criteriaArgument, (char*)1); //save info. about used criteria
+ }
+ }
+ }
+ //create new row data
+ KexiTableItem *newItem = createNewRow(tableName, fieldName, true /* visible*/);
+ if (criteriaExpr) {
+//! @todo fix for !INFIX operators
+ if (criteriaExpr->token()=='=')
+ criteriaString = criteriaArgument->toString();
+ else
+ criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
+ (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
+ }
+ d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
+ //OK, row inserted: create a new set for it
+ KoProperty::Set &set = *createPropertySet( row_num, tableName, fieldName, true/*new one*/ );
+ if (!columnAlias.isEmpty())
+ set["alias"].setValue(columnAlias, false);
+ if (!criteriaString.isEmpty())
+ set["criteria"].setValue( criteriaString, false );
+ if (field->isExpression()) {
+// (*newItem)[COLUMN_ID_COLUMN] = ;
+ if (!d->changeSingleCellValue(*newItem, COLUMN_ID_COLUMN,
+ QVariant(columnAlias + ": " + field->expression()->toString()), &result))
+ return; //problems with setting column expression
+ }
+ }
+
+ //4. show ORDER BY information
+ d->data->clearRowEditBuffer();
+ KexiDB::OrderByColumnList &orderByColumns = query->orderByColumnList();
+ QMap<KexiDB::QueryColumnInfo*,int> columnsOrder(
+ query->columnsOrder(KexiDB::QuerySchema::UnexpandedListWithoutAsterisks) );
+ for (KexiDB::OrderByColumn::ListConstIterator orderByColumnsIt( orderByColumns.constBegin() );
+ orderByColumnsIt!=orderByColumns.constEnd(); ++orderByColumnsIt)
+ {
+ KexiDB::QueryColumnInfo *column = (*orderByColumnsIt).column();
+ KexiTableItem *rowItem = 0;
+ KoProperty::Set *rowPropertySet = 0;
+ if (column) {
+ //sorting for visible column
+ if (column->visible) {
+ if (columnsOrder.contains(column)) {
+ const int columnPosition = columnsOrder[ column ];
+ rowItem = d->data->at( columnPosition );
+ rowPropertySet = d->sets->at( columnPosition );
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal():\n\t"
+ "Setting \"" << (*orderByColumnsIt).debugString() << "\" sorting for row #"
+ << columnPosition << endl;
+ }
+ }
+ }
+ else if ((*orderByColumnsIt).field()) {
+ //this will be presented as invisible field: create new row
+ field = (*orderByColumnsIt).field();
+ QString tableName( field->table() ? field->table()->name() : QString::null );
+ rowItem = createNewRow( tableName, field->name(), false /* !visible*/);
+ d->dataTable->dataAwareObject()->insertItem(rowItem, row_num);
+ rowPropertySet = createPropertySet( row_num, tableName, field->name(), true /*newOne*/ );
+ propertySetSwitched();
+ kexipluginsdbg << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal():\n\t"
+ "Setting \"" << (*orderByColumnsIt).debugString() << "\" sorting for invisible field "
+ << field->name() << ", table " << tableName << " -row #" << row_num << endl;
+ row_num++;
+ }
+ //alter sorting for either existing or new row
+ if (rowItem && rowPropertySet) {
+ d->data->updateRowEditBuffer(rowItem, COLUMN_ID_SORTING,
+ (*orderByColumnsIt).ascending() ? 1 : 2); // this will automatically update "sorting" property
+ // in slotBeforeCellChanged()
+ d->data->saveRowChanges(*rowItem, true);
+ (*rowPropertySet)["sorting"].clearModifiedFlag(); // this property should look "fresh"
+ if (!rowItem->at(COLUMN_ID_VISIBLE).toBool()) //update
+ (*rowPropertySet)["visible"].setValue(QVariant(false,0), false/*rememberOldValue*/);
+ }
+ }
+
+ //5. Show fields for unused criterias (with "Visible" column set to false)
+ KexiDB::BaseExpr *criteriaArgument; // <-- contains field or table.field
+ for (QDictIterator<KexiDB::BaseExpr> it(criterias); (criteriaArgument = it.current()); ++it) {
+ if (usedCriterias[it.current()])
+ continue;
+ //unused: append a new row
+ KexiDB::BinaryExpr *criteriaExpr = criteriaArgument->parent()->toBinary();
+ if (!criteriaExpr) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
+ "criteriaExpr is not a binary expr" << endl;
+ continue;
+ }
+ KexiDB::VariableExpr *columnNameArgument = criteriaExpr->left()->toVariable(); //left or right
+ if (!columnNameArgument) {
+ columnNameArgument = criteriaExpr->right()->toVariable();
+ if (!columnNameArgument) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
+ "columnNameArgument is not a variable (table or table.field) expr" << endl;
+ continue;
+ }
+ }
+ KexiDB::Field* field = 0;
+ if (-1 == columnNameArgument->name.find('.') && query->tables()->count()==1) {
+ //extreme case: only field name provided for one-table query:
+ field = query->tables()->first()->field(columnNameArgument->name);
+ }
+ else {
+ field = query->findTableField(columnNameArgument->name);
+ }
+
+ if (!field) {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::showFieldsOrRelationsForQueryInternal(): "
+ "no columnInfo found in the query for name \"" << columnNameArgument->name << endl;
+ continue;
+ }
+ QString tableName, fieldName, columnAlias, criteriaString;
+//! @todo what about ALIAS?
+ tableName = field->table()->name();
+ fieldName = field->name();
+ //create new row data
+ KexiTableItem *newItem = createNewRow(tableName, fieldName, false /* !visible*/);
+ if (criteriaExpr) {
+//! @todo fix for !INFIX operators
+ if (criteriaExpr->token()=='=')
+ criteriaString = criteriaArgument->toString();
+ else
+ criteriaString = criteriaExpr->tokenToString() + " " + criteriaArgument->toString();
+ (*newItem)[COLUMN_ID_CRITERIA] = criteriaString;
+ }
+ d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
+ //OK, row inserted: create a new set for it
+ KoProperty::Set &set = *createPropertySet( row_num++, tableName, fieldName, true/*new one*/ );
+//! @todo if (!columnAlias.isEmpty())
+//! @todo set["alias"].setValue(columnAlias, false);
+//// if (!criteriaString.isEmpty())
+ set["criteria"].setValue( criteriaString, false );
+ set["visible"].setValue( QVariant(false,1), false );
+ }
+
+ //current property set has most probably changed
+ propertySetSwitched();
+
+ if (!was_dirty)
+ setDirty(false);
+ //move to 1st column, 1st row
+ d->dataTable->dataAwareObject()->ensureCellVisible(0,0);
+// tempData()->registerTableSchemaChanges(query);
+}
+
+bool KexiQueryDesignerGuiEditor::loadLayout()
+{
+ QString xml;
+// if (!loadDataBlock( xml, "query_layout" )) {
+ loadDataBlock( xml, "query_layout" );
+ //TODO errmsg
+// return false;
+// }
+ if (xml.isEmpty()) {
+ //in a case when query layout was not saved, build layout by hand
+ // -- dynamic cast because of a need for handling invalid queries
+ // (as in KexiQueryDesignerGuiEditor::afterSwitchFrom()):
+ KexiDB::QuerySchema * q = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ if (q) {
+ showTablesForQuery( q );
+ KexiDB::ResultInfo result;
+ showRelationsForQuery( q, result );
+ if (!result.success) {
+ parentDialog()->setStatus(&result, i18n("Query definition loading failed."));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ QDomDocument doc;
+ doc.setContent(xml);
+ QDomElement doc_el = doc.documentElement(), el;
+ if (doc_el.tagName()!="query_layout") {
+ //TODO errmsg
+ return false;
+ }
+
+ const bool was_dirty = dirty();
+
+ //add tables and relations to the relation view
+ for (el = doc_el.firstChild().toElement(); !el.isNull(); el=el.nextSibling().toElement()) {
+ if (el.tagName()=="table") {
+ KexiDB::TableSchema *t = d->conn->tableSchema(el.attribute("name"));
+ int x = el.attribute("x","-1").toInt();
+ int y = el.attribute("y","-1").toInt();
+ int width = el.attribute("width","-1").toInt();
+ int height = el.attribute("height","-1").toInt();
+ QRect rect;
+ if (x!=-1 || y!=-1 || width!=-1 || height!=-1)
+ rect = QRect(x,y,width,height);
+ d->relations->addTable( t, rect );
+ }
+ else if (el.tagName()=="conn") {
+ SourceConnection src_conn;
+ src_conn.masterTable = el.attribute("mtable");
+ src_conn.masterField = el.attribute("mfield");
+ src_conn.detailsTable = el.attribute("dtable");
+ src_conn.detailsField = el.attribute("dfield");
+ d->relations->addConnection(src_conn);
+ }
+ }
+
+ if (!was_dirty)
+ setDirty(false);
+ return true;
+}
+
+bool KexiQueryDesignerGuiEditor::storeLayout()
+{
+ KexiQueryPart::TempData * temp = tempData();
+
+ // Save SQL without driver-escaped keywords.
+ KexiDB::Connection* dbConn = mainWin()->project()->dbConnection();
+ if (m_dialog->schemaData()) //set this instance as obsolete (only if it's stored)
+ dbConn->setQuerySchemaObsolete( m_dialog->schemaData()->name() );
+
+ KexiDB::Connection::SelectStatementOptions options;
+ options.identifierEscaping = KexiDB::Driver::EscapeKexi|KexiDB::Driver::EscapeAsNecessary;
+ options.addVisibleLookupColumns = false;
+ QString sqlText = dbConn->selectStatement( *temp->query(), options );
+ if (!storeDataBlock( sqlText, "sql" )) {
+ return false;
+ }
+
+ //serialize detailed XML query definition
+ QString xml = "<query_layout>", tmp;
+ for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
+ KexiRelationViewTableContainer *table_cont = it.current();
+/*! @todo what about query? */
+ tmp = QString("<table name=\"")+QString(table_cont->schema()->name())+"\" x=\""
+ +QString::number(table_cont->x())
+ +"\" y=\""+QString::number(table_cont->y())
+ +"\" width=\""+QString::number(table_cont->width())
+ +"\" height=\""+QString::number(table_cont->height())
+ +"\"/>";
+ xml += tmp;
+ }
+
+ KexiRelationViewConnection *con;
+ for (ConnectionListIterator it(*d->relations->connections()); (con = it.current()); ++it) {
+ tmp = QString("<conn mtable=\"") + QString(con->masterTable()->schema()->name())
+ + "\" mfield=\"" + con->masterField() + "\" dtable=\""
+ + QString(con->detailsTable()->schema()->name())
+ + "\" dfield=\"" + con->detailsField() + "\"/>";
+ xml += tmp;
+ }
+ xml += "</query_layout>";
+ if (!storeDataBlock( xml, "query_layout" )) {
+ return false;
+ }
+
+// mainWin()->project()->reloadPartItem( m_dialog );
+
+ return true;
+}
+
+QSize KexiQueryDesignerGuiEditor::sizeHint() const
+{
+ QSize s1 = d->relations->sizeHint();
+ QSize s2 = d->head->sizeHint();
+ return QSize(QMAX(s1.width(),s2.width()), s1.height()+s2.height());
+}
+
+KexiTableItem*
+KexiQueryDesignerGuiEditor::createNewRow(const QString& tableName, const QString& fieldName,
+ bool visible) const
+{
+ KexiTableItem *newItem = d->data->createItem();
+ QString key;
+ if (tableName=="*")
+ key="*";
+ else {
+ if (!tableName.isEmpty())
+ key = (tableName+".");
+ key += fieldName;
+ }
+ (*newItem)[COLUMN_ID_COLUMN]=key;
+ (*newItem)[COLUMN_ID_TABLE]=tableName;
+ (*newItem)[COLUMN_ID_VISIBLE]=QVariant(visible, 1);
+#ifndef KEXI_NO_QUERY_TOTALS
+ (*newItem)[COLUMN_ID_TOTALS]=QVariant(0);
+#endif
+ return newItem;
+}
+
+void KexiQueryDesignerGuiEditor::slotDragOverTableRow(
+ KexiTableItem * /*item*/, int /*row*/, QDragMoveEvent* e)
+{
+ if (e->provides("kexi/field")) {
+ e->acceptAction(true);
+ }
+}
+
+void
+KexiQueryDesignerGuiEditor::slotDroppedAtRow(KexiTableItem * /*item*/, int /*row*/,
+ QDropEvent *ev, KexiTableItem*& newItem)
+{
+ QString sourceMimeType;
+ QString srcTable;
+ QString srcField;
+
+ if (!KexiFieldDrag::decodeSingle(ev,sourceMimeType,srcTable,srcField))
+ return;
+ //insert new row at specific place
+ newItem = createNewRow(srcTable, srcField, true /* visible*/);
+ d->droppedNewItem = newItem;
+ d->droppedNewTable = srcTable;
+ d->droppedNewField = srcField;
+ //TODO
+}
+
+void KexiQueryDesignerGuiEditor::slotNewItemAppendedForAfterDeletingInSpreadSheetMode()
+{
+ KexiTableItem *item = d->data->last();
+ if (item)
+ item->at(COLUMN_ID_VISIBLE) = QVariant(false, 0); //the same init as in initTableRows()
+}
+
+void KexiQueryDesignerGuiEditor::slotRowInserted(KexiTableItem* item, uint row, bool /*repaint*/)
+{
+ if (d->droppedNewItem && d->droppedNewItem==item) {
+ createPropertySet( row, d->droppedNewTable, d->droppedNewField, true );
+ propertySetSwitched();
+ d->droppedNewItem=0;
+ }
+}
+
+void KexiQueryDesignerGuiEditor::slotTableAdded(KexiDB::TableSchema & /*t*/)
+{
+ if (!d->slotTableAdded_enabled)
+ return;
+ updateColumnsData();
+ setDirty();
+ d->dataTable->setFocus();
+}
+
+void KexiQueryDesignerGuiEditor::slotTableHidden(KexiDB::TableSchema & /*t*/)
+{
+ updateColumnsData();
+ setDirty();
+}
+
+/*! @internal generates smallest unique alias */
+QCString KexiQueryDesignerGuiEditor::generateUniqueAlias() const
+{
+//TODO: add option for using non-i18n'd "expr" prefix?
+ const QCString expStr
+ = i18n("short for 'expression' word (only latin letters, please)", "expr").latin1();
+//TODO: optimization: cache it?
+ QAsciiDict<char> aliases(101);
+ for (int r = 0; r<(int)d->sets->size(); r++) {
+ KoProperty::Set *set = d->sets->at(r);
+ if (set) {
+ const QCString a = (*set)["alias"].value().toCString().lower();
+ if (!a.isEmpty())
+ aliases.insert(a,(char*)1);
+ }
+ }
+ int aliasNr=1;
+ for (;;aliasNr++) {
+ if (!aliases[expStr+QString::number(aliasNr).latin1()])
+ break;
+ }
+ return expStr+QString::number(aliasNr).latin1();
+}
+
+//! @todo this is primitive, temporary: reuse SQL parser
+KexiDB::BaseExpr*
+KexiQueryDesignerGuiEditor::parseExpressionString(const QString& fullString, int& token,
+ bool allowRelationalOperator)
+{
+ QString str = fullString.stripWhiteSpace();
+ int len = 0;
+ //KexiDB::BaseExpr *expr = 0;
+ //1. get token
+ token = 0;
+ //2-char-long tokens
+ if (str.startsWith(">="))
+ token = GREATER_OR_EQUAL;
+ else if (str.startsWith("<="))
+ token = LESS_OR_EQUAL;
+ else if (str.startsWith("<>"))
+ token = NOT_EQUAL;
+ else if (str.startsWith("!="))
+ token = NOT_EQUAL2;
+ else if (str.startsWith("=="))
+ token = '=';
+
+ if (token!=0)
+ len = 2;
+ else if (str.startsWith("=") //1-char-long tokens
+ || str.startsWith("<")
+ || str.startsWith(">"))
+ {
+ token = str[0].latin1();
+ len = 1;
+ }
+ else {
+ if (allowRelationalOperator)
+ token = '=';
+ }
+
+ if (!allowRelationalOperator && token!=0)
+ return 0;
+
+ //1. get expression after token
+ if (len>0)
+ str = str.mid(len).stripWhiteSpace();
+ if (str.isEmpty())
+ return 0;
+
+ KexiDB::BaseExpr *valueExpr = 0;
+ QRegExp re;
+ if (str.length()>=2 &&
+ (
+ (str.startsWith("\"") && str.endsWith("\""))
+ || (str.startsWith("'") && str.endsWith("'")))
+ )
+ {
+ valueExpr = new KexiDB::ConstExpr(CHARACTER_STRING_LITERAL, str.mid(1,str.length()-2));
+ }
+ else if (str.startsWith("[") && str.endsWith("]")) {
+ valueExpr = new KexiDB::QueryParameterExpr(str.mid(1,str.length()-2));
+ }
+ else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})")).exactMatch( str ))
+ {
+ valueExpr = new KexiDB::ConstExpr(DATE_CONST, QDate::fromString(
+ re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
+ +"-"+re.cap(3).rightJustify(2, '0'), Qt::ISODate));
+ }
+ else if ((re = QRegExp("(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
+ || (re = QRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
+ {
+ QString res = re.cap(1).rightJustify(2, '0')+":"+re.cap(2).rightJustify(2, '0')
+ +":"+re.cap(3).rightJustify(2, '0');
+// kexipluginsdbg << res << endl;
+ valueExpr = new KexiDB::ConstExpr(TIME_CONST, QTime::fromString(res, Qt::ISODate));
+ }
+ else if ((re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2})")).exactMatch( str )
+ || (re = QRegExp("(\\d{1,4})-(\\d{1,2})-(\\d{1,2})\\s+(\\d{1,2}):(\\d{1,2}):(\\d{1,2})")).exactMatch( str ))
+ {
+ QString res = re.cap(1).rightJustify(4, '0')+"-"+re.cap(2).rightJustify(2, '0')
+ +"-"+re.cap(3).rightJustify(2, '0')
+ +"T"+re.cap(4).rightJustify(2, '0')+":"+re.cap(5).rightJustify(2, '0')
+ +":"+re.cap(6).rightJustify(2, '0');
+// kexipluginsdbg << res << endl;
+ valueExpr = new KexiDB::ConstExpr(DATETIME_CONST,
+ QDateTime::fromString(res, Qt::ISODate));
+ }
+ else if (str[0]>='0' && str[0]<='9' || str[0]=='-' || str[0]=='+') {
+ //number
+ QString decimalSym = KGlobal::locale()->decimalSymbol();
+ bool ok;
+ int pos = str.find('.');
+ if (pos==-1) {//second chance: local decimal symbol
+ pos = str.find(decimalSym);
+ }
+ if (pos>=0) {//real const number
+ const int left = str.left(pos).toInt(&ok);
+ if (!ok)
+ return 0;
+ const int right = str.mid(pos+1).toInt(&ok);
+ if (!ok)
+ return 0;
+ valueExpr = new KexiDB::ConstExpr(REAL_CONST, QPoint(left,right)); //decoded to QPoint
+ }
+ else {
+ //integer const
+ const Q_LLONG val = str.toLongLong(&ok);
+ if (!ok)
+ return 0;
+ valueExpr = new KexiDB::ConstExpr(INTEGER_CONST, val);
+ }
+ }
+ else if (str.lower()=="null") {
+ valueExpr = new KexiDB::ConstExpr(SQL_NULL, QVariant());
+ }
+ else {//identfier
+ if (!KexiUtils::isIdentifier(str))
+ return 0;
+ valueExpr = new KexiDB::VariableExpr(str);
+ //find first matching field for name 'str':
+ for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) {
+/*! @todo what about query? */
+ if (it.current()->schema()->table() && it.current()->schema()->table()->field(str)) {
+ valueExpr->toVariable()->field = it.current()->schema()->table()->field(str);
+ break;
+ }
+ }
+ }
+ return valueExpr;
+}
+
+void KexiQueryDesignerGuiEditor::slotBeforeCellChanged(KexiTableItem *item, int colnum,
+ QVariant& newValue, KexiDB::ResultInfo* result)
+{
+ if (colnum == COLUMN_ID_COLUMN) {
+ if (newValue.isNull()) {
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(), false/*!allowSignals*/);
+ d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));//invisible
+ d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, QVariant());
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());//remove totals
+#endif
+ d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());//remove crit.
+ d->sets->removeCurrentPropertySet();
+ }
+ else {
+ //auto fill 'table' column
+ QString fieldId( newValue.toString().stripWhiteSpace() ); //tmp, can look like "table.field"
+ QString fieldName; //"field" part of "table.field" or expression string
+ QString tableName; //empty for expressions
+ QCString alias;
+ QString columnValueForExpr; //for setting pretty printed "alias: expr" in 1st column
+ const bool isExpression = !d->fieldColumnIdentifiers[fieldId];
+ if (isExpression) {
+ //this value is entered by hand and doesn't match
+ //any value in the combo box -- we're assuming this is an expression
+ //-table remains null
+ //-find "alias" in something like "alias : expr"
+ const int id = fieldId.find(':');
+ if (id>0) {
+ alias = fieldId.left(id).stripWhiteSpace().latin1();
+ if (!KexiUtils::isIdentifier(alias)) {
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Entered column alias \"%1\" is not a valid identifier.")
+ .arg(alias);
+ result->desc = i18n("Identifiers should start with a letter or '_' character");
+ return;
+ }
+ }
+ fieldName = fieldId.mid(id+1).stripWhiteSpace();
+ //check expr.
+ KexiDB::BaseExpr *e;
+ int dummyToken;
+ if ((e = parseExpressionString(fieldName, dummyToken, false/*allowRelationalOperator*/)))
+ {
+ fieldName = e->toString(); //print it prettier
+ //this is just checking: destroy expr. object
+ delete e;
+ }
+ else {
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Invalid expression \"%1\"").arg(fieldName);
+ return;
+ }
+ }
+ else {//not expr.
+ //this value is properly selected from combo box list
+ if (fieldId=="*") {
+ tableName = "*";
+ }
+ else {
+ if (!KexiDB::splitToTableAndFieldParts(
+ fieldId, tableName, fieldName, KexiDB::SetFieldNameIfNoTableName))
+ {
+ kexipluginswarn << "KexiQueryDesignerGuiEditor::slotBeforeCellChanged(): no 'field' or 'table.field'" << endl;
+ return;
+ }
+ }
+ }
+ bool saveOldValue = true;
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item); //*propertyBuffer();
+ if (!set) {
+ saveOldValue = false; // no old val.
+ const int row = d->data->findRef(item);
+ if (row<0) {
+ result->success = false;
+ return;
+ }
+ set = createPropertySet( row, tableName, fieldName, true );
+ propertySetSwitched();
+ }
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TABLE, QVariant(tableName), false/*!allowSignals*/);
+ d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(true,1));
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));
+#endif
+ if (!sortingAllowed(fieldName, tableName)) {
+ // sorting is not available for "*" or "table.*" rows
+//! @todo what about expressions?
+ d->data->updateRowEditBuffer(item, COLUMN_ID_SORTING, QVariant());
+ }
+ //update properties
+ (*set)["field"].setValue(fieldName, saveOldValue);
+ if (isExpression) {
+ //-no alias but it's needed:
+ if (alias.isEmpty()) //-try oto get old alias
+ alias = (*set)["alias"].value().toCString();
+ if (alias.isEmpty()) //-generate smallest unique alias
+ alias = generateUniqueAlias();
+ }
+ (*set)["isExpression"].setValue(QVariant(isExpression,1), saveOldValue);
+ if (!alias.isEmpty()) {
+ (*set)["alias"].setValue(alias, saveOldValue);
+ //pretty printed "alias: expr"
+ newValue = QString(alias) + ": " + fieldName;
+ }
+ (*set)["caption"].setValue(QString::null, saveOldValue);
+ (*set)["table"].setValue(tableName, saveOldValue);
+ updatePropertiesVisibility(*set);
+ }
+ }
+ else if (colnum==COLUMN_ID_TABLE) {
+ if (newValue.isNull()) {
+ if (!item->at(COLUMN_ID_COLUMN).toString().isEmpty())
+ d->data->updateRowEditBuffer(item, COLUMN_ID_COLUMN, QVariant(), false/*!allowSignals*/);
+ d->data->updateRowEditBuffer(item, COLUMN_ID_VISIBLE, QVariant(false,1));//invisible
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant());//remove totals
+#endif
+ d->data->updateRowEditBuffer(item, COLUMN_ID_CRITERIA, QVariant());//remove crit.
+ d->sets->removeCurrentPropertySet();
+ }
+ //update property
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
+ if (set) {
+ if ((*set)["isExpression"].value().toBool()==false) {
+ (*set)["table"] = newValue;
+ (*set)["caption"] = QString::null;
+ }
+ else {
+ //do not set table for expr. columns
+ newValue = QVariant();
+ }
+// KoProperty::Set &set = *propertyBuffer();
+ updatePropertiesVisibility(*set);
+ }
+ }
+ else if (colnum==COLUMN_ID_VISIBLE) {
+ bool saveOldValue = true;
+ if (!propertySet()) {
+ saveOldValue = false;
+ createPropertySet( d->dataTable->dataAwareObject()->currentRow(),
+ item->at(COLUMN_ID_TABLE).toString(), item->at(COLUMN_ID_COLUMN).toString(), true );
+#ifndef KEXI_NO_QUERY_TOTALS
+ d->data->updateRowEditBuffer(item, COLUMN_ID_TOTALS, QVariant(0));//totals
+#endif
+ propertySetSwitched();
+ }
+ KoProperty::Set &set = *propertySet();
+ set["visible"].setValue(newValue, saveOldValue);
+ }
+#ifndef KEXI_NO_QUERY_TOTALS
+ else if (colnum==COLUMN_ID_TOTALS) {
+ //TODO:
+ //unused yet
+ setDirty(true);
+ }
+#endif
+ else if (colnum==COLUMN_ID_SORTING) {
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
+ QString table( set->property("table").value().toString() );
+ QString field( set->property("field").value().toString() );
+ if (newValue.toInt()==0 || sortingAllowed(field, table)) {
+ KoProperty::Property &property = set->property("sorting");
+ QString key( property.listData()->keysAsStringList()[ newValue.toInt() ] );
+ kexipluginsdbg << "new key=" << key << endl;
+ property.setValue(key, true);
+ }
+ else { //show msg: sorting is not available
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Could not set sorting for multiple columns (%1)")
+ .arg(table=="*" ? table : (table+".*"));
+ }
+ }
+ else if (colnum==COLUMN_ID_CRITERIA) {
+//! @todo this is primitive, temporary: reuse SQL parser
+ QString operatorStr, argStr;
+ KexiDB::BaseExpr* e = 0;
+ const QString str = newValue.toString().stripWhiteSpace();
+ int token;
+ QString field, table;
+ KoProperty::Set *set = d->sets->findPropertySetForItem(*item);
+ if (set) {
+ field = (*set)["field"].value().toString();
+ table = (*set)["table"].value().toString();
+ }
+ if (!str.isEmpty() && (!set || table=="*" || field.find("*")!=-1)) {
+ //asterisk found! criteria not allowed
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ if (propertySet())
+ result->msg = i18n("Could not set criteria for \"%1\"")
+ .arg(table=="*" ? table : field);
+ else
+ result->msg = i18n("Could not set criteria for empty row");
+ //moved to result->allowToDiscardChanges handler //d->dataTable->dataAwareObject()->cancelEditor(); //prevents further editing of this cell
+ }
+ else if (str.isEmpty() || (e = parseExpressionString(str, token, true/*allowRelationalOperator*/)))
+ {
+ if (e) {
+ QString tokenStr;
+ if (token!='=') {
+ KexiDB::BinaryExpr be(KexiDBExpr_Relational, 0, token, 0);
+ tokenStr = be.tokenToString() + " ";
+ }
+ (*set)["criteria"] = tokenStr + e->toString(); //print it prettier
+ //this is just checking: destroy expr. object
+ delete e;
+ }
+ else if (str.isEmpty()) {
+ (*set)["criteria"] = QVariant(); //clear it
+ }
+ setDirty(true);
+ }
+ else {
+ result->success = false;
+ result->allowToDiscardChanges = true;
+ result->column = colnum;
+ result->msg = i18n("Invalid criteria \"%1\"").arg(newValue.toString());
+ }
+ }
+}
+
+void KexiQueryDesignerGuiEditor::slotTablePositionChanged(KexiRelationViewTableContainer*)
+{
+ setDirty(true);
+}
+
+void KexiQueryDesignerGuiEditor::slotAboutConnectionRemove(KexiRelationViewConnection*)
+{
+ setDirty(true);
+}
+
+void KexiQueryDesignerGuiEditor::slotTableFieldDoubleClicked(
+ KexiDB::TableSchema* table, const QString& fieldName )
+{
+ if (!table || (!table->field(fieldName) && fieldName!="*"))
+ return;
+ int row_num;
+ //find last filled row in the GUI table
+ for (row_num=d->sets->size()-1; row_num>=0 && !d->sets->at(row_num); row_num--)
+ ;
+ row_num++; //after
+ //add row
+ KexiTableItem *newItem = createNewRow(table->name(), fieldName, true /* visible*/);
+ d->dataTable->dataAwareObject()->insertItem(newItem, row_num);
+ d->dataTable->dataAwareObject()->setCursorPosition(row_num, 0);
+ //create buffer
+ createPropertySet( row_num, table->name(), fieldName, true/*new one*/ );
+ propertySetSwitched();
+ d->dataTable->setFocus();
+}
+
+KoProperty::Set *KexiQueryDesignerGuiEditor::propertySet()
+{
+ return d->sets->currentPropertySet();
+}
+
+void KexiQueryDesignerGuiEditor::updatePropertiesVisibility(KoProperty::Set& set)
+{
+ const bool asterisk = isAsterisk(
+ set["table"].value().toString(), set["field"].value().toString()
+ );
+#ifndef KEXI_NO_UNFINISHED
+ set["caption"].setVisible( !asterisk );
+#endif
+ set["alias"].setVisible( !asterisk );
+/*always invisible #ifndef KEXI_NO_UNFINISHED
+ set["sorting"].setVisible( !asterisk );
+#endif*/
+ propertySetReloaded(true);
+}
+
+KoProperty::Set*
+KexiQueryDesignerGuiEditor::createPropertySet( int row,
+ const QString& tableName, const QString& fieldName, bool newOne )
+{
+ //const bool asterisk = isAsterisk(tableName, fieldName);
+ QString typeName = "KexiQueryDesignerGuiEditor::Column";
+ KoProperty::Set *set = new KoProperty::Set(d->sets, typeName);
+ KoProperty::Property *prop;
+
+ //meta-info for property editor
+ set->addProperty(prop = new KoProperty::Property("this:classString", i18n("Query column")) );
+ prop->setVisible(false);
+//! \todo add table_field icon (add buff->addProperty(prop = new KexiProperty("this:iconName", "table_field") );
+// prop->setVisible(false);
+
+ set->addProperty(prop = new KoProperty::Property("table", QVariant(tableName)) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty(prop = new KoProperty::Property("field", QVariant(fieldName)) );
+ prop->setVisible(false);//always hidden
+
+ set->addProperty(prop = new KoProperty::Property("caption", QVariant(QString::null), i18n("Caption") ) );
+#ifdef KEXI_NO_UNFINISHED
+ prop->setVisible(false);
+#endif
+
+ set->addProperty(prop = new KoProperty::Property("alias", QVariant(QString::null), i18n("Alias")) );
+
+ set->addProperty(prop = new KoProperty::Property("visible", QVariant(true, 4)) );
+ prop->setVisible(false);
+
+/*TODO:
+ set->addProperty(prop = new KexiProperty("totals", QVariant(QString::null)) );
+ prop->setVisible(false);*/
+
+ //sorting
+ QStringList slist, nlist;
+ slist << "nosorting" << "ascending" << "descending";
+ nlist << i18n("None") << i18n("Ascending") << i18n("Descending");
+ set->addProperty(prop = new KoProperty::Property("sorting",
+ slist, nlist, *slist.at(0), i18n("Sorting")));
+ prop->setVisible(false);
+
+ set->addProperty(prop = new KoProperty::Property("criteria", QVariant(QString::null)) );
+ prop->setVisible(false);
+
+ set->addProperty(prop = new KoProperty::Property("isExpression", QVariant(false, 1)) );
+ prop->setVisible(false);
+
+ connect(set, SIGNAL(propertyChanged(KoProperty::Set&, KoProperty::Property&)),
+ this, SLOT(slotPropertyChanged(KoProperty::Set&, KoProperty::Property&)));
+
+ d->sets->insert(row, set, newOne);
+
+ updatePropertiesVisibility(*set);
+ return set;
+}
+
+void KexiQueryDesignerGuiEditor::setFocus()
+{
+ d->dataTable->setFocus();
+}
+
+void KexiQueryDesignerGuiEditor::slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property)
+{
+ const QCString& pname = property.name();
+/*
+ * TODO (js) use KexiProperty::setValidator(QString) when implemented as described in TODO #60
+ */
+ if (pname=="alias" || pname=="name") {
+ const QVariant& v = property.value();
+ if (!v.toString().stripWhiteSpace().isEmpty() && !KexiUtils::isIdentifier( v.toString() )) {
+ KMessageBox::sorry(this,
+ KexiUtils::identifierExpectedMessage(property.caption(), v.toString()));
+ property.resetValue();
+ }
+ if (pname=="alias") {
+ if (set["isExpression"].value().toBool()==true) {
+ //update value in column #1
+ d->dataTable->dataAwareObject()->acceptEditor();
+// d->dataTable->dataAwareObject()->setCursorPosition(d->dataTable->dataAwareObject()->currentRow(),0);
+ //d->dataTable->dataAwareObject()->startEditCurrentCell();
+ d->data->updateRowEditBuffer(d->dataTable->dataAwareObject()->selectedItem(),
+ 0, QVariant(set["alias"].value().toString() + ": " + set["field"].value().toString()));
+ d->data->saveRowChanges(*d->dataTable->dataAwareObject()->selectedItem(), true);
+// d->dataTable->dataAwareObject()->acceptRowEdit();
+ }
+ }
+ }
+}
+
+void KexiQueryDesignerGuiEditor::slotNewItemStored(KexiPart::Item& item)
+{
+ d->relations->objectCreated(item.mimeType(), item.name().latin1());
+}
+
+void KexiQueryDesignerGuiEditor::slotItemRemoved(const KexiPart::Item& item)
+{
+ d->relations->objectDeleted(item.mimeType(), item.name().latin1());
+}
+
+void KexiQueryDesignerGuiEditor::slotItemRenamed(const KexiPart::Item& item, const QCString& oldName)
+{
+ d->relations->objectRenamed(item.mimeType(), oldName, item.name().latin1());
+}
+
+#include "kexiquerydesignerguieditor.moc"
+
diff --git a/kexi/plugins/queries/kexiquerydesignerguieditor.h b/kexi/plugins/queries/kexiquerydesignerguieditor.h
new file mode 100644
index 00000000..03acb7f6
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignerguieditor.h
@@ -0,0 +1,170 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYDESIGNERGUIEDITOR_H
+#define KEXIQUERYDESIGNERGUIEDITOR_H
+
+#include <qguardedptr.h>
+#include <qsplitter.h>
+
+#include <kexiviewbase.h>
+#include "kexiquerypart.h"
+
+class KexiMainWindow;
+class KexiTableViewData;
+class KexiDataTable;
+class KexiTableItem;
+class KexiRelationWidget;
+class KexiSectionHeader;
+class KexiDataAwarePropertySet;
+class KexiRelationViewTableContainer;
+class KexiRelationViewConnection;
+
+namespace KexiPart
+{
+ class Item;
+}
+
+namespace KoProperty {
+ class Property;
+ class Set;
+}
+
+namespace KexiDB
+{
+ class Connection;
+ class QuerySchema;
+ class TableSchema;
+ class ResultInfo;
+}
+
+//! Design view of the Query Designer
+class KexiQueryDesignerGuiEditor : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryDesignerGuiEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+ virtual ~KexiQueryDesignerGuiEditor();
+
+// KexiDB::QuerySchema *schema();
+
+ KexiRelationWidget *relationView() const;
+
+ virtual QSize sizeHint() const;
+
+ public slots:
+ virtual void setFocus();
+
+ protected:
+ void initTableColumns(); //!< Called just once.
+ void initTableRows(); //!< Called to have all rows empty.
+//unused void addRow(const QString &tbl, const QString &field);
+// void restore();
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+ virtual tristate storeData(bool dontAsk = false);
+
+ /*! Updates data in columns depending on tables that are currently inserted.
+ Tabular Data in combo box popups is updated as well. */
+ void updateColumnsData();
+
+ /*! \return property buffer associated with currently selected row (i.e. field)
+ or 0 if current row is empty. */
+ virtual KoProperty::Set *propertySet();
+
+ KoProperty::Set* createPropertySet( int row,
+ const QString& tableName, const QString& fieldName, bool newOne = false );
+
+ /*! Builds query schema out of information provided by gui.
+ The schema is stored in temp->query member.
+ \a errMsg is optional error message returned.
+ \return true on proper schema creation. */
+ bool buildSchema(QString *errMsg = 0);
+
+ KexiQueryPart::TempData * tempData() const;
+
+ /*! Helper: allocates and initializes new table view's row. Doesn't insert it, just returns.
+ \a tableName and \a fieldName shoudl be provided.
+ \a visible flag sets value for "Visible" column. */
+ KexiTableItem* createNewRow(const QString& tableName, const QString& fieldName,
+ bool visible) const;
+
+ KexiDB::BaseExpr* parseExpressionString(const QString& fullString, int& token,
+ bool allowRelationalOperator);
+
+ QCString generateUniqueAlias() const;
+ void updatePropertiesVisibility(KoProperty::Set& buf);
+
+ protected slots:
+ void slotDragOverTableRow(KexiTableItem *item, int row, QDragMoveEvent* e);
+ void slotDroppedAtRow(KexiTableItem *item, int row,
+ QDropEvent *ev, KexiTableItem*& newItem);
+ //! Reaction on appending a new item after deleting one
+ void slotNewItemAppendedForAfterDeletingInSpreadSheetMode();
+ void slotTableAdded(KexiDB::TableSchema &t);
+ void slotTableHidden(KexiDB::TableSchema &t);
+
+ //! Called before cell change in tableview.
+ void slotBeforeCellChanged(KexiTableItem *item, int colnum,
+ QVariant& newValue, KexiDB::ResultInfo* result);
+
+ void slotRowInserted(KexiTableItem* item, uint row, bool repaint);
+ void slotTablePositionChanged(KexiRelationViewTableContainer*);
+ void slotAboutConnectionRemove(KexiRelationViewConnection*);
+ void slotTableFieldDoubleClicked( KexiDB::TableSchema* table, const QString& fieldName );
+
+ /*! Loads layout of relation GUI diagram. */
+ bool loadLayout();
+
+ /*! Stores layout of relation GUI diagram. */
+ bool storeLayout();
+
+ void showTablesForQuery(KexiDB::QuerySchema *query);
+ //! @internal
+ void showFieldsOrRelationsForQueryInternal(
+ KexiDB::QuerySchema *query, bool showFields, bool showRelations, KexiDB::ResultInfo& result);
+ //! convenience method equal to showFieldsOrRelationsForQueryInternal(query, true, true)
+ void showFieldsAndRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result);
+ //! convenience method equal to showFieldsOrRelationsForQueryInternal(query, true, false)
+ void showFieldsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result);
+ //! convenience method equal to showFieldsOrRelationsForQueryInternal(query, false, true)
+ void showRelationsForQuery(KexiDB::QuerySchema *query, KexiDB::ResultInfo& result);
+
+ void addConnection(KexiDB::Field *masterField, KexiDB::Field *detailsField);
+
+ void slotPropertyChanged(KoProperty::Set& list, KoProperty::Property& property);
+
+// void slotObjectCreated(const QCString &mime, const QCString& name);
+ void slotNewItemStored(KexiPart::Item&);
+ void slotItemRemoved(const KexiPart::Item& item);
+ void slotItemRenamed(const KexiPart::Item& item, const QCString& oldName);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class KexiQueryView; // for storeNewData() and storeData() only
+};
+
+#endif
+
diff --git a/kexi/plugins/queries/kexiquerydesignersql.cpp b/kexi/plugins/queries/kexiquerydesignersql.cpp
new file mode 100644
index 00000000..469d551c
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersql.cpp
@@ -0,0 +1,542 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qsplitter.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qtimer.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+
+#include <kexiutils/utils.h>
+#include <kexidb/driver.h>
+#include <kexidb/connection.h>
+#include <kexidb/parser/parser.h>
+
+#include <kexiproject.h>
+#include <keximainwindow.h>
+
+#include "kexiquerydesignersqleditor.h"
+#include "kexiquerydesignersqlhistory.h"
+#include "kexiquerydesignersql.h"
+#include "kexiquerypart.h"
+
+#include "kexisectionheader.h"
+
+
+static bool compareSQL(const QString& sql1, const QString& sql2)
+{
+ //TODO: use reformatting functions here
+ return sql1.stripWhiteSpace()==sql2.stripWhiteSpace();
+}
+
+//===================
+
+//! @internal
+class KexiQueryDesignerSQLView::Private
+{
+ public:
+ Private() :
+ history(0)
+ , historyHead(0)
+ , statusPixmapOk( DesktopIcon("button_ok") )
+ , statusPixmapErr( DesktopIcon("button_cancel") )
+ , statusPixmapInfo( DesktopIcon("messagebox_info") )
+ , parsedQuery(0)
+ , heightForStatusMode(-1)
+ , heightForHistoryMode(-1)
+ , eventFilterForSplitterEnabled(true)
+ , justSwitchedFromNoViewMode(false)
+ , slotTextChangedEnabled(true)
+ {
+ }
+ KexiQueryDesignerSQLEditor *editor;
+ KexiQueryDesignerSQLHistory *history;
+ QLabel *pixmapStatus, *lblStatus;
+ QHBox *status_hbox;
+ QVBox *history_section;
+ KexiSectionHeader *head, *historyHead;
+ QPixmap statusPixmapOk, statusPixmapErr, statusPixmapInfo;
+ QSplitter *splitter;
+ KToggleAction *action_toggle_history;
+ //! For internal use, this pointer is usually copied to TempData structure,
+ //! when switching out of this view (then it's cleared).
+ KexiDB::QuerySchema *parsedQuery;
+ //! For internal use, statement passed in switching to this view
+ QString origStatement;
+ //! needed to remember height for both modes, between switching
+ int heightForStatusMode, heightForHistoryMode;
+ //! helper for slotUpdateMode()
+ bool action_toggle_history_was_checked : 1;
+ //! helper for eventFilter()
+ bool eventFilterForSplitterEnabled : 1;
+ //! helper for beforeSwitchTo()
+ bool justSwitchedFromNoViewMode : 1;
+ //! helper for slotTextChanged()
+ bool slotTextChangedEnabled : 1;
+};
+
+//===================
+
+KexiQueryDesignerSQLView::KexiQueryDesignerSQLView(KexiMainWindow *mainWin, QWidget *parent, const char *name)
+ : KexiViewBase(mainWin, parent, name)
+ , d( new Private() )
+{
+ d->splitter = new QSplitter(this);
+ d->splitter->setOrientation(Vertical);
+ d->head = new KexiSectionHeader(i18n("SQL Query Text"), Vertical, d->splitter);
+ d->editor = new KexiQueryDesignerSQLEditor(mainWin, d->head, "sqle");
+// d->editor->installEventFilter(this);//for keys
+ connect(d->editor, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
+ addChildView(d->editor);
+ setViewWidget(d->editor);
+ d->splitter->setFocusProxy(d->editor);
+ setFocusProxy(d->editor);
+
+ d->history_section = new QVBox(d->splitter);
+
+ d->status_hbox = new QHBox(d->history_section);
+ d->status_hbox->installEventFilter(this);
+ d->splitter->setResizeMode(d->history_section, QSplitter::KeepSize);
+ d->status_hbox->setSpacing(0);
+ d->pixmapStatus = new QLabel(d->status_hbox);
+ d->pixmapStatus->setFixedWidth(d->statusPixmapOk.width()*3/2);
+ d->pixmapStatus->setAlignment(AlignHCenter | AlignTop);
+ d->pixmapStatus->setMargin(d->statusPixmapOk.width()/4);
+ d->pixmapStatus->setPaletteBackgroundColor( palette().active().color(QColorGroup::Base) );
+
+ d->lblStatus = new QLabel(d->status_hbox);
+ d->lblStatus->setAlignment(AlignLeft | AlignTop | WordBreak);
+ d->lblStatus->setMargin(d->statusPixmapOk.width()/4);
+ d->lblStatus->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
+ d->lblStatus->resize(d->lblStatus->width(),d->statusPixmapOk.width()*3);
+ d->lblStatus->setPaletteBackgroundColor( palette().active().color(QColorGroup::Base) );
+
+ QHBoxLayout *b = new QHBoxLayout(this);
+ b->addWidget(d->splitter);
+
+ plugSharedAction("querypart_check_query", this, SLOT(slotCheckQuery()));
+ plugSharedAction("querypart_view_toggle_history", this, SLOT(slotUpdateMode()));
+ d->action_toggle_history = static_cast<KToggleAction*>( sharedAction( "querypart_view_toggle_history" ) );
+
+ d->historyHead = new KexiSectionHeader(i18n("SQL Query History"), Vertical, d->history_section);
+ d->historyHead->installEventFilter(this);
+ d->history = new KexiQueryDesignerSQLHistory(d->historyHead, "sql_history");
+
+ static const QString msg_back = i18n("Back to Selected Query");
+ static const QString msg_clear = i18n("Clear History");
+ d->historyHead->addButton("select_item", msg_back, this, SLOT(slotSelectQuery()));
+ d->historyHead->addButton("editclear", msg_clear, d->history, SLOT(clear()));
+ d->history->popupMenu()->insertItem(SmallIcon("select_item"), msg_back, this, SLOT(slotSelectQuery()));
+ d->history->popupMenu()->insertItem(SmallIcon("editclear"), msg_clear, d->history, SLOT(clear()));
+ connect(d->history, SIGNAL(currentItemDoubleClicked()), this, SLOT(slotSelectQuery()));
+
+ d->heightForHistoryMode = -1; //height() / 2;
+ //d->historyHead->hide();
+ d->action_toggle_history_was_checked = !d->action_toggle_history->isChecked(); //to force update
+ slotUpdateMode();
+ slotCheckQuery();
+}
+
+KexiQueryDesignerSQLView::~KexiQueryDesignerSQLView()
+{
+ delete d;
+}
+
+KexiQueryDesignerSQLEditor *KexiQueryDesignerSQLView::editor() const
+{
+ return d->editor;
+}
+
+void KexiQueryDesignerSQLView::setStatusOk()
+{
+ d->pixmapStatus->setPixmap(d->statusPixmapOk);
+ setStatusText("<h2>"+i18n("The query is correct")+"</h2>");
+ d->history->addEvent(d->editor->text().stripWhiteSpace(), true, QString::null);
+}
+
+void KexiQueryDesignerSQLView::setStatusError(const QString& msg)
+{
+ d->pixmapStatus->setPixmap(d->statusPixmapErr);
+ setStatusText("<h2>"+i18n("The query is incorrect")+"</h2><p>"+msg+"</p>");
+ d->history->addEvent(d->editor->text().stripWhiteSpace(), false, msg);
+}
+
+void KexiQueryDesignerSQLView::setStatusEmpty()
+{
+ d->pixmapStatus->setPixmap(d->statusPixmapInfo);
+ setStatusText(i18n("Please enter your query and execute \"Check query\" function to verify it."));
+}
+
+void KexiQueryDesignerSQLView::setStatusText(const QString& text)
+{
+ if (!d->action_toggle_history->isChecked()) {
+ QSimpleRichText rt(text, d->lblStatus->font());
+ rt.setWidth(d->lblStatus->width());
+ QValueList<int> sz = d->splitter->sizes();
+ const int newHeight = rt.height()+d->lblStatus->margin()*2;
+ if (sz[1]<newHeight) {
+ sz[1] = newHeight;
+ d->splitter->setSizes(sz);
+ }
+ d->lblStatus->setText(text);
+ }
+}
+
+tristate
+KexiQueryDesignerSQLView::beforeSwitchTo(int mode, bool &dontStore)
+{
+//TODO
+ dontStore = true;
+ if (mode==Kexi::DesignViewMode || mode==Kexi::DataViewMode) {
+ QString sqlText = d->editor->text().stripWhiteSpace();
+ KexiQueryPart::TempData * temp = tempData();
+ if (sqlText.isEmpty()) {
+ //special case: empty SQL text
+ if (temp->query()) {
+ temp->queryChangedInPreviousView = true; //query changed
+ temp->setQuery(0);
+// delete temp->query; //safe?
+// temp->query = 0;
+ }
+ }
+ else {
+ const bool designViewWasVisible = parentDialog()->viewForMode(mode)!=0;
+ //should we check SQL text?
+ if (designViewWasVisible
+ && !d->justSwitchedFromNoViewMode //unchanged, but we should check SQL text
+ && compareSQL(d->origStatement, d->editor->text())) {
+ //statement unchanged! - nothing to do
+ temp->queryChangedInPreviousView = false;
+ }
+ else {
+ //yes: parse SQL text
+ if (!slotCheckQuery()) {
+ if (KMessageBox::No==KMessageBox::warningYesNo(this, "<p>"+i18n("The query you entered is incorrect.")
+ +"</p><p>"+i18n("Do you want to cancel any changes made to this SQL text?")+"</p>"
+ +"</p><p>"+i18n("Answering \"No\" allows you to make corrections.")+"</p>"))
+ {
+ return cancelled;
+ }
+ //do not change original query - it's invalid
+ temp->queryChangedInPreviousView = false;
+ //this view is no longer _just_ switched from "NoViewMode"
+ d->justSwitchedFromNoViewMode = false;
+ return true;
+ }
+ //this view is no longer _just_ switched from "NoViewMode"
+ d->justSwitchedFromNoViewMode = false;
+ //replace old query schema with new one
+ temp->setQuery( d->parsedQuery ); //this will also delete temp->query()
+// delete temp->query; //safe?
+// temp->query = d->parsedQuery;
+ d->parsedQuery = 0;
+ temp->queryChangedInPreviousView = true;
+ }
+ }
+ }
+
+ //TODO
+ /*
+ if (d->doc) {
+ KexiDB::Parser *parser = new KexiDB::Parser(mainWin()->project()->dbConnection());
+ parser->parse(getQuery());
+ d->doc->setSchema(parser->select());
+
+ if(parser->operation() == KexiDB::Parser::OP_Error)
+ {
+ d->history->addEvent(getQuery(), false, parser->error().error());
+ kdDebug() << "KexiQueryDesignerSQLView::beforeSwitchTo(): syntax error!" << endl;
+ return false;
+ }
+ delete parser;
+ }
+
+ setDirty(true);*/
+// if (parentDialog()->hasFocus())
+ d->editor->setFocus();
+ return true;
+}
+
+tristate
+KexiQueryDesignerSQLView::afterSwitchFrom(int mode)
+{
+ kdDebug() << "KexiQueryDesignerSQLView::afterSwitchFrom()" << endl;
+// if (mode==Kexi::DesignViewMode || mode==Kexi::DataViewMode) {
+ if (mode==Kexi::NoViewMode) {
+ //User opened text view _directly_.
+ //This flag is set to indicate for beforeSwitchTo() that even if text has not been changed,
+ //SQL text should be invalidated.
+ d->justSwitchedFromNoViewMode = true;
+ }
+ KexiQueryPart::TempData * temp = tempData();
+ KexiDB::QuerySchema *query = temp->query();
+ if (!query) {//try to just get saved schema, instead of temporary one
+ query = dynamic_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ }
+
+ if (mode!=0/*failure only if it is switching from prev. view*/ && !query) {
+ //TODO msg
+ return false;
+ }
+
+ if (!query) {
+ //no valid query schema delivered: just load sql text, no matter if it's valid
+ if (!loadDataBlock( d->origStatement, "sql", true /*canBeEmpty*/ ))
+ return false;
+ }
+ else {
+ // Use query with Kexi keywords (but not driver-specific keywords) escaped.
+ temp->setQuery( query );
+// temp->query = query;
+ KexiDB::Connection* conn = mainWin()->project()->dbConnection();
+ KexiDB::Connection::SelectStatementOptions options;
+ options.identifierEscaping = KexiDB::Driver::EscapeKexi;
+ options.addVisibleLookupColumns = false;
+ d->origStatement = conn->selectStatement(*query, options).stripWhiteSpace();
+ }
+
+ d->slotTextChangedEnabled = false;
+ d->editor->setText( d->origStatement );
+ d->slotTextChangedEnabled = true;
+ QTimer::singleShot(100, d->editor, SLOT(setFocus()));
+ return true;
+}
+
+QString
+KexiQueryDesignerSQLView::sqlText() const
+{
+ return d->editor->text();
+}
+
+bool KexiQueryDesignerSQLView::slotCheckQuery()
+{
+ QString sqlText( d->editor->text().stripWhiteSpace() );
+ if (sqlText.isEmpty()) {
+ delete d->parsedQuery;
+ d->parsedQuery = 0;
+ setStatusEmpty();
+ return true;
+ }
+
+ kdDebug() << "KexiQueryDesignerSQLView::slotCheckQuery()" << endl;
+ //KexiQueryPart::TempData * temp = tempData();
+ KexiDB::Parser *parser = mainWin()->project()->sqlParser();
+ const bool ok = parser->parse( sqlText );
+ delete d->parsedQuery;
+ d->parsedQuery = parser->query();
+ if (!d->parsedQuery || !ok || !parser->error().type().isEmpty()) {
+ KexiDB::ParserError err = parser->error();
+ setStatusError(err.error());
+ d->editor->jump(err.at());
+ delete d->parsedQuery;
+ d->parsedQuery = 0;
+ return false;
+ }
+
+ setStatusOk();
+ return true;
+}
+
+void KexiQueryDesignerSQLView::slotUpdateMode()
+{
+ if (d->action_toggle_history->isChecked() == d->action_toggle_history_was_checked)
+ return;
+
+ d->eventFilterForSplitterEnabled = false;
+
+ QValueList<int> sz = d->splitter->sizes();
+ d->action_toggle_history_was_checked = d->action_toggle_history->isChecked();
+ int heightToSet = -1;
+ if (d->action_toggle_history->isChecked()) {
+ d->status_hbox->hide();
+ d->historyHead->show();
+ d->history->show();
+ if (d->heightForHistoryMode==-1)
+ d->heightForHistoryMode = m_dialog->height() / 2;
+ heightToSet = d->heightForHistoryMode;
+ d->heightForStatusMode = sz[1]; //remember
+ }
+ else {
+ if (d->historyHead)
+ d->historyHead->hide();
+ d->status_hbox->show();
+ if (d->heightForStatusMode>=0) {
+ heightToSet = d->heightForStatusMode;
+ } else {
+ d->heightForStatusMode = d->status_hbox->height();
+ }
+ if (d->heightForHistoryMode>=0)
+ d->heightForHistoryMode = sz[1];
+ }
+
+ if (heightToSet>=0) {
+ sz[1] = heightToSet;
+ d->splitter->setSizes(sz);
+ }
+ d->eventFilterForSplitterEnabled = true;
+ slotCheckQuery();
+}
+
+void KexiQueryDesignerSQLView::slotTextChanged()
+{
+ if (!d->slotTextChangedEnabled)
+ return;
+ setDirty(true);
+ setStatusEmpty();
+}
+
+bool KexiQueryDesignerSQLView::eventFilter( QObject *o, QEvent *e )
+{
+ if (d->eventFilterForSplitterEnabled) {
+ if (e->type()==QEvent::Resize && o && o==d->historyHead && d->historyHead->isVisible()) {
+ d->heightForHistoryMode = d->historyHead->height();
+ }
+ else if (e->type()==QEvent::Resize && o && o==d->status_hbox && d->status_hbox->isVisible()) {
+ d->heightForStatusMode = d->status_hbox->height();
+ }
+ }
+ return KexiViewBase::eventFilter(o, e);
+}
+
+void KexiQueryDesignerSQLView::updateActions(bool activated)
+{
+ if (activated) {
+ slotUpdateMode();
+ }
+ setAvailable("querypart_check_query", true);
+ setAvailable("querypart_view_toggle_history", true);
+ KexiViewBase::updateActions(activated);
+}
+
+void KexiQueryDesignerSQLView::slotSelectQuery()
+{
+ QString sql = d->history->selectedStatement();
+ if (!sql.isEmpty()) {
+ d->editor->setText( sql );
+ }
+}
+
+KexiQueryPart::TempData *
+KexiQueryDesignerSQLView::tempData() const
+{
+ return dynamic_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
+}
+
+KexiDB::SchemaData*
+KexiQueryDesignerSQLView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ Q_UNUSED( cancel );
+
+ //here: we won't store query layout: it will be recreated 'by hand' in GUI Query Editor
+ bool queryOK = slotCheckQuery();
+ bool ok = true;
+ KexiDB::SchemaData* query = 0;
+ if (queryOK) {
+ //query is ok
+ if (d->parsedQuery) {
+ query = d->parsedQuery; //will be returned, so: don't keep it
+ d->parsedQuery = 0;
+ }
+ else {//empty query
+ query = new KexiDB::SchemaData(); //just empty
+ }
+
+ (KexiDB::SchemaData&)*query = sdata; //copy main attributes
+ ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true /*newObject*/ );
+ if (ok) {
+ m_dialog->setId( query->id() );
+ ok = storeDataBlock( d->editor->text(), "sql" );
+ }
+ }
+ else {
+ //query is not ok
+//#if 0
+ //TODO: allow saving invalid queries
+ //TODO: just ask this question:
+ query = new KexiDB::SchemaData(); //just empty
+
+ ok = (KMessageBox::questionYesNo(this, i18n("Do you want to save invalid query?"),
+ 0, KStdGuiItem::yes(), KStdGuiItem::no(), "askBeforeSavingInvalidQueries"/*config entry*/)==KMessageBox::Yes);
+ if (ok) {
+ (KexiDB::SchemaData&)*query = sdata; //copy main attributes
+ ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *query, true /*newObject*/ );
+ }
+ if (ok) {
+ m_dialog->setId( query->id() );
+ ok = storeDataBlock( d->editor->text(), "sql" );
+ }
+//#else
+ //ok = false;
+//#endif
+ }
+ if (!ok) {
+ delete query;
+ query = 0;
+ }
+ return query;
+}
+
+tristate KexiQueryDesignerSQLView::storeData(bool dontAsk)
+{
+ tristate res = KexiViewBase::storeData(dontAsk);
+ if (~res)
+ return res;
+ if (res == true) {
+ res = storeDataBlock( d->editor->text(), "sql" );
+#if 0
+ bool queryOK = slotCheckQuery();
+ if (queryOK) {
+ res = storeDataBlock( d->editor->text(), "sql" );
+ }
+ else {
+ //query is not ok
+ //TODO: allow saving invalid queries
+ //TODO: just ask this question:
+ res = false;
+ }
+#endif
+ }
+ if (res == true) {
+ QString empty_xml;
+ res = storeDataBlock( empty_xml, "query_layout" ); //clear
+ }
+ if (!res)
+ setDirty(true);
+ return res;
+}
+
+
+/*void KexiQueryDesignerSQLView::slotHistoryHeaderButtonClicked(const QString& buttonIdentifier)
+{
+ if (buttonIdentifier=="select_query") {
+ slotSelectQuery();
+ }
+ else if (buttonIdentifier=="clear_history") {
+ d->history->clear();
+ }
+}*/
+
+#include "kexiquerydesignersql.moc"
+
diff --git a/kexi/plugins/queries/kexiquerydesignersql.h b/kexi/plugins/queries/kexiquerydesignersql.h
new file mode 100644
index 00000000..f31c838f
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersql.h
@@ -0,0 +1,82 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYDESIGNERSQL_H
+#define KEXIQUERYDESIGNERSQL_H
+
+#include <kexiviewbase.h>
+#include "kexiquerypart.h"
+
+class KexiQueryDesignerSQLEditor;
+class KexiQueryDesignerSQLViewPrivate;
+
+//! The KexiQueryDesignerSQLView class for editing Queries in text mode.
+/*! It is a view containing SQL text editor
+ and SQL history/status widget splitted vertically.
+ Depending on user's will, the widget can be in "sql history"
+ mode or in "sql status" mode. */
+class KexiQueryDesignerSQLView : public KexiViewBase
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryDesignerSQLView(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0);
+ virtual ~KexiQueryDesignerSQLView();
+
+ QString sqlText() const;
+ KexiQueryDesignerSQLEditor *editor() const;
+
+ virtual bool eventFilter ( QObject *o, QEvent *e );
+
+ protected:
+ KexiQueryPart::TempData * tempData() const;
+
+ virtual tristate beforeSwitchTo(int mode, bool &dontStore);
+ virtual tristate afterSwitchFrom(int mode);
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+ virtual tristate storeData(bool dontAsk = false);
+
+ void setStatusOk();
+ void setStatusError(const QString& msg);
+ void setStatusEmpty();
+ void setStatusText(const QString& text);
+
+ virtual void updateActions(bool activated);
+
+ protected slots:
+ /*! Performs query checking (by text parsing). \return true and sets d->parsedQuery
+ to the new query schema object on success. */
+ bool slotCheckQuery();
+ void slotUpdateMode();
+ void slotTextChanged();
+// void slotHistoryHeaderButtonClicked(const QString& buttonIdentifier);
+ void slotSelectQuery();
+
+ signals:
+ void queryShortcut();
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class KexiQueryView; // for storeNewData() and storeData() only
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiquerydesignersqlhistory.cpp b/kexi/plugins/queries/kexiquerydesignersqlhistory.cpp
new file mode 100644
index 00000000..d86caf83
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersqlhistory.cpp
@@ -0,0 +1,373 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qpainter.h>
+#include <qclipboard.h>
+#include <qregexp.h>
+
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <kapplication.h>
+
+#include "kexiquerydesignersqlhistory.h"
+
+KexiQueryDesignerSQLHistory::KexiQueryDesignerSQLHistory(QWidget *parent, const char *name)
+ : QScrollView(parent, name)
+{
+ viewport()->setPaletteBackgroundColor(white);
+
+ m_selected = 0;
+ m_history = new History();
+ m_history->setAutoDelete(true);
+
+ m_popup = new KPopupMenu(this);
+ m_popup->insertItem(SmallIcon("editcopy"), i18n("Copy to Clipboard"), this, SLOT(slotToClipboard()));
+}
+
+KexiQueryDesignerSQLHistory::~KexiQueryDesignerSQLHistory()
+{
+}
+
+void
+KexiQueryDesignerSQLHistory::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ QRect clipping(cx, cy, cw, ch);
+
+ int y = 0;
+ for(HistoryEntry *it = m_history->first(); it; it = m_history->next())
+ {
+// it->drawItem(p, visibleWidth());
+ if(clipping.intersects(it->geometry(y, visibleWidth(), fontMetrics())))
+ {
+ p->saveWorldMatrix();
+ p->translate(0, y);
+ it->drawItem(p, visibleWidth(), colorGroup());
+ p->restoreWorldMatrix();
+ }
+ y += it->geometry(y, visibleWidth(), fontMetrics()).height() + 5;
+ }
+}
+
+void
+KexiQueryDesignerSQLHistory::contentsMousePressEvent(QMouseEvent * e)
+{
+ int y = 0;
+ HistoryEntry *popupHistory = 0;
+ int pos;
+ for(QPtrListIterator<HistoryEntry> it(*m_history); it.current(); ++it)
+ {
+ if(it.current()->isSelected())
+ {
+ //clear
+ it.current()->setSelected(false, colorGroup());
+ updateContents(it.current()->geometry(y, visibleWidth(), fontMetrics()));
+ }
+
+ if(it.current()->geometry(y, visibleWidth(), fontMetrics()).contains(e->pos()))
+ {
+ popupHistory = it.current();
+ pos = y;
+ }
+ y += it.current()->geometry(y, visibleWidth(), fontMetrics()).height() + 5;
+ }
+
+ //now do update
+ if (popupHistory) {
+ if (m_selected && m_selected != popupHistory) {
+ m_selected->setSelected(false, colorGroup());
+ updateContents(m_selected->geometry(pos, visibleWidth(), fontMetrics()));
+ }
+ m_selected = popupHistory;
+ m_selected->setSelected(true, colorGroup());
+ updateContents(m_selected->geometry(pos, visibleWidth(), fontMetrics()));
+ if(e->button() == RightButton) {
+ m_popup->exec(e->globalPos());
+ }
+ }
+}
+
+void
+KexiQueryDesignerSQLHistory::contentsMouseDoubleClickEvent(QMouseEvent * e)
+{
+ contentsMousePressEvent(e);
+ if (m_selected)
+ emit currentItemDoubleClicked();
+}
+
+void
+KexiQueryDesignerSQLHistory::addEvent(const QString& q, bool s, const QString &error)
+{
+ HistoryEntry *he=m_history->last();
+ if (he) {
+ if (he->statement()==q) {
+ he->updateTime(QTime::currentTime());
+ repaint();
+ return;
+ }
+ }
+ addEntry(new HistoryEntry(s, QTime::currentTime(), q, error));
+}
+
+void
+KexiQueryDesignerSQLHistory::addEntry(HistoryEntry *e)
+{
+ m_history->append(e);
+// m_history->prepend(e);
+
+ int y = 0;
+ for(HistoryEntry *it = m_history->first(); it; it = m_history->next())
+ {
+ y += it->geometry(y, visibleWidth(), fontMetrics()).height() + 5;
+ }
+
+ resizeContents(visibleWidth() - 1, y);
+ if (m_selected) {
+ m_selected->setSelected(false, colorGroup());
+ }
+ m_selected = e;
+ m_selected->setSelected(true, colorGroup());
+ ensureVisible(0,y+5);
+ updateContents();
+/* ensureVisible(0, 0);
+ if (m_selected) {
+ m_selected->setSelected(false, colorGroup());
+ }
+ m_selected = e;
+ m_selected->setSelected(true, colorGroup());
+// updateContents();
+ updateContents(m_selected->geometry(0, visibleWidth(), fontMetrics()));*/
+}
+
+/*void
+KexiQueryDesignerSQLHistory::contextMenu(const QPoint &pos, HistoryEntry *)
+{
+ KPopupMenu p(this);
+ p.insertItem(SmallIcon("editcopy"), i18n("Copy to Clipboard"), this, SLOT(slotToClipboard()));
+
+
+#ifndef KEXI_NO_UNFINISHED
+ p.insertSeparator();
+ p.insertItem(SmallIcon("edit"), i18n("Edit"), this, SLOT(slotEdit()));
+ p.insertItem(SmallIcon("reload"), i18n("Requery"));
+#endif
+
+ p.exec(pos);
+}*/
+
+void
+KexiQueryDesignerSQLHistory::slotToClipboard()
+{
+ if(!m_selected)
+ return;
+
+ QApplication::clipboard()->setText(m_selected->statement(), QClipboard::Clipboard);
+}
+
+void
+KexiQueryDesignerSQLHistory::slotEdit()
+{
+ emit editRequested(m_selected->statement());
+}
+
+QString
+KexiQueryDesignerSQLHistory::selectedStatement() const
+{
+ return m_selected ? m_selected->statement() : QString::null;
+}
+
+void
+KexiQueryDesignerSQLHistory::setHistory(History *h)
+{
+ m_history = h;
+ update();
+}
+
+void KexiQueryDesignerSQLHistory::clear()
+{
+ m_selected = 0;
+ m_history->clear();
+ updateContents();
+}
+
+KPopupMenu* KexiQueryDesignerSQLHistory::popupMenu() const
+{
+ return m_popup;
+}
+
+//==================================
+
+HistoryEntry::HistoryEntry(bool succeed, const QTime &execTime, const QString &statement, /*int ,*/ const QString &err)
+{
+ m_succeed = succeed;
+ m_execTime = execTime;
+ m_statement = statement;
+ m_error = err;
+ m_selected = false;
+ highlight(QColorGroup());
+}
+
+void
+HistoryEntry::drawItem(QPainter *p, int width, const QColorGroup &cg)
+{
+ p->setPen(QColor(200, 200, 200));
+ p->setBrush(QColor(200, 200, 200));
+ p->drawRect(2, 2, 200, 20);
+ p->setPen(QColor(0, 0, 0));
+
+ if(m_succeed)
+ p->drawPixmap(4, 4, SmallIcon("button_ok"));
+ else
+ p->drawPixmap(4, 4, SmallIcon("button_cancel"));
+
+ p->drawText(22, 2, 180, 20, Qt::AlignLeft | Qt::AlignVCenter, m_execTime.toString());
+ p->setPen(QColor(200, 200, 200));
+ p->setBrush(QColor(255, 255, 255));
+ m_formated->setWidth(width - 2);
+ QRect content(2, 21, width - 2, m_formated->height());
+// QRect content = p->fontMetrics().boundingRect(2, 21, width - 2, 0, Qt::WordBreak | Qt::AlignLeft | Qt::AlignVCenter, m_statement);
+// QRect content(2, 21, width - 2, p->fontMetrics().height() + 4);
+// content = QRect(2, 21, width - 2, m_for.height());
+
+ if(m_selected)
+ p->setBrush(cg.highlight());
+
+ p->drawRect(content);
+
+ if(!m_selected)
+ p->setPen(cg.text());
+ else
+ p->setPen(cg.highlightedText());
+
+ content.setX(content.x() + 2);
+ content.setWidth(content.width() - 2);
+// p->drawText(content, Qt::WordBreak | Qt::AlignLeft | Qt::AlignVCenter, m_statement);
+ m_formated->draw(p, content.x(), content.y(), content, cg);
+}
+
+void
+HistoryEntry::highlight(const QColorGroup &cg)
+{
+ QString statement;
+ QString text;
+ bool quote = false;
+ bool dblquote = false;
+
+ statement = m_statement;
+ statement.replace("<", "&lt;");
+ statement.replace(">", "&gt;");
+ statement.replace("\r\n", "<br>"); //(js) first win32 specific pair
+ statement.replace("\n", "<br>"); // now single \n
+ statement.replace(" ", "&nbsp;");
+ statement.replace("\t", "&nbsp;&nbsp;&nbsp;");
+
+ // getting quoting...
+ if(!m_selected)
+ {
+ for(int i=0; i < (int)statement.length(); i++)
+ {
+ QString beginTag;
+ QString endTag;
+ QChar curr = QChar(statement[i]);
+
+ if(curr == "'" && !dblquote && QChar(statement[i-1]) != "\\")
+ {
+ if(!quote)
+ {
+ quote = true;
+ beginTag += "<font color=\"#ff0000\">";
+ }
+ else
+ {
+ quote = false;
+ endTag += "</font>";
+ }
+ }
+ if(curr == "\"" && !quote && QChar(statement[i-1]) != "\\")
+ {
+ if(!dblquote)
+ {
+ dblquote = true;
+ beginTag += "<font color=\"#ff0000\">";
+ }
+ else
+ {
+ dblquote = false;
+ endTag += "</font>";
+ }
+ }
+ if(QRegExp("[0-9]").exactMatch(QString(curr)) && !quote && !dblquote)
+ {
+ beginTag += "<font color=\"#0000ff\">";
+ endTag += "</font>";
+ }
+
+ text += beginTag + curr + endTag;
+ }
+ }
+ else
+ {
+ text = QString("<font color=\"%1\">%2").arg(cg.highlightedText().name()).arg(statement);
+ }
+
+ QRegExp keywords("\\b(SELECT|UPDATE|INSERT|DELETE|DROP|FROM|WHERE|AND|OR|NOT|NULL|JOIN|LEFT|RIGHT|ON|INTO|TABLE)\\b");
+ keywords.setCaseSensitive(false);
+ text = text.replace(keywords, "<b>\\1</b>");
+
+ if(!m_error.isEmpty())
+// text += ("<br>"+i18n("Error: %1").arg(m_error));
+// text += QString("<br><font face=\"") + KGlobalSettings::generalFont().family() + QString("\" size=\"-1\">") + i18n("Error: %1").arg(m_error) + "</font>";
+ text += QString("<br><font face=\"") + KGlobalSettings::generalFont().family() + QString("\">") + i18n("Error: %1").arg(m_error) + "</font>";
+
+ kdDebug() << "HistoryEntry::highlight() text:" << text << endl;
+// m_formated = new QSimpleRichText(text, QFont("courier", 8));
+ m_formated = new QSimpleRichText(text, KGlobalSettings::fixedFont());
+
+}
+
+void
+HistoryEntry::setSelected(bool selected, const QColorGroup &cg)
+{
+ m_selected = selected;
+ highlight(cg);
+}
+
+QRect
+HistoryEntry::geometry(int y, int width, QFontMetrics f)
+{
+ Q_UNUSED( f );
+
+// int h = 21 + f.boundingRect(2, 21, width - 2, 0, Qt::WordBreak | Qt::AlignLeft | Qt::AlignVCenter, m_statement).height();
+// return QRect(0, y, width, h);
+ m_formated->setWidth(width - 2);
+ return QRect(0, y, width, m_formated->height() + 21);
+}
+
+void HistoryEntry::updateTime(const QTime &execTime) {
+ m_execTime=execTime;
+}
+
+HistoryEntry::~HistoryEntry()
+{
+}
+
+#include "kexiquerydesignersqlhistory.moc"
diff --git a/kexi/plugins/queries/kexiquerydesignersqlhistory.h b/kexi/plugins/queries/kexiquerydesignersqlhistory.h
new file mode 100644
index 00000000..a8d0c2e0
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerydesignersqlhistory.h
@@ -0,0 +1,104 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYDESIGNERSQLHISTORY_H
+#define KEXIQUERYDESIGNERSQLHISTORY_H
+
+#include <qscrollview.h>
+#include <qdatetime.h>
+#include <qptrlist.h>
+#include <qmap.h>
+#include <qsimplerichtext.h>
+
+class QSimpleRichText;
+class KPopupMenu;
+
+class HistoryEntry
+{
+ public:
+ HistoryEntry(bool success, const QTime &time, const QString &statement, /*int y,*/ const QString &error = QString::null);
+ ~HistoryEntry();
+
+ QRect geometry(int y, int width, QFontMetrics f);
+ void drawItem(QPainter *p, int width, const QColorGroup &cg);
+
+ void setSelected(bool selected, const QColorGroup &cg);
+ bool isSelected() const { return m_selected; }
+ void highlight(const QColorGroup &selected);
+
+ QString statement() { return m_statement; }
+ void updateTime(const QTime &execTime);
+
+ private:
+ bool m_succeed;
+ QTime m_execTime;
+ QString m_statement;
+ QString m_error;
+ QSimpleRichText *m_formated;
+
+ int m_y;
+ bool m_selected;
+};
+
+typedef QPtrList<HistoryEntry> History;
+
+class KexiQueryDesignerSQLHistory : public QScrollView
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryDesignerSQLHistory(QWidget *parent, const char *name=0);
+ virtual ~KexiQueryDesignerSQLHistory();
+
+ KPopupMenu* popupMenu() const;
+
+// void contextMenu(const QPoint &pos, HistoryEntry *e);
+
+ void setHistory(History *h);
+
+ QString selectedStatement() const;
+
+ public slots:
+ void addEvent(const QString& q, bool s, const QString &error);
+
+ void slotToClipboard();
+ void slotEdit();
+
+ void clear();
+
+// HistoryItem itemAt(int y);
+
+ protected:
+ void addEntry(HistoryEntry *e);
+ virtual void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ virtual void contentsMousePressEvent(QMouseEvent * e);
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent * e);
+
+ signals:
+ void editRequested(const QString &text);
+ void currentItemDoubleClicked();
+
+ private:
+ History *m_history;
+ HistoryEntry *m_selected;
+ KPopupMenu *m_popup;
+};
+
+#endif
diff --git a/kexi/plugins/queries/kexiqueryhandler.desktop b/kexi/plugins/queries/kexiqueryhandler.desktop
new file mode 100644
index 00000000..4a4f478e
--- /dev/null
+++ b/kexi/plugins/queries/kexiqueryhandler.desktop
@@ -0,0 +1,111 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kexi/Handler
+
+GenericName=Queries
+GenericName[bg]=Заявки
+GenericName[ca]=Consultes
+GenericName[cs]=Dotazy
+GenericName[cy]=Ymholiadau
+GenericName[da]=Forespørgsler
+GenericName[de]=Abfragen
+GenericName[el]=Ερωτήματα
+GenericName[eo]=Serĉmendoj
+GenericName[es]=Consultas
+GenericName[et]=Päringud
+GenericName[eu]=Kontsultak
+GenericName[fa]=پرس‌و‌جوها
+GenericName[fi]=Kyselyt
+GenericName[fr]=Requêtes
+GenericName[ga]=Iarratais
+GenericName[gl]=Pesquisas
+GenericName[he]=שאילתות
+GenericName[hi]=क्वैरीज़
+GenericName[hr]=Upiti
+GenericName[hu]=Lekérdezések
+GenericName[is]=Fyrirspurnir
+GenericName[it]=Interrogazioni
+GenericName[ja]=クエリ
+GenericName[km]=សំណួរ​
+GenericName[lt]=Užklausos
+GenericName[lv]=Vaicājumi
+GenericName[ms]=Pertanyaan
+GenericName[nb]=Spørringer
+GenericName[nds]=Affragen
+GenericName[ne]=क्वेरीहरू
+GenericName[nn]=Spørjingar
+GenericName[pl]=Zapytania
+GenericName[pt]=Pesquisas
+GenericName[pt_BR]=Consultas
+GenericName[ru]=Запросы
+GenericName[se]=Jearahusat
+GenericName[sk]=Otázky
+GenericName[sl]=Poizvedbe
+GenericName[sr]=Упити
+GenericName[sr@Latn]=Upiti
+GenericName[sv]=Förfrågningar
+GenericName[ta]=கேள்விகள்
+GenericName[tr]=Sorgular
+GenericName[uk]=Запити
+GenericName[uz]=Soʻrovlar
+GenericName[uz@cyrillic]=Сўровлар
+GenericName[zh_CN]=查询
+GenericName[zh_TW]=查詢
+Name=Queries
+Name[bg]=Заявки
+Name[ca]=Consultes
+Name[cs]=Dotazy
+Name[cy]=Ymholiadau
+Name[da]=Forespørgsler
+Name[de]=Abfragen
+Name[el]=Ερωτήματα
+Name[eo]=Serĉmendoj
+Name[es]=Consultas
+Name[et]=Päringud
+Name[eu]=Kontsultak
+Name[fa]=پرس‌و‌جوها
+Name[fi]=Kyselyt
+Name[fr]=Requêtes
+Name[ga]=Iarratais
+Name[gl]=Pesquisas
+Name[he]=שאילתות
+Name[hi]=क्वैरीज़
+Name[hr]=Upiti
+Name[hu]=Lekérdezések
+Name[is]=Fyrirspurnir
+Name[it]=Interrogazioni
+Name[ja]=クエリ
+Name[km]=សំណួរ​
+Name[lt]=Užklausos
+Name[lv]=Vaicājumi
+Name[ms]=Pertanyaan
+Name[nb]=Spørringer
+Name[nds]=Affragen
+Name[ne]=क्वेरीहरू
+Name[nn]=Spørjingar
+Name[pl]=Zapytania
+Name[pt]=Procuras
+Name[pt_BR]=Consultas
+Name[ru]=Запросы
+Name[se]=Jearahusat
+Name[sk]=Otázky
+Name[sl]=Poizvedbe
+Name[sr]=Упити
+Name[sr@Latn]=Upiti
+Name[sv]=Förfrågningar
+Name[ta]=கேள்விகள்
+Name[tg]=Талаботҳо
+Name[tr]=Sorgular
+Name[uk]=Запити
+Name[uz]=Talablar
+Name[uz@cyrillic]=Талаблар
+Name[zh_CN]=查询
+Name[zh_TW]=查詢
+X-KDE-Library=kexihandler_query
+X-KDE-ParentApp=kexi
+X-Kexi-PartVersion=2
+X-Kexi-TypeName=query
+X-Kexi-TypeMime=kexi/query
+X-Kexi-ItemIcon=query
+X-Kexi-SupportsDataExport=true
+X-Kexi-SupportsPrinting=true
diff --git a/kexi/plugins/queries/kexiquerypart.cpp b/kexi/plugins/queries/kexiquerypart.cpp
new file mode 100644
index 00000000..6cecfcf1
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypart.cpp
@@ -0,0 +1,310 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include "kexiquerypart.h"
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include <keximainwindow.h>
+#include <kexidialogbase.h>
+#include <kexiproject.h>
+#include <kexipartinfo.h>
+
+#include <kexidb/cursor.h>
+#include <kexidb/parser/parser.h>
+
+#include "kexiqueryview.h"
+#include "kexiquerydesignerguieditor.h"
+#include "kexiquerydesignersql.h"
+
+//------------------------------------------------
+
+KexiQueryPart::KexiQueryPart(QObject *parent, const char *name, const QStringList &l)
+ : KexiPart::Part(parent, name, l)
+{
+ // REGISTERED ID:
+ m_registeredPartID = (int)KexiPart::QueryObjectType;
+
+ 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.",
+ "query");
+ m_names["instanceCaption"] = i18n("Query");
+ m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode | Kexi::TextViewMode;
+}
+
+KexiQueryPart::~KexiQueryPart()
+{
+}
+
+KexiDialogTempData*
+KexiQueryPart::createTempData(KexiDialogBase* dialog)
+{
+ KexiQueryPart::TempData *data = new KexiQueryPart::TempData(dialog, dialog->mainWin()->project()->dbConnection());
+ data->listenerInfoString = dialog->part()->instanceCaption() + " \"" + dialog->partItem()->name() + "\"";
+ return data;
+}
+
+KexiViewBase*
+KexiQueryPart::createView(QWidget *parent, KexiDialogBase* dialog, KexiPart::Item &item, int viewMode, QMap<QString,QString>*)
+{
+ Q_UNUSED( item );
+
+ kdDebug() << "KexiQueryPart::createView()" << endl;
+
+ if (viewMode == Kexi::DataViewMode) {
+ return new KexiQueryView(dialog->mainWin(), parent, "dataview");
+ }
+ else if (viewMode == Kexi::DesignViewMode) {
+ KexiQueryDesignerGuiEditor* view = new KexiQueryDesignerGuiEditor(
+ dialog->mainWin(), parent, "guieditor");
+ //needed for updating tables combo box:
+ KexiProject *prj = dialog->mainWin()->project();
+ connect(prj, SIGNAL(newItemStored(KexiPart::Item&)),
+ view, SLOT(slotNewItemStored(KexiPart::Item&)));
+ connect(prj, SIGNAL(itemRemoved(const KexiPart::Item&)),
+ view, SLOT(slotItemRemoved(const KexiPart::Item&)));
+ connect(prj, SIGNAL(itemRenamed(const KexiPart::Item&, const QCString&)),
+ view, SLOT(slotItemRenamed(const KexiPart::Item&, const QCString&)));
+
+// connect(dialog->mainWin()->project(), SIGNAL(tableCreated(KexiDB::TableSchema&)),
+// view, SLOT(slotTableCreated(KexiDB::TableSchema&)));
+ return view;
+ }
+ else if (viewMode == Kexi::TextViewMode) {
+ return new KexiQueryDesignerSQLView(dialog->mainWin(), parent, "sqldesigner");
+ }
+
+ return 0;
+}
+
+bool
+KexiQueryPart::remove(KexiMainWindow *win, KexiPart::Item &item)
+{
+ if (!win || !win->project() || !win->project()->dbConnection())
+ return false;
+ KexiDB::Connection *conn = win->project()->dbConnection();
+ KexiDB::QuerySchema *sch = conn->querySchema(item.identifier());
+ if (sch)
+ return conn->dropQuery( sch );
+ //last chance: just remove item
+ return conn->removeObject( item.identifier() );
+}
+
+#if 0
+KexiPart::DataSource *
+KexiQueryPart::dataSource()
+{
+ return new KexiQueryDataSource(this);
+}
+
+void KexiQueryPart::initPartActions( KActionCollection *col )
+{
+}
+
+void KexiQueryPart::initInstanceActions( int mode, KActionCollection *col )
+{
+ if (mode==Kexi::DataViewMode) {
+ }
+ else if (mode==Kexi::DesignViewMode) {
+ }
+ else if (mode==Kexi::TextViewMode) {
+// new KAction(i18n("Check Query"), "test_it", 0, this, SLOT(slotCheckQuery()), col, "querypart_check_query");
+
+//TODO new KAction(i18n("Execute Query"), "?????", 0, this, SLOT(checkQuery()), col, "querypart_execute_query");
+ }
+}
+#endif
+
+void KexiQueryPart::initPartActions()
+{
+}
+
+void KexiQueryPart::initInstanceActions()
+{
+// new KAction(i18n("Check Query"), "test_it", 0, this, SLOT(slotCheckQuery()),
+// m_instanceGuiClients[Kexi::DesignViewMode]->actionCollection(), "querypart_check_query");
+
+ KAction *a = createSharedAction(Kexi::TextViewMode, i18n("Check Query"), "test_it",
+ Key_F9, "querypart_check_query");
+ a->setToolTip(i18n("Check Query"));
+ a->setWhatsThis(i18n("Checks query for validity."));
+
+ a = createSharedToggleAction(
+ Kexi::TextViewMode, i18n("Show SQL History"), "view_top_bottom"/*TODO other icon*/,
+ 0, "querypart_view_toggle_history");
+ a->setWhatsThis(i18n("Shows or hides SQL editor's history."));
+
+// setActionAvailable("querypart_check_query", true);
+}
+
+KexiDB::SchemaData*
+KexiQueryPart::loadSchemaData(KexiDialogBase *dlg, const KexiDB::SchemaData& sdata, int viewMode)
+{
+ KexiQueryPart::TempData * temp = static_cast<KexiQueryPart::TempData*>(dlg->tempData());
+ QString sqlText;
+ if (!loadDataBlock( dlg, sqlText, "sql" )) {
+ return 0;
+ }
+ KexiDB::Parser *parser = dlg->mainWin()->project()->sqlParser();
+ parser->parse( sqlText );
+ KexiDB::QuerySchema *query = parser->query();
+ //error?
+ if (!query) {
+ if (viewMode==Kexi::TextViewMode) {
+ //for SQL view, no parsing is initially needed:
+ //-just make a copy:
+ return KexiPart::Part::loadSchemaData(dlg, sdata, viewMode);
+ }
+ /* Set this to true on data loading loadSchemaData() to indicate that TextView mode
+ could be used instead of DataView or DesignView, because there are problems
+ with opening object. */
+ temp->proposeOpeningInTextViewModeBecauseOfProblems = true;
+ //todo
+ return 0;
+ }
+ query->debug();
+ (KexiDB::SchemaData&)*query = sdata; //copy main attributes
+
+ temp->registerTableSchemaChanges(query);
+
+ query->debug();
+ return query;
+}
+
+QString KexiQueryPart::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const
+{
+ Q_UNUSED(dlg);
+ if (englishMessage=="Design of object \"%1\" has been modified.")
+ return i18n("Design of query \"%1\" has been modified.");
+ if (englishMessage=="Object \"%1\" already exists.")
+ return i18n("Query \"%1\" already exists.");
+
+ return englishMessage;
+}
+
+tristate KexiQueryPart::rename(KexiMainWindow *win, KexiPart::Item &item, const QString& newName)
+{
+ Q_UNUSED(newName);
+ if (!win->project()->dbConnection())
+ return false;
+ win->project()->dbConnection()->setQuerySchemaObsolete( item.name() );
+ return true;
+}
+
+//----------------
+
+KexiQueryPart::TempData::TempData(KexiDialogBase* parent, KexiDB::Connection *conn)
+ : KexiDialogTempData(parent)
+ , KexiDB::Connection::TableSchemaChangeListenerInterface()
+ , queryChangedInPreviousView(false)
+ , m_query(0)
+{
+ this->conn = conn;
+}
+
+KexiQueryPart::TempData::~TempData()
+{
+ conn->unregisterForTablesSchemaChanges(*this);
+}
+
+void KexiQueryPart::TempData::clearQuery()
+{
+ if (!m_query)
+ return;
+ unregisterForTablesSchemaChanges();
+ m_query->clear();
+}
+
+void KexiQueryPart::TempData::unregisterForTablesSchemaChanges()
+{
+ conn->unregisterForTablesSchemaChanges(*this);
+}
+
+void KexiQueryPart::TempData::registerTableSchemaChanges(KexiDB::QuerySchema *q)
+{
+ if (!q)
+ return;
+ for (KexiDB::TableSchema::ListIterator it(*q->tables());
+ it.current(); ++it)
+ {
+ conn->registerForTableSchemaChanges(*this, *it.current());
+ }
+}
+
+tristate KexiQueryPart::TempData::closeListener()
+{
+ KexiDialogBase* dlg = static_cast<KexiDialogBase*>(parent());
+ return dlg->mainWin()->closeDialog(dlg);
+}
+
+KexiDB::QuerySchema *KexiQueryPart::TempData::takeQuery()
+{
+ KexiDB::QuerySchema *query = m_query;
+ m_query = 0;
+ return query;
+}
+
+void KexiQueryPart::TempData::setQuery(KexiDB::QuerySchema *query)
+{
+ if (m_query && m_query == query)
+ return;
+ if (m_query
+ /* query not owned by dialog */
+ && (static_cast<KexiDialogBase*>(parent())->schemaData() != static_cast<KexiDB::SchemaData*>( m_query )))
+ {
+ delete m_query;
+ }
+ m_query = query;
+}
+
+//----------------
+
+#if 0
+KexiQueryDataSource::KexiQueryDataSource(KexiPart::Part *part)
+ : KexiPart::DataSource(part)
+{
+}
+
+KexiQueryDataSource::~KexiQueryDataSource()
+{
+}
+
+KexiDB::FieldList *
+KexiQueryDataSource::fields(KexiProject *, const KexiPart::Item &)
+{
+ return 0;
+}
+
+KexiDB::Cursor *
+KexiQueryDataSource::cursor(KexiProject *, const KexiPart::Item &, bool)
+{
+ return 0;
+}
+#endif
+
+//----------------
+
+K_EXPORT_COMPONENT_FACTORY( kexihandler_query, KGenericFactory<KexiQueryPart>("kexihandler_query") )
+
+#include "kexiquerypart.moc"
+
diff --git a/kexi/plugins/queries/kexiquerypart.h b/kexi/plugins/queries/kexiquerypart.h
new file mode 100644
index 00000000..6b16f28d
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypart.h
@@ -0,0 +1,118 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004,2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYPART_H
+#define KEXIQUERYPART_H
+
+#include <qmap.h>
+
+#include <kexidialogbase.h>
+#include <kexipart.h>
+#include <kexipartitem.h>
+//#include <kexipartdatasource.h>
+
+#include <kexidb/queryschema.h>
+#include <kexidb/connection.h>
+
+class KexiMainWin;
+namespace KexiDB
+{
+ class QuerySchema;
+ class Connection;
+}
+
+class KexiProject;
+
+//! @short Kexi Query Designer Plugin.
+class KexiQueryPart : public KexiPart::Part
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryPart(QObject *parent, const char *name, const QStringList &);
+ virtual ~KexiQueryPart();
+
+ virtual bool remove(KexiMainWindow *win, KexiPart::Item &item);
+
+ //! @short Temporary data kept in memory while switching between Query Dialog's views
+ class TempData : public KexiDialogTempData,
+ public KexiDB::Connection::TableSchemaChangeListenerInterface
+ {
+ public:
+ TempData(KexiDialogBase* parent, KexiDB::Connection *conn);
+ virtual ~TempData();
+ virtual tristate closeListener();
+ void clearQuery();
+ void unregisterForTablesSchemaChanges();
+ void registerTableSchemaChanges(KexiDB::QuerySchema *q);
+
+ /*! Assigns query \a query for this data.
+ Existing query (available using query()) is deleted but only
+ if it is not owned by parent dialog (i.e. != KexiDialogBase::schemaData()).
+ \a query can be 0.
+ If \a query is equal to existing query, nothing is performed.
+ */
+ void setQuery(KexiDB::QuerySchema *query);
+
+ //! \return query associated with this data
+ KexiDB::QuerySchema *query() const { return m_query; }
+
+ //! Takes query associated with this data (without deleting) and returns it.
+ //! After this call query() == 0
+ KexiDB::QuerySchema *takeQuery();
+
+ //! Connection used for retrieving definition of the query
+ KexiDB::Connection *conn;
+
+ /*! true, if \a query member has changed in previous view.
+ Used on view switching. We're checking this flag to see if we should
+ rebuild internal structure for DesignViewMode of regenerated sql text
+ in TextViewMode after switch from other view. */
+ bool queryChangedInPreviousView : 1;
+
+ protected:
+ KexiDB::QuerySchema *m_query;
+ };
+
+ virtual QString i18nMessage(const QCString& englishMessage,
+ KexiDialogBase* dlg) const;
+
+ /*! Renames stored data pointed by \a item to \a newName.
+ Reimplemented to mark the query obsolete by using KexiDB::Connection::setQuerySchemaObsolete(). */
+ virtual tristate rename(KexiMainWindow * win, KexiPart::Item & item, const QString& newName);
+
+ 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( KActionCollection *col );
+// virtual void initInstanceActions( int mode, KActionCollection *col );
+
+ virtual void initPartActions();
+ virtual void initInstanceActions();
+
+ virtual KexiDB::SchemaData* loadSchemaData(KexiDialogBase *dlg,
+ const KexiDB::SchemaData& sdata, int viewMode);
+};
+
+#endif
+
diff --git a/kexi/plugins/queries/kexiquerypartinstui.rc b/kexi/plugins/queries/kexiquerypartinstui.rc
new file mode 100644
index 00000000..405c4377
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypartinstui.rc
@@ -0,0 +1,24 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiquerypartinst" version="4">
+
+<MenuBar>
+ <Menu name="view" noMerge="1">
+ <text>&amp;View</text>
+ <Action name="querypart_view_toggle_history"/>
+ </Menu>
+ <Menu name="data">
+ <text>&amp;Data</text>
+ <Action name="querypart_check_query"/>
+ <Action name="querypart_execute_query"/>
+ <Merge/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="designToolBar" fullWidth="false" noMerge="0">
+ <text>Design</text>
+ <Action name="querypart_check_query"/>
+ <Action name="querypart_execute_query"/>
+ <Action name="querypart_view_toggle_history"/>
+</ToolBar>
+
+</kpartgui>
diff --git a/kexi/plugins/queries/kexiquerypartui.rc b/kexi/plugins/queries/kexiquerypartui.rc
new file mode 100644
index 00000000..5b384aea
--- /dev/null
+++ b/kexi/plugins/queries/kexiquerypartui.rc
@@ -0,0 +1,11 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kexiquerypart" version="2">
+
+<MenuBar>
+ <!-- (not needed - it is autogenerated!) <Menu name="widgets">
+ <text>&amp;Create</text>
+ <Action name="querypart_create"/>
+ </Menu -->
+</MenuBar>
+
+</kpartgui>
diff --git a/kexi/plugins/queries/kexiqueryview.cpp b/kexi/plugins/queries/kexiqueryview.cpp
new file mode 100644
index 00000000..cf3fee96
--- /dev/null
+++ b/kexi/plugins/queries/kexiqueryview.cpp
@@ -0,0 +1,154 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004, 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <kexiproject.h>
+#include <kexidb/connection.h>
+#include <kexidb/parser/parser.h>
+#include <kexidb/cursor.h>
+#include <keximainwindow.h>
+#include <kexiutils/utils.h>
+
+#include "kexiqueryview.h"
+#include "kexiquerydesignersql.h"
+#include "kexiquerydesignerguieditor.h"
+#include "kexiquerypart.h"
+#include <widget/tableview/kexitableview.h>
+#include <widget/kexiqueryparameters.h>
+
+//! @internal
+class KexiQueryView::Private
+{
+ public:
+ Private()
+ : cursor(0)
+// , queryHasBeenChangedInViewMode( Kexi::NoViewMode )
+ {}
+ ~Private() {}
+ KexiDB::Cursor *cursor;
+ /*! Used in storeNewData(), storeData() to decide whether
+ we should ask other view to save changes.
+ Stores information about view mode. */
+// int queryHasBeenChangedInViewMode;
+};
+
+//---------------------------------------------------------------------------------
+
+KexiQueryView::KexiQueryView(KexiMainWindow *win, QWidget *parent, const char *name)
+ : KexiDataTable(win, parent, name)
+ , d( new Private() )
+{
+ tableView()->setInsertingEnabled(false); //default
+}
+
+KexiQueryView::~KexiQueryView()
+{
+ if (d->cursor)
+ d->cursor->connection()->deleteCursor(d->cursor);
+ delete d;
+}
+
+tristate KexiQueryView::executeQuery(KexiDB::QuerySchema *query)
+{
+ if (!query)
+ return false;
+ KexiUtils::WaitCursor wait;
+ KexiDB::Cursor *oldCursor = d->cursor;
+ KexiDB::debug( query->parameters() );
+ bool ok;
+ QValueList<QVariant> params;
+ {
+ KexiUtils::WaitCursorRemover remover;
+ params = KexiQueryParameters::getParameters(this,
+ *mainWin()->project()->dbConnection()->driver(), *query, ok);
+ }
+ if (!ok) {//input cancelled
+ return cancelled;
+ }
+ d->cursor = mainWin()->project()->dbConnection()->executeQuery(*query, params);
+ if (!d->cursor) {
+ parentDialog()->setStatus(parentDialog()->mainWin()->project()->dbConnection(),
+ i18n("Query executing failed."));
+ //todo: also provide server result and sql statement
+ return false;
+ }
+ setData(d->cursor);
+
+//! @todo remove close() when dynamic cursors arrive
+ d->cursor->close();
+
+ if (oldCursor)
+ oldCursor->connection()->deleteCursor(oldCursor);
+
+//! @todo maybe allow writing and inserting for single-table relations?
+ tableView()->setReadOnly( true );
+//! @todo maybe allow writing and inserting for single-table relations?
+ //set data model itself read-only too
+ tableView()->data()->setReadOnly( true );
+ tableView()->setInsertingEnabled( false );
+ return true;
+}
+
+tristate KexiQueryView::afterSwitchFrom(int mode)
+{
+ if (mode==Kexi::NoViewMode) {
+ KexiDB::QuerySchema *querySchema = static_cast<KexiDB::QuerySchema *>(parentDialog()->schemaData());
+ const tristate result = executeQuery(querySchema);
+ if (true != result)
+ return result;
+ }
+ else if (mode==Kexi::DesignViewMode || Kexi::TextViewMode) {
+ KexiQueryPart::TempData * temp = static_cast<KexiQueryPart::TempData*>(parentDialog()->tempData());
+
+ //remember what view we should use to store data changes, if needed
+// if (temp->queryChangedInPreviousView)
+// d->queryHasBeenChangedInViewMode = mode;
+// else
+// d->queryHasBeenChangedInViewMode = Kexi::NoViewMode;
+
+ const tristate result = executeQuery(temp->query());
+ if (true != result)
+ return result;
+ }
+ return true;
+}
+
+KexiDB::SchemaData* KexiQueryView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel)
+{
+ KexiViewBase * view = parentDialog()->viewThatRecentlySetDirtyFlag();
+ if (dynamic_cast<KexiQueryDesignerGuiEditor*>(view))
+ return dynamic_cast<KexiQueryDesignerGuiEditor*>(view)->storeNewData(sdata, cancel);
+ if (dynamic_cast<KexiQueryDesignerSQLView*>(view))
+ return dynamic_cast<KexiQueryDesignerSQLView*>(view)->storeNewData(sdata, cancel);
+ return 0;
+}
+
+tristate KexiQueryView::storeData(bool dontAsk)
+{
+ KexiViewBase * view = parentDialog()->viewThatRecentlySetDirtyFlag();
+ if (dynamic_cast<KexiQueryDesignerGuiEditor*>(view))
+ return dynamic_cast<KexiQueryDesignerGuiEditor*>(view)->storeData(dontAsk);
+ if (dynamic_cast<KexiQueryDesignerSQLView*>(view))
+ return dynamic_cast<KexiQueryDesignerSQLView*>(view)->storeData(dontAsk);
+ return false;
+}
+
+
+#include "kexiqueryview.moc"
+
diff --git a/kexi/plugins/queries/kexiqueryview.h b/kexi/plugins/queries/kexiqueryview.h
new file mode 100644
index 00000000..f0083738
--- /dev/null
+++ b/kexi/plugins/queries/kexiqueryview.h
@@ -0,0 +1,58 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2004, 2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEXIQUERYVIEW_H
+#define KEXIQUERYVIEW_H
+
+#include <kexidatatable.h>
+
+namespace KexiDB
+{
+ class QuerySchema;
+}
+class KexiMainWindow;
+
+class KexiQueryView : public KexiDataTable
+{
+ Q_OBJECT
+
+ public:
+ KexiQueryView(KexiMainWindow *win, QWidget *parent, const char *name=0);
+ ~KexiQueryView();
+
+ protected:
+ virtual tristate afterSwitchFrom(int mode);
+
+ virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel);
+
+ virtual tristate storeData(bool dontAsk = false);
+
+ /*! Executes query \a query, filling the table view with query results.
+ \return true on success, false on failure and cancelled when user has
+ cancelled execution (for example when she pressed the Cancel button
+ of the "Enter Query Parameter" input dialog. */
+ tristate executeQuery(KexiDB::QuerySchema *query);
+
+ class Private;
+ Private *d;
+};
+
+#endif
+