From 8362bf63dea22bbf6736609b0f49c152f975eb63 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 20 Jan 2010 01:29:50 +0000 Subject: 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 --- kexi/plugins/scripting/Makefile.am | 1 + kexi/plugins/scripting/README | 28 + kexi/plugins/scripting/kexiapp/Makefile.am | 21 + .../scripting/kexiapp/kexiappmainwindow.cpp | 106 ++++ kexi/plugins/scripting/kexiapp/kexiappmainwindow.h | 91 +++ kexi/plugins/scripting/kexiapp/kexiappmodule.cpp | 97 ++++ kexi/plugins/scripting/kexiapp/kexiappmodule.h | 76 +++ kexi/plugins/scripting/kexiapp/kexiapppart.cpp | 46 ++ kexi/plugins/scripting/kexiapp/kexiapppart.h | 56 ++ kexi/plugins/scripting/kexidb.doxyfile | 324 +++++++++++ kexi/plugins/scripting/kexidb/Makefile.am | 30 + kexi/plugins/scripting/kexidb/kexidbconnection.cpp | 221 +++++++ kexi/plugins/scripting/kexidb/kexidbconnection.h | 194 +++++++ .../scripting/kexidb/kexidbconnectiondata.cpp | 112 ++++ .../scripting/kexidb/kexidbconnectiondata.h | 126 ++++ kexi/plugins/scripting/kexidb/kexidbcursor.cpp | 139 +++++ kexi/plugins/scripting/kexidb/kexidbcursor.h | 159 +++++ kexi/plugins/scripting/kexidb/kexidbdriver.cpp | 70 +++ kexi/plugins/scripting/kexidb/kexidbdriver.h | 114 ++++ .../scripting/kexidb/kexidbdrivermanager.cpp | 178 ++++++ .../plugins/scripting/kexidb/kexidbdrivermanager.h | 105 ++++ kexi/plugins/scripting/kexidb/kexidbfield.cpp | 147 +++++ kexi/plugins/scripting/kexidb/kexidbfield.h | 148 +++++ kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp | 100 ++++ kexi/plugins/scripting/kexidb/kexidbfieldlist.h | 104 ++++ kexi/plugins/scripting/kexidb/kexidbmodule.cpp | 74 +++ kexi/plugins/scripting/kexidb/kexidbmodule.h | 69 +++ kexi/plugins/scripting/kexidb/kexidbparser.cpp | 77 +++ kexi/plugins/scripting/kexidb/kexidbparser.h | 95 +++ kexi/plugins/scripting/kexidb/kexidbschema.cpp | 197 +++++++ kexi/plugins/scripting/kexidb/kexidbschema.h | 134 +++++ .../plugins/scripting/kexidb/kexidbtransaction.cpp | 52 ++ kexi/plugins/scripting/kexidb/kexidbtransaction.h | 62 ++ kexi/plugins/scripting/kexidb/readme.dox | 32 + kexi/plugins/scripting/kexiscripting/Makefile.am | 37 ++ .../kexiscripting/kexiscriptdesignview.cpp | 337 +++++++++++ .../scripting/kexiscripting/kexiscriptdesignview.h | 124 ++++ .../scripting/kexiscripting/kexiscripteditor.cpp | 104 ++++ .../scripting/kexiscripting/kexiscripteditor.h | 77 +++ .../kexiscripting/kexiscripthandler.desktop | 105 ++++ .../scripting/kexiscripting/kexiscriptpart.cpp | 201 +++++++ .../scripting/kexiscripting/kexiscriptpart.h | 100 ++++ .../kexiscripting/kexiscriptpartinstui.rc | 10 + .../scripting/kexiscripting/kexiscriptpartui.rc | 10 + kexi/plugins/scripting/scripts/Makefile.am | 1 + .../scripting/scripts/copycenter/CopyCenter.py | 644 ++++++++++++++++++++ .../scripting/scripts/copycenter/CopyCenter.rc | 10 + .../scripts/copycenter/CopyCenterPluginKexiDB.py | 646 +++++++++++++++++++++ .../scripts/copycenter/CopyCenterPluginQtSQL.py | 495 ++++++++++++++++ .../scripting/scripts/copycenter/Makefile.am | 4 + .../scripting/scripts/copycenter/readme.html | 20 + .../scripting/scripts/exportxhtml/ExportXHTML.py | 196 +++++++ .../scripting/scripts/exportxhtml/ExportXHTML.rc | 8 + .../scripting/scripts/exportxhtml/Makefile.am | 4 + .../scripting/scripts/importxhtml/ImportXHTML.py | 434 ++++++++++++++ .../scripting/scripts/importxhtml/ImportXHTML.rc | 8 + .../scripting/scripts/importxhtml/Makefile.am | 4 + .../scripts/projectdocumentor/Makefile.am | 4 + .../scripts/projectdocumentor/ProjectDocumentor.py | 186 ++++++ .../scripts/projectdocumentor/ProjectDocumentor.rc | 8 + kexi/plugins/scripting/scripts/python/Makefile.am | 2 + .../scripting/scripts/python/kexiapp/Makefile.am | 2 + .../scripting/scripts/python/kexiapp/__init__.py | 25 + 63 files changed, 7391 insertions(+) create mode 100644 kexi/plugins/scripting/Makefile.am create mode 100644 kexi/plugins/scripting/README create mode 100644 kexi/plugins/scripting/kexiapp/Makefile.am create mode 100644 kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp create mode 100644 kexi/plugins/scripting/kexiapp/kexiappmainwindow.h create mode 100644 kexi/plugins/scripting/kexiapp/kexiappmodule.cpp create mode 100644 kexi/plugins/scripting/kexiapp/kexiappmodule.h create mode 100644 kexi/plugins/scripting/kexiapp/kexiapppart.cpp create mode 100644 kexi/plugins/scripting/kexiapp/kexiapppart.h create mode 100644 kexi/plugins/scripting/kexidb.doxyfile create mode 100644 kexi/plugins/scripting/kexidb/Makefile.am create mode 100644 kexi/plugins/scripting/kexidb/kexidbconnection.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbconnection.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbconnectiondata.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbcursor.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbcursor.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbdriver.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbdriver.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbdrivermanager.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbfield.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbfield.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbfieldlist.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbmodule.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbmodule.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbparser.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbparser.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbschema.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbschema.h create mode 100644 kexi/plugins/scripting/kexidb/kexidbtransaction.cpp create mode 100644 kexi/plugins/scripting/kexidb/kexidbtransaction.h create mode 100644 kexi/plugins/scripting/kexidb/readme.dox create mode 100644 kexi/plugins/scripting/kexiscripting/Makefile.am create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscripteditor.h create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscriptpart.h create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc create mode 100644 kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc create mode 100644 kexi/plugins/scripting/scripts/Makefile.am create mode 100644 kexi/plugins/scripting/scripts/copycenter/CopyCenter.py create mode 100644 kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc create mode 100644 kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py create mode 100644 kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py create mode 100644 kexi/plugins/scripting/scripts/copycenter/Makefile.am create mode 100644 kexi/plugins/scripting/scripts/copycenter/readme.html create mode 100644 kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py create mode 100644 kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc create mode 100644 kexi/plugins/scripting/scripts/exportxhtml/Makefile.am create mode 100755 kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py create mode 100644 kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc create mode 100644 kexi/plugins/scripting/scripts/importxhtml/Makefile.am create mode 100644 kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am create mode 100755 kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py create mode 100644 kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc create mode 100644 kexi/plugins/scripting/scripts/python/Makefile.am create mode 100644 kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am create mode 100755 kexi/plugins/scripting/scripts/python/kexiapp/__init__.py (limited to 'kexi/plugins/scripting') diff --git a/kexi/plugins/scripting/Makefile.am b/kexi/plugins/scripting/Makefile.am new file mode 100644 index 00000000..ccd1bd64 --- /dev/null +++ b/kexi/plugins/scripting/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = kexiscripting kexiapp kexidb scripts diff --git a/kexi/plugins/scripting/README b/kexi/plugins/scripting/README new file mode 100644 index 00000000..e754b9cd --- /dev/null +++ b/kexi/plugins/scripting/README @@ -0,0 +1,28 @@ +Kexi Scripting README +--------------------- + +The code in this directory implements a scripting plugin for +Kexi. The Kross Scripting Framework located at koffice/libs/kross +is used to embed scripting interpreters and access Kexi +functionality from within those interpreters. + +See also http://www.kexi-project.org/wiki/wikiview/index.php?Scripting + +/kexiscripting/ +The scripting-plugin which will be loaded by Kexi at startup to +embed Kross into Kexi. + +/kexiapp/ +Access to a running Kexi application. Kexi itself takes care of +publishing it's KexiMainWindowImpl instance and the kexiapp-plugin +provides access to some of the applications functionality at runtime. + +/kexidb/ +Kross-plugin to provide nearly the whole KexiDB-framework to scripting +interpreters. That way we are able to read/write from/to all by KexiDB +supported databases. + +/scripts/ +Kexi-dependend scripts. This directory holds our in python or ruby +written scripting extensions. Those extensions are just plugins for +Kexi to extend it's functionality. diff --git a/kexi/plugins/scripting/kexiapp/Makefile.am b/kexi/plugins/scripting/kexiapp/Makefile.am new file mode 100644 index 00000000..a2702a26 --- /dev/null +++ b/kexi/plugins/scripting/kexiapp/Makefile.am @@ -0,0 +1,21 @@ +include $(top_srcdir)/kexi/Makefile.global + +INCLUDES = -I$(top_srcdir)/kexi $(KROSS_INCLUDES) $(all_includes) + +kde_module_LTLIBRARIES = krosskexiapp.la + +krosskexiapp_la_SOURCES = \ + kexiapppart.cpp \ + kexiappmainwindow.cpp \ + kexiappmodule.cpp + +krosskexiapp_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module +krosskexiapp_la_LIBADD = \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + $(LIB_KROSS_API) \ + $(LIB_KROSS_MAIN) \ + $(top_builddir)/kexi/core/libkexicore.la + +METASOURCES = AUTO +SUBDIRS = . diff --git a/kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp new file mode 100644 index 00000000..4d82bc5d --- /dev/null +++ b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + * kexiappmainwindow.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexiappmainwindow.h" +#include "kexiapppart.h" + +#include "core/keximainwindow.h" +#include "core/kexiproject.h" +#include "core/kexi.h" +#include "kexidb/connection.h" + +#include "main/manager.h" + +//#include + +namespace Kross { namespace KexiApp { + + /// \internal + class KexiAppMainWindowPrivate + { + public: + KexiMainWindow* mainwindow; + + KexiProject* project() { + KexiProject* project = mainwindow->project(); + if(! project) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("No project loaded.") ); + return project; + } + }; + +}} + +using namespace Kross::KexiApp; + +KexiAppMainWindow::KexiAppMainWindow(KexiMainWindow* mainwindow) + : Kross::Api::Class("KexiAppMainWindow") + , d(new KexiAppMainWindowPrivate()) +{ + d->mainwindow = mainwindow; + + this->addFunction0("isConnected", this, &KexiAppMainWindow::isConnected); + this->addFunction0("getConnection", this, &KexiAppMainWindow::getConnection); + + this->addFunction1("getPartItems", this, &KexiAppMainWindow::getPartItems); + this->addFunction1("openPartItem", this, &KexiAppMainWindow::openPartItem); +} + +KexiAppMainWindow::~KexiAppMainWindow() +{ + delete d; +} + +const QString KexiAppMainWindow::getClassName() const +{ + return "Kross::KexiApp::KexiAppMainWindow"; +} + +bool KexiAppMainWindow::isConnected() +{ + return d->project()->isConnected(); +} + +Kross::Api::Object::Ptr KexiAppMainWindow::getConnection() +{ + ::KexiDB::Connection* connection = d->project()->dbConnection(); + if(! connection) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("No connection established.") ); + Kross::Api::Module::Ptr module = Kross::Api::Manager::scriptManager()->loadModule("krosskexidb"); + if(! module) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception("Could not load \"krosskexidb\" module.") ); + return module->get("KexiDBConnection", connection); +} + +Kross::Api::List* KexiAppMainWindow::getPartItems(const QString& mimetype) +{ + if(mimetype.isNull()) return 0; // just to be sure... + KexiPart::ItemDict* items = d->project()->itemsForMimeType( mimetype.latin1() ); + if(! items) return 0; + return new Kross::Api::ListT( *items ); +} + +bool KexiAppMainWindow::openPartItem(KexiAppPartItem* partitem) +{ + bool openingCancelled; + KexiDialogBase* dialog = partitem + ? d->mainwindow->openObject(partitem->item(), Kexi::DataViewMode, openingCancelled) + : 0; + return (dialog != 0 && ! openingCancelled); +} diff --git a/kexi/plugins/scripting/kexiapp/kexiappmainwindow.h b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.h new file mode 100644 index 00000000..fd02c193 --- /dev/null +++ b/kexi/plugins/scripting/kexiapp/kexiappmainwindow.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * kexiappmainwindow.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIAPP_KEXIAPPMAINWINDOW_H +#define KROSS_KEXIAPP_KEXIAPPMAINWINDOW_H + +#include +#include + +#include +#include +#include +#include + +// Forward declarations. +class KexiMainWindow; + +namespace Kross { namespace KexiApp { + + // Forward declarations. + class KexiAppPartItem; + class KexiAppMainWindowPrivate; + + /** + * Class to handle Kexi's mainwindow instance. + */ + class KexiAppMainWindow : public Kross::Api::Class + { + public: + + /** + * Constructor. + * + * \param mainwindow The \a KexiMainWindow instance + * this class provides access to. + */ + KexiAppMainWindow(KexiMainWindow* mainwindow); + + /** + * Destructor. + */ + virtual ~KexiAppMainWindow(); + + /// \see Kross::Api::Object::getClassName + virtual const QString getClassName() const; + + /** \return true if Kexi is connected with a project else + false is returned. */ + bool isConnected(); + + /** \return the \a Kross::KexiDB::KexiDBConnection object that + belongs to the opened project or throw an exception if there + was no project opened (no connection established). Cause the + KexiApp-module doesn't know anything about the KexiDB-module + we have to use the base-class \a Kross::Api::Object to pass + the \a Kross::KexiDB::KexiDBConnection object around. */ + Kross::Api::Object::Ptr getConnection(); + + /** \return a list of \a KexiAppPartItem objects for the defined + \p mimetype string. */ + Kross::Api::List* getPartItems(const QString& mimetype); + + /** Try to open the defined \a KexiAppPartItem and \return true + on success else false. */ + bool openPartItem(KexiAppPartItem* partitem); + + private: + /// Private d-pointer class. + KexiAppMainWindowPrivate* d; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexiapp/kexiappmodule.cpp b/kexi/plugins/scripting/kexiapp/kexiappmodule.cpp new file mode 100644 index 00000000..cb664496 --- /dev/null +++ b/kexi/plugins/scripting/kexiapp/kexiappmodule.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * kexiappmodule.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexiappmodule.h" +#include "kexiappmainwindow.h" + +#include "core/keximainwindow.h" + +#include +#include +#include
+ +#include + +// The as version() published versionnumber of this kross-module. +#define KROSS_KEXIAPP_VERSION 1 + +extern "C" +{ + /** + * Exported an loadable function as entry point to use + * the \a KexiAppModule. + */ + Kross::Api::Object* KDE_EXPORT init_module(Kross::Api::Manager* manager) + { + return new Kross::KexiApp::KexiAppModule(manager); + } +} + +namespace Kross { namespace KexiApp { + + /// \internal + class KexiAppModulePrivate + { + public: + Kross::Api::Manager* manager; + }; + +}} + +using namespace Kross::KexiApp; + +KexiAppModule::KexiAppModule(Kross::Api::Manager* manager) + : Kross::Api::Module("KexiApp") + , d(new KexiAppModulePrivate()) +{ + kdDebug() << "Kross::KexiApp::KexiAppModule Ctor" << endl; + + d->manager = manager; + + Kross::Api::Object::Ptr mainwinobject = manager->getChild("KexiMainWindow"); + if(mainwinobject) { + Kross::Api::QtObject* mainwinqtobject = dynamic_cast< Kross::Api::QtObject* >( mainwinobject.data() ); + if(mainwinqtobject) { + ::KexiMainWindow* mainwin = dynamic_cast< ::KexiMainWindow* >( mainwinqtobject->getObject() ); + if(mainwin) { + addChild( "version", new Kross::Api::Variant(KROSS_KEXIAPP_VERSION) ); + addChild( new KexiAppMainWindow(mainwin) ); + return; + } + else kdDebug()<<"Kross::KexiApp::KexiAppModule: Failed to determinate KexiMainWindow instance"< +#include + +#include + +namespace Kross { namespace Api { + class Manager; +}} + +namespace Kross { + +/** + * Wrapper around the Kexi-application to access runtime + * information a running Kexi-application likes to + * provide. + */ +namespace KexiApp { + + class KexiAppModulePrivate; + + /** + * The Kexi-application module which provides us the + * main entrypoint to communicate with a running + * Kexi-application. + */ + class KexiAppModule : public Kross::Api::Module + { + public: + + /** + * Constructor. + * + * \param manager The \a Kross::Api::Manager singleton + * instance used to access this module. + */ + KexiAppModule(Kross::Api::Manager* manager); + + /** + * Destructor. + */ + virtual ~KexiAppModule(); + + /// \see Kross::Api::Object::getClassName + virtual const QString getClassName() const; + + private: + /// Private d-pointer class. + KexiAppModulePrivate* d; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexiapp/kexiapppart.cpp b/kexi/plugins/scripting/kexiapp/kexiapppart.cpp new file mode 100644 index 00000000..23d6c2f5 --- /dev/null +++ b/kexi/plugins/scripting/kexiapp/kexiapppart.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** + * kexiapppart.cpp + * This file is part of the KDE project + * copyright (C)2006 by Sebastian Sauer (mail@dipe.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 "kexiapppart.h" + +#include "core/kexipart.h" +#include "core/kexipartitem.h" +//#include "core/kexiproject.h" + +using namespace Kross::KexiApp; + +KexiAppPartItem::KexiAppPartItem(KexiPart::Item* item) + : Kross::Api::Class("KexiAppPartItem") +{ + this->addFunction0("identifier", item, &::KexiPart::Item::identifier ); + + this->addFunction1("setIdentifier", item, &::KexiPart::Item::setIdentifier ); + + this->addFunction0("mimeType", item, &::KexiPart::Item::mimeType ); + this->addFunction1("setMimeType", item, &::KexiPart::Item::setMimeType ); + + this->addFunction0("name", item, &::KexiPart::Item::name ); + this->addFunction1("setName", item, &::KexiPart::Item::setName ); + + this->addFunction0("caption", item, &::KexiPart::Item::caption ); + this->addFunction1("setCaption", item, &::KexiPart::Item::setCaption ); + + this->addFunction0("description", item, &::KexiPart::Item::description ); + this->addFunction1("setDescription", item, &::KexiPart::Item::setDescription ); +} diff --git a/kexi/plugins/scripting/kexiapp/kexiapppart.h b/kexi/plugins/scripting/kexiapp/kexiapppart.h new file mode 100644 index 00000000..5f55d6bf --- /dev/null +++ b/kexi/plugins/scripting/kexiapp/kexiapppart.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * kexiapppart.h + * This file is part of the KDE project + * copyright (C)2006 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIAPP_KEXIAPPPART_H +#define KROSS_KEXIAPP_KEXIAPPPART_H + +#include +#include + +#include +#include +#include + +// Forward declarations. +namespace KexiPart { + class Item; + class Part; +} + +namespace Kross { namespace KexiApp { + + /** + * Class to handle Kexi Part::Item instance. + */ + class KexiAppPartItem : public Kross::Api::Class + { + public: + KexiAppPartItem(KexiPart::Item*); + virtual ~KexiAppPartItem() {} + virtual const QString getClassName() const { return "Kross::KexiApp::KexiAppPartItem"; } + + KexiPart::Item* item() { return m_item; } + private: + KexiPart::Item* m_item; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb.doxyfile b/kexi/plugins/scripting/kexidb.doxyfile new file mode 100644 index 00000000..e40a378e --- /dev/null +++ b/kexi/plugins/scripting/kexidb.doxyfile @@ -0,0 +1,324 @@ +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +PROJECT_NAME = KrossKexiDB +PROJECT_NUMBER = 1.0. +OUTPUT_DIRECTORY = kexidbdocs +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. +INLINE_INHERITED_MEMB = NO +##INLINE_INHERITED_MEMB = YES + +FULL_PATH_NAMES = NO +#STRIP_FROM_PATH = /home/snoopy/ +STRIP_FROM_INC_PATH = +SHORT_NAMES = YES +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. +INHERIT_DOCS = YES + +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = YES +HIDE_IN_BODY_DOCS = YES +INTERNAL_DOCS = NO + +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = YES +SHOW_INCLUDE_FILES = NO +INLINE_INFO = NO +SORT_MEMBER_DOCS = NO +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = NO +GENERATE_TESTLIST = NO +GENERATE_BUGLIST = NO +GENERATE_DEPRECATEDLIST= NO +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +INPUT = ../kexidb/ +IMAGE_PATH = +FILE_PATTERNS = *.cpp *.h *.dox +RECURSIVE = YES + +EXCLUDE = +EXCLUDE_SYMLINKS = YES +EXCLUDE_PATTERNS = config.h *.moc.cpp + +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO + +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. +STRIP_CODE_COMMENTS = YES + +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 4 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. +COMPACT_LATEX = YES + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. +##PDF_HYPERLINKS = NO +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. +##LATEX_BATCHMODE = NO +LATEX_BATCHMODE = YES + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = NO +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +TAGFILES = +GENERATE_TAGFILE = krosskexidb.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = NO +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) +HAVE_DOT = NO + +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = NO +CLASS_GRAPH = NO +COLLABORATION_GRAPH = NO +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similiar to the OMG's Unified Modeling +# Language. +UML_LOOK = NO + +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +##CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = NO +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = NO +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +SEARCHENGINE = NO diff --git a/kexi/plugins/scripting/kexidb/Makefile.am b/kexi/plugins/scripting/kexidb/Makefile.am new file mode 100644 index 00000000..12f82501 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/Makefile.am @@ -0,0 +1,30 @@ +include $(top_srcdir)/kexi/Makefile.global + +INCLUDES = -I$(top_srcdir)/kexi $(KROSS_INCLUDES) $(all_includes) + +kde_module_LTLIBRARIES = krosskexidb.la + +krosskexidb_la_SOURCES = \ + kexidbfield.cpp \ + kexidbfieldlist.cpp \ + kexidbschema.cpp \ + kexidbparser.cpp \ + kexidbcursor.cpp \ + kexidbtransaction.cpp \ + kexidbconnectiondata.cpp \ + kexidbconnection.cpp \ + kexidbdriver.cpp \ + kexidbdrivermanager.cpp \ + kexidbmodule.cpp + +krosskexidb_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) $(VER_INFO) -module +krosskexidb_la_LIBADD = \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + $(LIB_KROSS_API) \ + $(LIB_KROSS_MAIN) \ + $(top_builddir)/kexi/kexidb/libkexidb.la \ + $(top_builddir)/kexi/kexidb/parser/libkexidbparser.la + +METASOURCES = AUTO +SUBDIRS = . diff --git a/kexi/plugins/scripting/kexidb/kexidbconnection.cpp b/kexi/plugins/scripting/kexidb/kexidbconnection.cpp new file mode 100644 index 00000000..d3b7cc76 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbconnection.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** + * kexidbconnection.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbconnection.h" +#include "kexidbconnectiondata.h" +#include "kexidbdrivermanager.h" +#include "kexidbdriver.h" +#include "kexidbcursor.h" +#include "kexidbfieldlist.h" +#include "kexidbschema.h" +#include "kexidbtransaction.h" +#include "kexidbparser.h" + +#include + +#include + +#include + +using namespace Kross::KexiDB; + +KexiDBConnection::KexiDBConnection(::KexiDB::Connection* connection, KexiDBDriver* driver, KexiDBConnectionData* connectiondata) + : Kross::Api::Class("KexiDBConnection") + , m_connection(connection) + , m_connectiondata(connectiondata ? connectiondata : new KexiDBConnectionData(connection->data())) + , m_driver(driver ? driver : new KexiDBDriver(connection->driver())) +{ + this->addFunction0< Kross::Api::Variant >("hadError", this, &KexiDBConnection::hadError); + this->addFunction0< Kross::Api::Variant >("lastError", this, &KexiDBConnection::lastError); + + this->addFunction0< KexiDBConnectionData >("data", this, &KexiDBConnection::data); + this->addFunction0< KexiDBDriver >("driver", this, &KexiDBConnection::driver); + + this->addFunction0< Kross::Api::Variant >("connect", this, &KexiDBConnection::connect); + this->addFunction0< Kross::Api::Variant >("isConnected", this, &KexiDBConnection::isConnected); + this->addFunction0< Kross::Api::Variant >("disconnect", this, &KexiDBConnection::disconnect); + + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("databaseExists", this, &KexiDBConnection::databaseExists); + this->addFunction0< Kross::Api::Variant >("currentDatabase", this, &KexiDBConnection::currentDatabase); + this->addFunction0< Kross::Api::Variant >("databaseNames", this, &KexiDBConnection::databaseNames); + this->addFunction0< Kross::Api::Variant >("isDatabaseUsed", this, &KexiDBConnection::isDatabaseUsed); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("useDatabase", this, &KexiDBConnection::useDatabase); + this->addFunction0< Kross::Api::Variant >("closeDatabase", this, &KexiDBConnection::closeDatabase); + + this->addFunction0< Kross::Api::Variant >("tableNames", this, &KexiDBConnection::tableNames); + this->addFunction0< Kross::Api::Variant >("queryNames", this, &KexiDBConnection::queryNames); + + this->addFunction1< KexiDBCursor, Kross::Api::Variant >("executeQueryString", this, &KexiDBConnection::executeQueryString); + this->addFunction1< KexiDBCursor, KexiDBQuerySchema >("executeQuerySchema", this, &KexiDBConnection::executeQuerySchema); + + addFunction("insertRecord", &KexiDBConnection::insertRecord); + + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("createDatabase", this, &KexiDBConnection::createDatabase); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("dropDatabase", this, &KexiDBConnection::dropDatabase); + + this->addFunction1< Kross::Api::Variant, KexiDBTableSchema >("createTable", this, &KexiDBConnection::createTable); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("dropTable", this, &KexiDBConnection::dropTable); + this->addFunction2< Kross::Api::Variant, KexiDBTableSchema, KexiDBTableSchema >("alterTable", this, &KexiDBConnection::alterTable); + this->addFunction2< Kross::Api::Variant, KexiDBTableSchema, Kross::Api::Variant >("alterTableName", this, &KexiDBConnection::alterTableName); + + this->addFunction1< KexiDBTableSchema, Kross::Api::Variant >("tableSchema", this, &KexiDBConnection::tableSchema); + this->addFunction1< Kross::Api::Variant, KexiDBTableSchema >("isEmptyTable", this, &KexiDBConnection::isEmptyTable); + this->addFunction1< KexiDBQuerySchema, Kross::Api::Variant >("querySchema", this, &KexiDBConnection::querySchema); + + this->addFunction0< Kross::Api::Variant >("autoCommit", this, &KexiDBConnection::autoCommit); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("setAutoCommit", this, &KexiDBConnection::setAutoCommit); + + this->addFunction0< KexiDBTransaction >("beginTransaction", this, &KexiDBConnection::beginTransaction); + this->addFunction1< Kross::Api::Variant, KexiDBTransaction >("commitTransaction", this, &KexiDBConnection::commitTransaction); + this->addFunction1< Kross::Api::Variant, KexiDBTransaction >("rollbackTransaction", this, &KexiDBConnection::rollbackTransaction); + this->addFunction0< KexiDBTransaction >("defaultTransaction", this, &KexiDBConnection::defaultTransaction); + this->addFunction1< void, KexiDBTransaction >("setDefaultTransaction", this, &KexiDBConnection::setDefaultTransaction); + this->addFunction0("transactions", this, &KexiDBConnection::transactions); + + this->addFunction0< KexiDBParser >("parser", this, &KexiDBConnection::parser); +} + +KexiDBConnection::~KexiDBConnection() { +} + +const QString KexiDBConnection::getClassName() const { + return "Kross::KexiDB::KexiDBConnection"; +} + +::KexiDB::Connection* KexiDBConnection::connection() const { + if(! m_connection) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::Connection is NULL.")) ); + //if(m_connection->error()) + // throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::Connection error: %1").arg(m_connection->errorMsg())) ); + return m_connection; +} + +bool KexiDBConnection::hadError() const { return connection()->error(); } +const QString KexiDBConnection::lastError() const { return connection()->errorMsg(); } + +KexiDBConnectionData* KexiDBConnection::data() { return m_connectiondata.data(); } +KexiDBDriver* KexiDBConnection::driver() { return m_driver.data(); } + +bool KexiDBConnection::connect() { return connection()->connect(); } +bool KexiDBConnection::isConnected() { return connection()->isConnected(); } +bool KexiDBConnection::disconnect() { return connection()->disconnect(); } + +bool KexiDBConnection::isReadOnly() const { return connection()->isReadOnly(); } + +bool KexiDBConnection::databaseExists(const QString& dbname) { return connection()->databaseExists(dbname); } +const QString KexiDBConnection::currentDatabase() const { return connection()->currentDatabase(); } +const QStringList KexiDBConnection::databaseNames() const { return connection()->databaseNames(); } +bool KexiDBConnection::isDatabaseUsed() const { return connection()->isDatabaseUsed(); } +bool KexiDBConnection::useDatabase(const QString& dbname) { return connection()->databaseExists(dbname) && m_connection->useDatabase(dbname); } +bool KexiDBConnection::closeDatabase() { return connection()->closeDatabase(); } + +const QStringList KexiDBConnection::allTableNames() const { return connection()->tableNames(true); } +const QStringList KexiDBConnection::tableNames() const { return connection()->tableNames(false); } + +const QStringList KexiDBConnection::queryNames() const { + bool ok = true; + QStringList queries = connection()->objectNames(::KexiDB::QueryObjectType, &ok); + if(! ok) throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to determinate querynames.")) ); + return queries; +} + +KexiDBCursor* KexiDBConnection::executeQueryString(const QString& sqlquery) { + // The ::KexiDB::Connection::executeQuery() method does not check if we pass a valid SELECT-statement + // or e.g. a DROP TABLE operation. So, let's check for such dangerous operations right now. + ::KexiDB::Parser parser( connection() ); + if(! parser.parse(sqlquery)) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Failed to parse query: %1 %2").arg(parser.error().type()).arg(parser.error().error())) ); + if( parser.query() == 0 || parser.operation() != ::KexiDB::Parser::OP_Select ) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid query operation \"%1\"").arg(parser.operationString()) ) ); + ::KexiDB::Cursor* cursor = connection()->executeQuery(sqlquery); + return cursor ? new KexiDBCursor(cursor) : 0; +} + +KexiDBCursor* KexiDBConnection::executeQuerySchema(KexiDBQuerySchema* queryschema) { + ::KexiDB::Cursor* cursor = connection()->executeQuery( *queryschema->queryschema() ); + return cursor ? new KexiDBCursor(cursor) : 0; +} + +/*TODO +bool KexiDBConnection::insertRecordIntoFieldlist(KexiDBFieldList* fieldlist, QValueList values) { + return connection()->insertRecord(*fieldlist->fieldlist(), values); +} + +bool KexiDBConnection::insertRecordIntoTable(KexiDBTableSchema* tableschema, QValueList values) { + return connection()->insertRecord(*tableschema->tableschema(), values); +} +*/ +Kross::Api::Object::Ptr KexiDBConnection::insertRecord(Kross::Api::List::Ptr args) { + QValueList values = Kross::Api::Variant::toList(args->item(1)); + Kross::Api::Object::Ptr obj = args->item(0); + if(obj->getClassName() == "Kross::KexiDB::KexiDBFieldList") + return new Kross::Api::Variant( + QVariant(connection()->insertRecord( + *Kross::Api::Object::fromObject(obj)->fieldlist(), + values + ), 0)); + return new Kross::Api::Variant( + QVariant(connection()->insertRecord( + *Kross::Api::Object::fromObject(obj)->tableschema(), + values + ), 0)); +} + +bool KexiDBConnection::createDatabase(const QString& dbname) { return connection()->createDatabase(dbname); } +bool KexiDBConnection::dropDatabase(const QString& dbname) { return connection()->dropDatabase(dbname); } + +bool KexiDBConnection::createTable(KexiDBTableSchema* tableschema) { return connection()->createTable(tableschema->tableschema(), false); } +bool KexiDBConnection::dropTable(const QString& tablename) { return true == connection()->dropTable(tablename); } +bool KexiDBConnection::alterTable(KexiDBTableSchema* fromschema, KexiDBTableSchema* toschema) { return true == connection()->alterTable(*fromschema->tableschema(), *toschema->tableschema()); } +bool KexiDBConnection::alterTableName(KexiDBTableSchema* tableschema, const QString& newtablename) { return connection()->alterTableName(*tableschema->tableschema(), newtablename); } + +KexiDBTableSchema* KexiDBConnection::tableSchema(const QString& tablename) const { + ::KexiDB::TableSchema* tableschema = connection()->tableSchema(tablename); + return tableschema ? new KexiDBTableSchema(tableschema) : 0; +} + +bool KexiDBConnection::isEmptyTable(KexiDBTableSchema* tableschema) const { + bool success; + bool notempty = connection()->isEmpty(*tableschema->tableschema(), success); + return (! (success && notempty)); +} + +KexiDBQuerySchema* KexiDBConnection::querySchema(const QString& queryname) const { + ::KexiDB::QuerySchema* queryschema = connection()->querySchema(queryname); + return queryschema ? new KexiDBQuerySchema(queryschema) : 0; +} + +bool KexiDBConnection::autoCommit() const { return connection()->autoCommit(); } +bool KexiDBConnection::setAutoCommit(bool enabled) { return connection()->setAutoCommit(enabled); } + +KexiDBTransaction* KexiDBConnection::beginTransaction() { + ::KexiDB::Transaction t = connection()->beginTransaction(); + return new KexiDBTransaction(t); +} + +bool KexiDBConnection::commitTransaction(KexiDBTransaction* transaction) { return connection()->commitTransaction( transaction->transaction() ); } +bool KexiDBConnection::rollbackTransaction(KexiDBTransaction* transaction) { return connection()->rollbackTransaction( transaction->transaction() ); } +KexiDBTransaction* KexiDBConnection::defaultTransaction() { return new KexiDBTransaction( connection()->defaultTransaction() ); } +void KexiDBConnection::setDefaultTransaction(KexiDBTransaction* transaction) { connection()->setDefaultTransaction( transaction->transaction() ); } + +Kross::Api::List* KexiDBConnection::transactions() { + return new Kross::Api::ListT( connection()->transactions() ); +} + +KexiDBParser* KexiDBConnection::parser() { return new KexiDBParser(this, new ::KexiDB::Parser(connection())); } diff --git a/kexi/plugins/scripting/kexidb/kexidbconnection.h b/kexi/plugins/scripting/kexidb/kexidbconnection.h new file mode 100644 index 00000000..7e1a7d3a --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbconnection.h @@ -0,0 +1,194 @@ +/*************************************************************************** + * kexidbconnection.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBCONNECTION_H +#define KROSS_KEXIDB_KEXIDBCONNECTION_H + +#include +#include + +#include +#include +#include +#include + +//#include +#include + +namespace Kross { namespace KexiDB { + + // Forward declarations. + class KexiDBDriver; + class KexiDBConnectionData; + class KexiDBCursor; + class KexiDBTableSchema; + class KexiDBQuerySchema; + class KexiDBTransaction; + class KexiDBParser; + + /** + * A connection to a database. + * + * Example (in Python) ; + * @code + * # Import the kexidb module. + * import krosskexidb + * # Get the drivermanager. + * drivermanager = krosskexidb.DriverManager() + * # We need a connectiondata object. + * connectiondata = drivermanager.createConnectionData() + * # Fill the new connectiondata object with what we need to connect. + * connectiondata.setFileName("/home/user/kexisqlite3file.kexi") + * # Create the database-driver to access the SQLite3 backend. + * driver = drivermanager.driver("SQLite3") + * # Create the connection now. + * connection = driver.createConnection(connectiondata) + * # Establish the connection. + * if not connection.connect(): raise("Failed to connect with db") + * # Open database for usage. The filebased driver uses the filename as databasename. + * if not connection.useDatabase("/home/user/kexisqlite3file.kexi"): raise("Failed to use db") + * @endcode + */ + class KexiDBConnection : public Kross::Api::Class + { + public: + KexiDBConnection(::KexiDB::Connection* connection, KexiDBDriver* driver = 0, KexiDBConnectionData* connectiondata = 0); + virtual ~KexiDBConnection(); + virtual const QString getClassName() const; + + private: + + /** Return true if there was an error during last operation on the database. */ + bool hadError() const; + /** Return the last errormessage. */ + const QString lastError() const; + + /** Return the KexiDBConnectionData object used to create this connection. */ + KexiDBConnectionData* data(); + /** Return the KexiDBDriver object this connection belongs too. */ + KexiDBDriver* driver(); + + /** Try to connect and return true if we are successfully connected now. */ + bool connect(); + /** Return true if we are connected. */ + bool isConnected(); + /** Disconnect and return true if we are successfully disconnected now. */ + bool disconnect(); + + /** Returns true if the connection is read-only. */ + bool isReadOnly() const; + + /** Return true if the as argument passed databasename exists. */ + bool databaseExists(const QString& dbname); + /** Return the name of currently used database for this connection or empty + string if there is no used database. */ + const QString currentDatabase() const; + /** Return list of database names for opened connection. */ + const QStringList databaseNames() const; + /** Return true if connection is properly established. */ + bool isDatabaseUsed() const; + /** Opens an existing database specified by the as argument passed databasename + and returns true if the database is used now. */ + bool useDatabase(const QString& dbname); + /** Closes currently used database for this connection. */ + bool closeDatabase(); + + /** Return names of all table schemas stored in currently used database include the + internal KexiDB system table names (kexi__*) */ + const QStringList allTableNames() const; + /** Return names of all table schemas without the internal KexiDB system table names (kexi__*) */ + const QStringList tableNames() const; + /** Return names of all query schemas stored in currently used database. */ + const QStringList queryNames() const; + + /** Executes query described by the as argument passed sqlstatement-string. Returns the + opened cursor created for results of this query. */ + KexiDBCursor* executeQueryString(const QString& sqlquery); + /** Executes query described by the as argument passed KexiDBQuerySchema object. Returns + the opened cursor created for results of this query. */ + KexiDBCursor* executeQuerySchema(KexiDBQuerySchema* queryschema); + +//TODO replace following method with a proxymethod. + /** Inserts the as argument passed KexiDBField object. */ + Kross::Api::Object::Ptr insertRecord(Kross::Api::List::Ptr); + + /** Creates new database with the as argument passed databasename. */ + bool createDatabase(const QString& dbname); + /** Drops the as argument passed databasename. */ + bool dropDatabase(const QString& dbname); + + /** Creates table defined by the as argument passed KexiTableSchema object. */ + bool createTable(KexiDBTableSchema* tableschema); + /** Drops table defined by the as argument passed KexiDBTableSchema object. */ + bool dropTable(const QString& tablename); + /** Alters the as first argument passed KexiDBTableSchema object using the as + second argument passed KexiDBTableSchema. */ + bool alterTable(KexiDBTableSchema* fromschema, KexiDBTableSchema* toschema); + /** Alters the tablename of the as first argument passed KexiDBTableSchema into + the as second argument passed new tablename. */ + bool alterTableName(KexiDBTableSchema* tableschema, const QString& newtablename); + + /** Returns the KexiDBTableSchema object of the table matching to the as argument + passed tablename. */ + KexiDBTableSchema* tableSchema(const QString& tablename) const; + /** Returns true if there is at least one valid record in the as argument passed tablename. */ + bool isEmptyTable(KexiDBTableSchema* tableschema) const; + /** Returns the KexiDBQuerySchema object of the query matching to the as argument passed queryname. */ + KexiDBQuerySchema* querySchema(const QString& queryname) const; + + /** Return true if the \"auto commit\" option is on. */ + bool autoCommit() const; + /** Set the auto commit option. This does not affect currently started transactions and can + be changed even when connection is not established. */ + bool setAutoCommit(bool enabled); + + /** Creates new transaction handle and starts a new transaction. */ + KexiDBTransaction* beginTransaction(); + /** Commits the as rgument passed KexiDBTransaction object. */ + bool commitTransaction(KexiDBTransaction* transaction); + /** Rollback the as rgument passed KexiDBTransaction object. */ + bool rollbackTransaction(KexiDBTransaction* transaction); + /** Return the KEXIDBTransaction object for default transaction for this connection. */ + KexiDBTransaction* defaultTransaction(); + /** Sets default transaction that will be used as context for operations on data in opened + database for this connection. */ + void setDefaultTransaction(KexiDBTransaction* transaction); + + /** Return list of currently active KexiDBTransaction objects. */ + Kross::Api::List* transactions(); + + /** Return a KexiDBParser object. */ + KexiDBParser* parser(); + + private: + ::KexiDB::Connection* connection() const; + ::KexiDB::Connection* m_connection; + + KSharedPtr m_connectiondata; + KSharedPtr m_driver; + + /// Initialize the class instance. + void initialize(); + + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp new file mode 100644 index 00000000..61b81d3e --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** + * kexidbconnectiondata.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbconnectiondata.h" + +#include + +using namespace Kross::KexiDB; + +KexiDBConnectionData::KexiDBConnectionData(::KexiDB::ConnectionData* data) + : Kross::Api::Class("KexiDBConnectionData") + , m_data(data) +{ + this->addFunction0< Kross::Api::Variant >("caption", this, &KexiDBConnectionData::caption); + this->addFunction1< void, Kross::Api::Variant >("setCaption", this, &KexiDBConnectionData::setCaption); + + this->addFunction0< Kross::Api::Variant >("description", this, &KexiDBConnectionData::description); + this->addFunction1< void, Kross::Api::Variant >("setDescription", this, &KexiDBConnectionData::setDescription); + + this->addFunction0< Kross::Api::Variant >("driverName", this, &KexiDBConnectionData::driverName); + this->addFunction1< void, Kross::Api::Variant >("setDriverName", this, &KexiDBConnectionData::setDriverName); + + this->addFunction0< Kross::Api::Variant >("localSocketFileUsed", this, &KexiDBConnectionData::localSocketFileUsed); + this->addFunction1< void, Kross::Api::Variant >("setLocalSocketFileUsed", this, &KexiDBConnectionData::setLocalSocketFileUsed); + + this->addFunction0< Kross::Api::Variant >("localSocketFileName", this, &KexiDBConnectionData::localSocketFileName); + this->addFunction1< void, Kross::Api::Variant >("setLocalSocketFileName", this, &KexiDBConnectionData::setLocalSocketFileName); + + this->addFunction0< Kross::Api::Variant >("databaseName", this, &KexiDBConnectionData::databaseName); + this->addFunction1< void, Kross::Api::Variant >("setDatabaseName", this, &KexiDBConnectionData::setDatabaseName); + + this->addFunction0< Kross::Api::Variant >("hostName", this, &KexiDBConnectionData::hostName); + this->addFunction1< void, Kross::Api::Variant >("setHostName", this, &KexiDBConnectionData::setHostName); + + this->addFunction0< Kross::Api::Variant >("port", this, &KexiDBConnectionData::port); + this->addFunction1< void, Kross::Api::Variant >("setPort", this, &KexiDBConnectionData::setPort); + + this->addFunction0< Kross::Api::Variant >("password", this, &KexiDBConnectionData::password); + this->addFunction1< void, Kross::Api::Variant >("setPassword", this, &KexiDBConnectionData::setPassword); + + this->addFunction0< Kross::Api::Variant >("userName", this, &KexiDBConnectionData::userName); + this->addFunction1< void, Kross::Api::Variant >("setUserName", this, &KexiDBConnectionData::setUserName); + + this->addFunction0< Kross::Api::Variant >("fileName", this, &KexiDBConnectionData::fileName); + this->addFunction1< void, Kross::Api::Variant >("setFileName", this, &KexiDBConnectionData::setFileName); + + this->addFunction0< Kross::Api::Variant >("dbPath", this, &KexiDBConnectionData::dbPath); + this->addFunction0< Kross::Api::Variant >("dbFileName", this, &KexiDBConnectionData::dbFileName); + this->addFunction0< Kross::Api::Variant >("serverInfoString", this, &KexiDBConnectionData::serverInfoString); +} + +KexiDBConnectionData::~KexiDBConnectionData() +{ + //delete m_data; +} + +const QString KexiDBConnectionData::getClassName() const +{ + return "Kross::KexiDB::KexiDBConnectionData"; +} + +const QString KexiDBConnectionData::caption() const { return m_data->caption; } +void KexiDBConnectionData::setCaption(const QString& name) { m_data->caption = name; } + +const QString KexiDBConnectionData::description() const { return m_data->description; } +void KexiDBConnectionData::setDescription(const QString& desc) { m_data->description = desc; } + +const QString KexiDBConnectionData::driverName() const { return m_data->driverName; } +void KexiDBConnectionData::setDriverName(const QString& driver) { m_data->driverName = driver; } + +bool KexiDBConnectionData::localSocketFileUsed() const { return m_data->useLocalSocketFile; } +void KexiDBConnectionData::setLocalSocketFileUsed(bool used) { m_data->useLocalSocketFile = used; } +const QString KexiDBConnectionData::localSocketFileName() const { return m_data->localSocketFileName; } +void KexiDBConnectionData::setLocalSocketFileName(const QString& socketfilename) { m_data->localSocketFileName = socketfilename; } + +const QString KexiDBConnectionData::databaseName() const { return m_dbname; } +void KexiDBConnectionData::setDatabaseName(const QString& dbname) { m_dbname = dbname; } + +const QString KexiDBConnectionData::hostName() const { return m_data->hostName; } +void KexiDBConnectionData::setHostName(const QString& hostname) { m_data->hostName = hostname; } + +int KexiDBConnectionData::port() const { return m_data->port; } +void KexiDBConnectionData::setPort(int p) { m_data->port = p; } + +const QString KexiDBConnectionData::password() const { return m_data->password; } +void KexiDBConnectionData::setPassword(const QString& passwd) { m_data->password = passwd; } + +const QString KexiDBConnectionData::userName() const { return m_data->userName; } +void KexiDBConnectionData::setUserName(const QString& username) { m_data->userName = username; } + +const QString KexiDBConnectionData::fileName() const { return m_data->fileName(); } +void KexiDBConnectionData::setFileName(const QString& filename) { m_data->setFileName(filename); } + +const QString KexiDBConnectionData::dbPath() const { return m_data->dbPath(); } +const QString KexiDBConnectionData::dbFileName() const { return m_data->dbFileName(); } +const QString KexiDBConnectionData::serverInfoString() const { return m_data->serverInfoString(true); } diff --git a/kexi/plugins/scripting/kexidb/kexidbconnectiondata.h b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.h new file mode 100644 index 00000000..aaddffbd --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbconnectiondata.h @@ -0,0 +1,126 @@ +/*************************************************************************** + * kexidbconnectiondata.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBCONNECTIONDATA_H +#define KROSS_KEXIDB_KEXIDBCONNECTIONDATA_H + +#include + +#include +#include +#include +#include + +#include +#include + +namespace Kross { namespace KexiDB { + + /** + * A KexiDBConnectionData is used to store the details needed for + * a connection with a database. + */ + class KexiDBConnectionData : public Kross::Api::Class + { + friend class KexiDBDriverManager; + public: + KexiDBConnectionData(::KexiDB::ConnectionData* data); + virtual ~KexiDBConnectionData(); + operator ::KexiDB::ConnectionData& () { return *m_data; } + operator ::KexiDB::ConnectionData* () { return m_data; } + virtual const QString getClassName() const; + ::KexiDB::ConnectionData* data() { return m_data; } + + private: + + /** Return the connection name. */ + const QString caption() const; + /** Set the connection name. */ + void setCaption(const QString& name); + + /** Return the description. */ + const QString description() const; + /** Set the description. */ + void setDescription(const QString& desc); + + /** Return drivername. */ + const QString driverName() const; + /** Set the drivername. */ + void setDriverName(const QString& driver); + + /** Return true if a local socket file is used else false. */ + bool localSocketFileUsed() const; + /** Set if the local socket file should be used. */ + void setLocalSocketFileUsed(bool used); + /** Return the local socket filename. */ + const QString localSocketFileName() const; + /** Set the local socket filename. */ + void setLocalSocketFileName(const QString& socketfilename); + + // For serverbased drivers + + /** Return the database name. */ + const QString databaseName() const; + /** Set the database name. */ + void setDatabaseName(const QString& dbname); + + /** Return the hostname. */ + const QString hostName() const; + /** Set the hostname. */ + void setHostName(const QString& hostname); + + /** Return the port number. */ + int port() const; + /** Set the port number. */ + void setPort(int p); + + /** Return the password. */ + const QString password() const; + /** Set the password. */ + void setPassword(const QString& passwd); + + /** Return the username. */ + const QString userName() const; + /** Set the username. */ + void setUserName(const QString& username); + + // For filebased drivers + + /** Return the filename. */ + const QString fileName() const; + /** Set the filename. */ + void setFileName(const QString& filename); + + /** Return the database path. */ + const QString dbPath() const; + /** Return the database filename. */ + const QString dbFileName() const; + + /** Return a user-friendly string representation. */ + const QString serverInfoString() const; + + private: + ::KexiDB::ConnectionData* m_data; + QString m_dbname; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbcursor.cpp b/kexi/plugins/scripting/kexidb/kexidbcursor.cpp new file mode 100644 index 00000000..3bc1763d --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbcursor.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + * kexidbcursor.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbcursor.h" +#include "kexidbconnection.h" + +#include +#include + +#include + +using namespace Kross::KexiDB; + +KexiDBCursor::KexiDBCursor(::KexiDB::Cursor* cursor) + : Kross::Api::Class("KexiDBCursor") + , m_cursor(cursor) +{ + this->addFunction0("open", this, &KexiDBCursor::open ); + this->addFunction0("isOpened", this, &KexiDBCursor::isOpened ); + this->addFunction0("reopen", this, &KexiDBCursor::reopen ); + this->addFunction0("close", this, &KexiDBCursor::close ); + this->addFunction0("moveFirst", this, &KexiDBCursor::moveFirst ); + this->addFunction0("moveLast", this, &KexiDBCursor::moveLast ); + this->addFunction0("movePrev", this, &KexiDBCursor::movePrev ); + this->addFunction0("moveNext", this, &KexiDBCursor::moveNext ); + this->addFunction0("bof", this, &KexiDBCursor::bof ); + this->addFunction0("eof", this, &KexiDBCursor::eof ); + this->addFunction0("at", this, &KexiDBCursor::at ); + this->addFunction0("fieldCount", this, &KexiDBCursor::fieldCount ); + this->addFunction1("value", this, &KexiDBCursor::value ); + this->addFunction2("setValue", this, &KexiDBCursor::setValue ); + this->addFunction0("save", this, &KexiDBCursor::save ); +} + +KexiDBCursor::~KexiDBCursor() +{ + ///@todo check ownership + //delete m_cursor; + + clearBuffers(); +} + +void KexiDBCursor::clearBuffers() +{ + QMap::ConstIterator + it( m_modifiedrecords.constBegin() ), end( m_modifiedrecords.constEnd() ); + for( ; it != end; ++it) + delete it.data(); + m_modifiedrecords.clear(); +} + +const QString KexiDBCursor::getClassName() const +{ + return "Kross::KexiDB::KexiDBCursor"; +} + +bool KexiDBCursor::open() { return m_cursor->open(); } +bool KexiDBCursor::isOpened() { return m_cursor->isOpened(); } +bool KexiDBCursor::reopen() { return m_cursor->reopen(); } +bool KexiDBCursor::close() { return m_cursor->close(); } + +bool KexiDBCursor::moveFirst() { return m_cursor->moveFirst(); } +bool KexiDBCursor::moveLast() { return m_cursor->moveLast(); } +bool KexiDBCursor::movePrev() { return m_cursor->movePrev(); } +bool KexiDBCursor::moveNext() { return m_cursor->moveNext(); } + +bool KexiDBCursor::bof() { return m_cursor->bof(); } +bool KexiDBCursor::eof() { return m_cursor->eof(); } + +Q_LLONG KexiDBCursor::at() { return m_cursor->at(); } +uint KexiDBCursor::fieldCount() { return m_cursor->fieldCount(); } + +QVariant KexiDBCursor::value(uint index) +{ + return m_cursor->value(index); +} + +bool KexiDBCursor::setValue(uint index, QVariant value) +{ + ::KexiDB::QuerySchema* query = m_cursor->query(); + if(! query) { + kdDebug() << "Invalid query in KexiDBCursor::setValue index=" << index << " value=" << value << endl; + return false; + } + + ::KexiDB::QueryColumnInfo* column = query->fieldsExpanded().at(index); + if(! column) { + kdDebug() << "Invalid column in KexiDBCursor::setValue index=" << index << " value=" << value << endl; + return false; + } + + const Q_LLONG position = m_cursor->at(); + if(! m_modifiedrecords.contains(position)) + m_modifiedrecords.replace(position, new Record(m_cursor)); + m_modifiedrecords[position]->buffer->insert(*column, value); + return true; +} + +bool KexiDBCursor::save() +{ + if(m_modifiedrecords.count() < 1) + return true; + + //It is needed to close the cursor before we are able to update the rows + //since else the database could be locked (e.g. at the case of SQLite a + //KexiDB: Object ERROR: 6: SQLITE_LOCKED would prevent updating). + //Maybe it works fine with other drivers like MySQL or Postqre? + m_cursor->close(); + + bool ok = true; + QMap::ConstIterator + it( m_modifiedrecords.constBegin() ), end( m_modifiedrecords.constEnd() ); + for( ; it != end; ++it) { + bool b = m_cursor->updateRow(it.data()->rowdata, * it.data()->buffer, m_cursor->isBuffered()); + if(ok) { + ok = b; + //break; + } + } + //m_cursor->close(); + clearBuffers(); + return ok; +} diff --git a/kexi/plugins/scripting/kexidb/kexidbcursor.h b/kexi/plugins/scripting/kexidb/kexidbcursor.h new file mode 100644 index 00000000..6e92a38e --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbcursor.h @@ -0,0 +1,159 @@ +/*************************************************************************** + * kexidbcursor.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBCURSOR_H +#define KROSS_KEXIDB_KEXIDBCURSOR_H + +#include + +#include +#include +#include +#include + +#include +#include + +namespace Kross { namespace KexiDB { + + // Forward declaration. + class KexiDBConnection; + + /** + * The cursor provides a control structure for the successive traversal + * of records in a result set as returned e.g. by a query. + * + * Example (in Python) that shows how to iterate over the result of a query; + * @code + * # Once we have a KexiDBConnection object we are able to execute a query string and get a cursor as result. + * cursor = connection.executeQueryString("SELECT * from emp") + * # Let's check if the query was successfully. + * if not cursor: raise("Query failed") + * # Walk through all items in the table. + * while(not cursor.eof()): + * # Iterate over the fields the record has. + * for i in range( cursor.fieldCount() ): + * # Print some information. + * print "%s %s %s" % (cursor.at(), i, cursor.value(i)) + * # and move on to the next record. + * cursor.moveNext() + * @endcode + * + * Example (in Python) that shows how to use a cursor to strip + * all whitespaces at the beginning and the end from the values + * in a table; + * @code + * import krosskexidb + * drivermanager = krosskexidb.DriverManager() + * connectiondata = drivermanager.createConnectionDataByFile("/home/me/kexiprojectfile.kexi") + * driver = drivermanager.driver( connectiondata.driverName() ) + * connection = driver.createConnection(connectiondata) + * if not connection.connect(): raise "Failed to connect" + * if not connection.useDatabase( connectiondata.databaseName() ): + * if not connection.useDatabase( connectiondata.fileName() ): + * raise "Failed to use database" + * + * table = connection.tableSchema("emp") + * query = table.query() + * cursor = connection.executeQuerySchema(query) + * if not cursor: raise("Query failed") + * while(not cursor.eof()): + * for i in range( cursor.fieldCount() ): + * v = str( cursor.value(i) ) + * if v.startswith(' ') or v.endswith(' '): + * cursor.setValue(i, v.strip()) + * cursor.moveNext() + * if not cursor.save(): raise "Failed to save changes" + * @endcode + */ + class KexiDBCursor : public Kross::Api::Class + { + public: + KexiDBCursor(::KexiDB::Cursor* cursor); + virtual ~KexiDBCursor(); + virtual const QString getClassName() const; + + private: + + /** Opens the cursor. */ + bool open(); + /** Returns true if the cursor is opened else false. */ + bool isOpened(); + /** Closes and then opens again the same cursor. */ + bool reopen(); + /** Closes previously opened cursor. */ + bool close(); + + /** Moves current position to the first record and retrieves it. */ + bool moveFirst(); + /** Moves current position to the last record and retrieves it. */ + bool moveLast(); + /** Moves current position to the previous record and retrieves it. */ + bool movePrev(); + /** Moves current position to the next record and retrieves it. */ + bool moveNext(); + + /** Returns true if current position is before first record. */ + bool bof(); + /** Returns true if current position is after last record. */ + bool eof(); + + /** Returns current internal position of the cursor's query. Records + are numbered from 0; the value -1 means that the cursor does not + point to a valid record. */ + Q_LLONG at(); + /** Returns the number of fields available for this cursor. */ + uint fieldCount(); + /** Returns the value stored in the passed column number (counting from 0). */ + QVariant value(uint index); + /** Set the value for the field defined with index. The new value is buffered + and does not got written as long as save() is not called. */ + bool setValue(uint index, QVariant value); + + /** Save any changes done with setValue(). You should call this only once at + the end of all value/setValue iterations cause the cursor is closed once + the changes got saved successfully. */ + bool save(); + + private: + ::KexiDB::Cursor* m_cursor; + + class Record { + public: + ::KexiDB::RowData rowdata; + ::KexiDB::RowEditBuffer* buffer; + Record(::KexiDB::Cursor* cursor) + : buffer( new ::KexiDB::RowEditBuffer(true) ) + { + cursor->storeCurrentRow(rowdata); + } + ~Record() + { + delete buffer; + } + }; + QMap m_modifiedrecords; + + void clearBuffers(); + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbdriver.cpp b/kexi/plugins/scripting/kexidb/kexidbdriver.cpp new file mode 100644 index 00000000..f019b237 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbdriver.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * kexidbdriver.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbdriver.h" +#include "kexidbdrivermanager.h" + +#include +#include +#include + +#include + +using namespace Kross::KexiDB; + +KexiDBDriver::KexiDBDriver(::KexiDB::Driver* driver) + : Kross::Api::Class("KexiDBDriver") + , m_driver(driver) +{ + this->addFunction0("isValid", this, &KexiDBDriver::isValid ); + this->addFunction0("versionMajor", this, &KexiDBDriver::versionMajor ); + this->addFunction0("versionMinor", this, &KexiDBDriver::versionMinor ); + this->addFunction1("escapeString", this, &KexiDBDriver::escapeString); + this->addFunction0("isFileDriver", this, &KexiDBDriver::isFileDriver ); + this->addFunction0("fileDBDriverMimeType", this, &KexiDBDriver::fileDBDriverMimeType ); + this->addFunction1("isSystemObjectName", this, &KexiDBDriver::isSystemObjectName ); + this->addFunction1("isSystemDatabaseName", this, &KexiDBDriver::isSystemDatabaseName ); + this->addFunction1("isSystemFieldName", this, &KexiDBDriver::isSystemFieldName ); + this->addFunction2 ("valueToSQL", this, &KexiDBDriver::valueToSQL ); + + this->addFunction1("createConnection", this, &KexiDBDriver::createConnection); + this->addFunction0< Kross::Api::ListT< KexiDBConnection > >("connectionsList", this, &KexiDBDriver::connectionsList); +} + +KexiDBDriver::~KexiDBDriver() +{ +} + +const QString KexiDBDriver::getClassName() const +{ + return "Kross::KexiDB::KexiDBDriver"; +} + +bool KexiDBDriver::isValid() { return m_driver->isValid(); } +int KexiDBDriver::versionMajor() { return m_driver->version().major; } +int KexiDBDriver::versionMinor() { return m_driver->version().minor; } +QString KexiDBDriver::escapeString(const QString& s) { return m_driver->escapeString(s); } +bool KexiDBDriver::isFileDriver() { return m_driver->isFileDriver(); } +QString KexiDBDriver::fileDBDriverMimeType() { return m_driver->fileDBDriverMimeType(); } +bool KexiDBDriver::isSystemObjectName(const QString& name) { return m_driver->isSystemObjectName(name); } +bool KexiDBDriver::isSystemDatabaseName(const QString& name) { return m_driver->isSystemDatabaseName(name); } +bool KexiDBDriver::isSystemFieldName(const QString& name) { return m_driver->isSystemFieldName(name); } +QString KexiDBDriver::valueToSQL(const QString& fieldtype, const QVariant& value) { return m_driver->valueToSQL(fieldtype, value); } +KexiDBConnection* KexiDBDriver::createConnection(KexiDBConnectionData* data) { return new KexiDBConnection( m_driver->createConnection(*data) ); } +QPtrList< ::KexiDB::Connection > KexiDBDriver::connectionsList() { return m_driver->connectionsList(); } diff --git a/kexi/plugins/scripting/kexidb/kexidbdriver.h b/kexi/plugins/scripting/kexidb/kexidbdriver.h new file mode 100644 index 00000000..edf7283c --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbdriver.h @@ -0,0 +1,114 @@ +/*************************************************************************** + * kexidbdriver.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBDRIVER_H +#define KROSS_KEXIDB_KEXIDBDRIVER_H + +#include +#include +//#include + +#include +#include +#include +#include + +#include + +#include "kexidbconnection.h" +#include "kexidbconnectiondata.h" + +namespace Kross { namespace KexiDB { + + /** + * Drivers are the implementations Kexi uses to access the + * driver-backends. + * + * Example (in Python) ; + * @code + * # Import the kexidb module. + * import krosskexidb + * # Get the drivermanager. + * drivermanager = krosskexidb.DriverManager() + * # Create the driver now. + * driver = drivermanager.driver("SQLite3") + * # Check if the driver is valid. + * if not driver.isValid(): raise "Invalid driver" + * # Create a connectiondata object. + * connectiondata = drivermanager.createConnectionData() + * # Fill the new connectiondata object with what we need to connect. + * connectiondata.setFileName("/home/user/kexisqlite3file.kexi") + * # Print the list of connections before. + * print driver.connectionsList() + * # Create the connection now. + * connection = driver.createConnection(connectiondata) + * # Print the list of connections again. This includes our just created connection now. + * print driver.connectionsList() + * @endcode + */ + class KexiDBDriver : public Kross::Api::Class + { + public: + KexiDBDriver(::KexiDB::Driver* driver); + virtual ~KexiDBDriver(); + virtual const QString getClassName() const; + + private: + + /** Return true if this driver is valid else false. */ + bool isValid(); + /** The drivers major versionnumber. */ + int versionMajor(); + /** The drivers minor versionnumber. */ + int versionMinor(); + /** Driver-specific SQL string escaping. For example the " or ' char may + need to be escaped for values used within SQL-statements. */ + QString escapeString(const QString& s); + /** Returns true if this driver is file-based. */ + bool isFileDriver(); + /** Return a name of MIME type of files handled by this driver if it is a + file-based database's driver otherwise returns null string. */ + QString fileDBDriverMimeType(); + /** Returns true if the passed string is a system object's name, eg. name + of build-in system table that cannot be used or created by a user. */ + bool isSystemObjectName(const QString& name); + /** Returns true if the passed string is a system database's name, eg. name + of build-in, system database that cannot be used or created by a user. */ + bool isSystemDatabaseName(const QString& name); + /** Returns true if the passed string is a system field's name, build-in + system field that cannot be used or created by a user. */ + bool isSystemFieldName(const QString& name); + /** The as second argument passed string got escaped to be usable within + a SQL-statement and those escaped string got returned by the method. + The first argument defines the fieldtype to what we should escape the + second argument to. */ + QString valueToSQL(const QString& fieldtype, const QVariant& value); + /** Create a new KexiDBConnection object and return it. */ + KexiDBConnection* createConnection(KexiDBConnectionData* data); + /** Return a list of KexiDBConnection objects. */ + QPtrList< ::KexiDB::Connection > connectionsList(); + + private: + ::KexiDB::Driver* m_driver; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp new file mode 100644 index 00000000..66a0df26 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.cpp @@ -0,0 +1,178 @@ +/*************************************************************************** + * kexidbdrivermanager.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbdrivermanager.h" +#include "kexidbdriver.h" +#include "kexidbconnectiondata.h" +#include "kexidbfield.h" +#include "kexidbschema.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Kross::KexiDB; + +KexiDBDriverManager::KexiDBDriverManager() + : Kross::Api::Class("DriverManager") +{ + //krossdebug( QString("Kross::KexiDB::KexiDBDriverManager::KexiDBDriverManager()") ); + + this->addFunction0< Kross::Api::Variant >("driverNames", this, &KexiDBDriverManager::driverNames); + + this->addFunction1< KexiDBDriver, Kross::Api::Variant >("driver", this, &KexiDBDriverManager::driver); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("lookupByMime", this, &KexiDBDriverManager::lookupByMime); + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("mimeForFile", this, &KexiDBDriverManager::mimeForFile); + + this->addFunction0< KexiDBConnectionData >("createConnectionData", this, &KexiDBDriverManager::createConnectionData); + this->addFunction1< KexiDBConnectionData, Kross::Api::Variant >("createConnectionDataByFile", this, &KexiDBDriverManager::createConnectionDataByFile); + this->addFunction0< KexiDBField >("field", this, &KexiDBDriverManager::field); + this->addFunction1< KexiDBTableSchema, Kross::Api::Variant >("tableSchema", this, &KexiDBDriverManager::tableSchema); + this->addFunction0< KexiDBQuerySchema>("querySchema", this, &KexiDBDriverManager::querySchema); +} + +KexiDBDriverManager::~KexiDBDriverManager() { + //krossdebug( QString("Kross::KexiDB::KexiDBDriverManager::~KexiDBDriverManager()") ); +} + +const QString KexiDBDriverManager::getClassName() const { + return "Kross::KexiDB::KexiDBDriverManager"; +} + +KexiDB::DriverManager& KexiDBDriverManager::driverManager() +{ + if(m_drivermanager.error()) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::DriverManager error: %1").arg(m_drivermanager.errorMsg())) ); + return m_drivermanager; +} + +const QStringList KexiDBDriverManager::driverNames() { + return driverManager().driverNames(); +} + +KexiDBDriver* KexiDBDriverManager::driver(const QString& drivername) { + QGuardedPtr< ::KexiDB::Driver > driver = driverManager().driver(drivername); // caching is done by the DriverManager + if(! driver) return 0; + if(driver->error()) throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("KexiDB::Driver error for drivername '%1': %2").arg(drivername).arg(driver->errorMsg())) ); + return new KexiDBDriver(driver); +} + +const QString KexiDBDriverManager::lookupByMime(const QString& mimetype) { + return driverManager().lookupByMime(mimetype); +} + +const QString KexiDBDriverManager::mimeForFile(const QString& filename) { + QString mimename = KMimeType::findByFileContent( filename )->name(); + if(mimename.isEmpty() || mimename=="application/octet-stream" || mimename=="text/plain") + mimename = KMimeType::findByURL(filename)->name(); + return mimename; +} + +KexiDBConnectionData* KexiDBDriverManager::createConnectionData() { + return new KexiDBConnectionData( new ::KexiDB::ConnectionData() ); +} + +KexiDBConnectionData* KexiDBDriverManager::createConnectionDataByFile(const QString& filename) { + //! @todo reuse the original code! + + QString mimename = KMimeType::findByFileContent(filename)->name(); + if(mimename.isEmpty() || mimename=="application/octet-stream" || mimename=="text/plain") + mimename = KMimeType::findByURL(filename)->name(); + + if(mimename == "application/x-kexiproject-shortcut" || mimename == "application/x-kexi-connectiondata") { + KConfig config(filename, true, false); + QString groupkey; + QStringList groups(config.groupList()); + QStringList::ConstIterator it, end( groups.constEnd() ); + for( it = groups.constBegin(); it != end; ++it) { + if((*it).lower()!="file information") { + groupkey = *it; + break; + } + } + if(groupkey.isNull()) { + kdDebug() << "No groupkey in KexiDBDriverManager::createConnectionDataByFile filename=" << filename << endl; + return 0; + } + + config.setGroup(groupkey); + //QString type( config.readEntry("type", "database").lower() ); + //bool isDatabaseShortcut = (type == "database"); + + ::KexiDB::ConnectionData* data = new ::KexiDB::ConnectionData(); + int version = config.readNumEntry("version", 2); //KexiDBShortcutFile_version + data->setFileName(QString::null); + data->caption = config.readEntry("caption"); + data->description = config.readEntry("comment"); + QString dbname = config.readEntry("name"); + data->driverName = config.readEntry("engine"); + data->hostName = config.readEntry("server"); + data->port = config.readNumEntry("port", 0); + data->useLocalSocketFile = config.readBoolEntry("useLocalSocketFile", false); + data->localSocketFileName = config.readEntry("localSocketFile"); + + if(version >= 2 && config.hasKey("encryptedPassword")) { + data->password = config.readEntry("encryptedPassword"); + uint len = data->password.length(); + for (uint i=0; ipassword[i] = QChar( data->password[i].unicode() - 47 - i ); + } + if(data->password.isEmpty()) + data->password = config.readEntry("password"); + + data->savePassword = ! data->password.isEmpty(); + data->userName = config.readEntry("user"); + + KexiDBConnectionData* c = new KexiDBConnectionData(data); + c->setDatabaseName(dbname); + return c; + } + + QString const drivername = driverManager().lookupByMime(mimename); + if(! drivername) { + kdDebug() << "No driver in KexiDBDriverManager::createConnectionDataByFile filename=" << filename << " mimename=" << mimename << endl; + return 0; + } + + ::KexiDB::ConnectionData* data = new ::KexiDB::ConnectionData(); + data->setFileName(filename); + data->driverName = drivername; + return new KexiDBConnectionData(data); +} + +KexiDBField* KexiDBDriverManager::field() { + return new KexiDBField( new ::KexiDB::Field() ); +} + +KexiDBTableSchema* KexiDBDriverManager::tableSchema(const QString& tablename) { + return new KexiDBTableSchema( new ::KexiDB::TableSchema(tablename) ); +} + +KexiDBQuerySchema* KexiDBDriverManager::querySchema() { + return new KexiDBQuerySchema( new ::KexiDB::QuerySchema() ); +} + diff --git a/kexi/plugins/scripting/kexidb/kexidbdrivermanager.h b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.h new file mode 100644 index 00000000..b6e31108 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbdrivermanager.h @@ -0,0 +1,105 @@ +/*************************************************************************** + * kexidbdrivermanager.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBDRIVERMANAGER_H +#define KROSS_KEXIDB_KEXIDBDRIVERMANAGER_H + +#include + +#include +#include +#include +#include + +#include + +namespace Kross { namespace KexiDB { + + // Forward declarations. + class KexiDBDriver; + class KexiDBConnectionData; + class KexiDBField; + class KexiDBTableSchema; + class KexiDBQuerySchema; + + /** + * The drivermanager is the base class to access KexiDBDriver objects and provides + * common functionality to deal with the KexiDB module. + * + * Example (in Python) ; + * @code + * # Import the kexidb module. + * import krosskexidb + * # Get the drivermanager. + * drivermanager = krosskexidb.DriverManager() + * # Let's determinate the mimetype (e.g. "application/x-sqlite3"). + * mimetype = drivermanager.mimeForFile("/home/user/mykexidbfile.kexi") + * # Now we use that mimetype to get the name of the driver to handle that file (e.g. "SQLite3") + * drivername = drivermanager.lookupByMime(mimetype) + * # We are able to create the driver now. + * driver = drivermanager.driver(drivername) + * @endcode + */ + class KexiDBDriverManager : public Kross::Api::Class + { + public: + KexiDBDriverManager(); + virtual ~KexiDBDriverManager(); + virtual const QString getClassName() const; + + private: + + /** Returns a list with avaible drivernames. */ + const QStringList driverNames(); + + /** Return the to the defined drivername matching KexiDBDriver object. */ + KexiDBDriver* driver(const QString& drivername); + + /** Return the to the defined mimetype-string matching drivername. */ + const QString lookupByMime(const QString& mimetype); + + /** Return the matching mimetype for the defined file. */ + const QString mimeForFile(const QString& filename); + + /** Return a new KexiDBConnectionData object. */ + KexiDBConnectionData* createConnectionData(); + + /** Create and return a KexiDBConnectionData object. Fill the content of the + KexiDBConnectionData object with the defined file as. The file could be e.g. + a *.kexi file or a *.kexis file. */ + KexiDBConnectionData* createConnectionDataByFile(const QString& filename); + + /** Return a new KexiDBField object. */ + KexiDBField* field(); + + /** Return a new KexiDBTableSchema object. */ + KexiDBTableSchema* tableSchema(const QString& tablename); + + /** Return a new KexiDBQuerySchema object. */ + KexiDBQuerySchema* querySchema(); + + private: + inline ::KexiDB::DriverManager& driverManager(); + ::KexiDB::DriverManager m_drivermanager; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbfield.cpp b/kexi/plugins/scripting/kexidb/kexidbfield.cpp new file mode 100644 index 00000000..949b5e1a --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbfield.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + * kexidbfield.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbfield.h" + +#include + +using namespace Kross::KexiDB; + +KexiDBField::KexiDBField(::KexiDB::Field* field) + : Kross::Api::Class("KexiDBField") + , m_field(field) +{ + this->addFunction0< Kross::Api::Variant >("type", this, &KexiDBField::type); + this->addFunction1< void, Kross::Api::Variant >("setType", this, &KexiDBField::setType); + + this->addFunction0< Kross::Api::Variant >("subType", this, &KexiDBField::subType); + this->addFunction1< void, Kross::Api::Variant >("setSubType", this, &KexiDBField::setSubType); + + this->addFunction0< Kross::Api::Variant >("variantType", this, &KexiDBField::variantType); + this->addFunction0< Kross::Api::Variant >("typeGroup", this, &KexiDBField::typeGroup); + + this->addFunction0< Kross::Api::Variant >("isAutoInc", this, &KexiDBField::isAutoInc); + this->addFunction1< void, Kross::Api::Variant >("setAutoInc", this, &KexiDBField::setAutoInc); + + this->addFunction0< Kross::Api::Variant >("isUniqueKey", this, &KexiDBField::isUniqueKey); + this->addFunction1< void, Kross::Api::Variant >("setUniqueKey", this, &KexiDBField::setUniqueKey); + + this->addFunction0< Kross::Api::Variant >("isPrimaryKey", this, &KexiDBField::isPrimaryKey); + this->addFunction1< void, Kross::Api::Variant >("setPrimaryKey", this, &KexiDBField::setPrimaryKey); + + this->addFunction0< Kross::Api::Variant >("isForeignKey", this, &KexiDBField::isForeignKey); + this->addFunction1< void, Kross::Api::Variant >("setForeignKey", this, &KexiDBField::setForeignKey); + + this->addFunction0< Kross::Api::Variant >("isNotNull", this, &KexiDBField::isNotNull); + this->addFunction1< void, Kross::Api::Variant >("setNotNull", this, &KexiDBField::setNotNull); + + this->addFunction0< Kross::Api::Variant >("isNotEmpty", this, &KexiDBField::isNotEmpty); + this->addFunction1< void, Kross::Api::Variant >("setNotEmpty", this, &KexiDBField::setNotEmpty); + + this->addFunction0< Kross::Api::Variant >("isIndexed", this, &KexiDBField::isIndexed); + this->addFunction1< void, Kross::Api::Variant >("setIndexed", this, &KexiDBField::setIndexed); + + this->addFunction0< Kross::Api::Variant >("isUnsigned", this, &KexiDBField::isUnsigned); + this->addFunction1< void, Kross::Api::Variant >("setUnsigned", this, &KexiDBField::setUnsigned); + + this->addFunction0< Kross::Api::Variant >("name", this, &KexiDBField::name); + this->addFunction1< void, Kross::Api::Variant >("setName", this, &KexiDBField::setName); + + this->addFunction0< Kross::Api::Variant >("caption", this, &KexiDBField::caption); + this->addFunction1< void, Kross::Api::Variant >("setCaption", this, &KexiDBField::setCaption); + + this->addFunction0< Kross::Api::Variant >("description", this, &KexiDBField::description); + this->addFunction1< void, Kross::Api::Variant >("setDescription", this, &KexiDBField::setDescription); + + this->addFunction0< Kross::Api::Variant >("length", this, &KexiDBField::length); + this->addFunction1< void, Kross::Api::Variant >("setLength", this, &KexiDBField::setLength); + + this->addFunction0< Kross::Api::Variant >("precision", this, &KexiDBField::precision); + this->addFunction1< void, Kross::Api::Variant >("setPrecision", this, &KexiDBField::setPrecision); + + this->addFunction0< Kross::Api::Variant >("width", this, &KexiDBField::width); + this->addFunction1< void, Kross::Api::Variant >("setWidth", this, &KexiDBField::setWidth); + + this->addFunction0< Kross::Api::Variant >("defaultValue", this, &KexiDBField::defaultValue); + this->addFunction1< void, Kross::Api::Variant >("setDefaultValue", this, &KexiDBField::setDefaultValue); +} + +KexiDBField::~KexiDBField() +{ +} + +const QString KexiDBField::getClassName() const +{ + return "Kross::KexiDB::KexiDBField"; +} + +const QString KexiDBField::type() { return m_field->typeString(); } +void KexiDBField::setType(const QString type) { m_field->setType( ::KexiDB::Field::typeForString(type) ); } + +const QString KexiDBField::subType() { return m_field->subType(); } +void KexiDBField::setSubType(const QString& subtype) { m_field->setSubType(subtype); } + +const QString KexiDBField::variantType() { return QVariant::typeToName( m_field->variantType() ); } +const QString KexiDBField::typeGroup() { return m_field->typeGroupString(); } + +bool KexiDBField::isAutoInc() { return m_field->isAutoIncrement(); } +void KexiDBField::setAutoInc(bool autoinc) { m_field->setAutoIncrement(autoinc); } + +bool KexiDBField::isUniqueKey() { return m_field->isUniqueKey(); } +void KexiDBField::setUniqueKey(bool unique) { m_field->setUniqueKey(unique); } + +bool KexiDBField::isPrimaryKey() { return m_field->isPrimaryKey(); } +void KexiDBField::setPrimaryKey(bool primary) { m_field->setPrimaryKey(primary); } + +bool KexiDBField::isForeignKey() { return m_field->isForeignKey(); } +void KexiDBField::setForeignKey(bool foreign) { m_field->setForeignKey(foreign); } + +bool KexiDBField::isNotNull() { return m_field->isNotNull(); } +void KexiDBField::setNotNull(bool notnull) { m_field->setNotNull(notnull); } + +bool KexiDBField::isNotEmpty() { return m_field->isNotEmpty(); } +void KexiDBField::setNotEmpty(bool notempty) { m_field->setNotEmpty(notempty); } + +bool KexiDBField::isIndexed() { return m_field->isIndexed(); } +void KexiDBField::setIndexed(bool indexed) { m_field->setIndexed(indexed); } + +bool KexiDBField::isUnsigned() { return m_field->isUnsigned(); } +void KexiDBField::setUnsigned(bool isunsigned) { m_field->setUnsigned(isunsigned); } + +const QString KexiDBField::name() { return m_field->name(); } +void KexiDBField::setName(const QString& name) { m_field->setName(name); } + +const QString KexiDBField::caption() { return m_field->caption(); } +void KexiDBField::setCaption(const QString& caption) { m_field->setCaption(caption); } + +const QString KexiDBField::description() { return m_field->description(); } +void KexiDBField::setDescription(const QString& desc) { m_field->setDescription(desc); } + +uint KexiDBField::length() { return m_field->length(); } +void KexiDBField::setLength(uint length) { m_field->setLength(length); } + +uint KexiDBField::precision() { return m_field->precision(); } +void KexiDBField::setPrecision(uint precision) { m_field->setPrecision(precision); } + +uint KexiDBField::width() { return m_field->width(); } +void KexiDBField::setWidth(uint width) { m_field->setWidth(width); } + +QVariant KexiDBField::defaultValue() { return m_field->defaultValue(); } +void KexiDBField::setDefaultValue(const QVariant& defaultvalue) { m_field->setDefaultValue(defaultvalue); } diff --git a/kexi/plugins/scripting/kexidb/kexidbfield.h b/kexi/plugins/scripting/kexidb/kexidbfield.h new file mode 100644 index 00000000..a4c2ef23 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbfield.h @@ -0,0 +1,148 @@ +/*************************************************************************** + * kexidbfield.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBFIELD_H +#define KROSS_KEXIDB_KEXIDBFIELD_H + +#include + +#include +#include +#include + +#include +#include + +namespace Kross { namespace KexiDB { + + /** + * A field in a record. + */ + class KexiDBField : public Kross::Api::Class + { + public: + KexiDBField(::KexiDB::Field* field); + virtual ~KexiDBField(); + virtual const QString getClassName() const; + ::KexiDB::Field* field() { return m_field; } + + private: + + /** Returns the type string for this field, e.g. "Integer" for Integer type. */ + const QString type(); + /** Sets the type string for this field, e.g. "Integer" for Integer type. */ + void setType(const QString type); + + /** Returns the optional subtype for this field. Subtype is a string providing + additional hint for field's type. E.g. for BLOB type, it can be a MIME type or + certain QVariant type name, for example: "QPixmap", "QColor" or "QFont". */ + const QString subType(); + /** Sets the optional subtype for this field. */ + void setSubType(const QString& subtype); + + /** Returns the QVariant::typeName which is equivalent to the type this field has. */ + const QString variantType(); + /** Returns type group string for this field, e.g. "IntegerGroup" for IntegerGroup type. */ + const QString typeGroup(); + + /** Returns true if the field is autoincrement (e.g. integer/numeric). */ + bool isAutoInc(); + /** Sets auto increment flag. */ + void setAutoInc(bool autoinc); + + /** Returns true if the field is member of single-field unique key. */ + bool isUniqueKey(); + /** Specifies whether the field has single-field unique constraint or not. */ + void setUniqueKey(bool unique); + + /** Returns true if the field is member of single-field primary key. */ + bool isPrimaryKey(); + /** Specifies whether the field is single-field primary key or not. */ + void setPrimaryKey(bool primary); + + /** Returns true if the field is member of single-field foreign key. */ + bool isForeignKey(); + /** Sets whether the field has to be declared with single-field foreign key. */ + void setForeignKey(bool foreign); + + /** Returns true if the field is not allowed to be null. */ + bool isNotNull(); + /** Specifies whether the field has single-field unique constraint or not. */ + void setNotNull(bool notnull); + + /** Returns true if the field is not allowed to be empty. */ + bool isNotEmpty(); + /** Specifies whether the field has single-field unique constraint or not. */ + void setNotEmpty(bool notempty); + + /** Returns true if the field is indexed using single-field database index. */ + bool isIndexed(); + /** Specifies whether the field is indexed or not. */ + void setIndexed(bool indexed); + + /** Returns true if the field is an unsigned integer. */ + bool isUnsigned(); + /** Specifies whether the field is an unsigned integer or not. */ + void setUnsigned(bool isunsigned); + + /** Returns the name of this field. */ + const QString name(); + /** Sets the name of this field. */ + void setName(const QString& name); + + /** Returns the caption of this field. */ + const QString caption(); + /** Sets the caption of this field. */ + void setCaption(const QString& caption); + + /** Returns the descriptive text for this field. */ + const QString description(); + /** Set the description for this field. */ + void setDescription(const QString& desc); + + /** Returns the length of text if the field type is text. */ + uint length(); + /** Sets the length for this field. Only works for Text Type (not including LongText). */ + void setLength(uint length); + + /** Returns precision for numeric and other fields that have both length and + precision (floating point types). */ + uint precision(); + /** Sets the precision for numeric and other fields. */ + void setPrecision(uint precision); + + /** Returns the width of this field (usually in pixels or points). + 0 (the default) means there is no hint for the width. */ + uint width(); + /** Sets the width of this field. */ + void setWidth(uint width); + + /** Returns the default value this field has. */ + QVariant defaultValue(); + /** Sets the default value this field has. */ + void setDefaultValue(const QVariant& defaultvalue); + + private: + ::KexiDB::Field* m_field; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp b/kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp new file mode 100644 index 00000000..f36bf0b0 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbfieldlist.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** + * kexidbfieldlist.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbfieldlist.h" +#include "kexidbfield.h" + +#include +#include + +#include + +using namespace Kross::KexiDB; + +KexiDBFieldList::KexiDBFieldList(::KexiDB::FieldList* fieldlist) + : Kross::Api::Class("KexiDBFieldList") + , m_fieldlist(fieldlist) +{ + this->addFunction0< Kross::Api::Variant >("fieldCount", this, &KexiDBFieldList::fieldCount); + this->addFunction1< KexiDBField, Kross::Api::Variant >("field", this, &KexiDBFieldList::field); + this->addFunction1< KexiDBField, Kross::Api::Variant >("fieldByName", this, &KexiDBFieldList::fieldByName); + + this->addFunction0< Kross::Api::List >("fields", this, &KexiDBFieldList::fields); + + this->addFunction1< Kross::Api::Variant, KexiDBField >("hasField", this, &KexiDBFieldList::hasField); + this->addFunction0< Kross::Api::Variant >("names", this, &KexiDBFieldList::names); + + this->addFunction1< void, KexiDBField >("addField", this, &KexiDBFieldList::addField); + this->addFunction2< void, Kross::Api::Variant, KexiDBField >("insertField", this, &KexiDBFieldList::insertField); + this->addFunction1< void, KexiDBField >("removeField", this, &KexiDBFieldList::removeField); + this->addFunction0< void >("clear", this, &KexiDBFieldList::clear); + this->addFunction1< void, KexiDBFieldList >("setFields", this, &KexiDBFieldList::setFields); + + this->addFunction1< KexiDBFieldList, Kross::Api::Variant >("subList", this, &KexiDBFieldList::subList); +} + +KexiDBFieldList::~KexiDBFieldList() +{ +} + +const QString KexiDBFieldList::getClassName() const +{ + return "Kross::KexiDB::KexiDBFieldList"; +} + +uint KexiDBFieldList::fieldCount() { + return m_fieldlist->fieldCount(); +} + +KexiDBField* KexiDBFieldList::field(uint index) { + ::KexiDB::Field* field = m_fieldlist->field(index); + return field ? new KexiDBField(field) : 0; +} + +KexiDBField* KexiDBFieldList::fieldByName(const QString& name) { + ::KexiDB::Field* field = m_fieldlist->field(name); + return field ? new KexiDBField(field) : 0; +} + +Kross::Api::List* KexiDBFieldList::fields() { + return new Kross::Api::ListT( *m_fieldlist->fields() ); +} + +bool KexiDBFieldList::hasField(KexiDBField* field) { return m_fieldlist->hasField( field->field() ); } +const QStringList KexiDBFieldList::names() const { return m_fieldlist->names(); } +void KexiDBFieldList::addField(KexiDBField* field) { m_fieldlist->addField( field->field() ); } +void KexiDBFieldList::insertField(uint index, KexiDBField* field) { m_fieldlist->insertField(index, field->field()); } +void KexiDBFieldList::removeField(KexiDBField* field) { m_fieldlist->removeField( field->field() ); } +void KexiDBFieldList::clear() { m_fieldlist->clear(); } + +void KexiDBFieldList::setFields(KexiDBFieldList* fieldlist) { + m_fieldlist->clear(); + ::KexiDB::FieldList* fl = fieldlist->fieldlist(); + for(::KexiDB::Field::ListIterator it = *fl->fields(); it.current(); ++it) + m_fieldlist->addField( it.current() ); +} + +KexiDBFieldList* KexiDBFieldList::subList(QValueList list) { + QValueList::ConstIterator it( list.constBegin() ), end( list.constEnd() ); + QStringList sl; + for(; it != end; ++it) sl.append( (*it).toString() ); + ::KexiDB::FieldList* fl = m_fieldlist->subList(sl); + return fl ? new Kross::KexiDB::KexiDBFieldList(fl) : 0; +} + diff --git a/kexi/plugins/scripting/kexidb/kexidbfieldlist.h b/kexi/plugins/scripting/kexidb/kexidbfieldlist.h new file mode 100644 index 00000000..ee990eb3 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbfieldlist.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * kexidbfieldlist.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBFIELDLIST_H +#define KROSS_KEXIDB_KEXIDBFIELDLIST_H + +#include + +#include +#include +#include + +#include +#include + +namespace Kross { namespace KexiDB { + + // Forward declarations. + class KexiDBField; + class KexiDBFieldList; + + /** + * A list of fields. The KexiDBFieldList can be used to handle KexiDBField objects + * in a backend-independent way. + * + * Example (in Python) ; + * @code + * # Get the tableschema for the "dept" table. + * table = connection.tableSchema("dept") + * # Create a KexiDBFieldList based on the table and filled with the selected fields. + * subfields = ["deptno","name","loc"] + * fieldlist = table.fieldlist().subList(subfields) + * # Create the "SELECT * from dept;" queryschema. + * query = table.query() + * # We change the queryschema to "SELECT deptno,name,loc FROM dept;" now. + * query.fieldlist().setFields(fieldlist) + * # and change the query to "SELECT deptno,name,loc FROM dept WHERE deptno=5;" + * query.setWhereExpression("deptno=5") + * # Execute the query and get a KexiDBCursor object as result which could be used to iterate through the result. + * cursor = connection.executeQuerySchema(query) + * @endcode + */ + class KexiDBFieldList : public Kross::Api::Class + { + public: + KexiDBFieldList(::KexiDB::FieldList* fieldlist); + virtual ~KexiDBFieldList(); + virtual const QString getClassName() const; + ::KexiDB::FieldList* fieldlist() { return m_fieldlist; } + + private: + + /** Returns the number of fields. */ + uint fieldCount(); + /** Return the field specified by the index-number passed as an argument. */ + KexiDBField* field(uint index); + /** Return the field specified by the as an argument passed fieldname. */ + KexiDBField* fieldByName(const QString& name); + + /** Returns a list of all fields. */ + Kross::Api::List* fields(); + /** Returns true if the KexiDBField object passed as an argument is in the field list. */ + bool hasField(KexiDBField* field); + /** Return a list of field names. */ + const QStringList names() const; + + /** Adds the KexiDBField object passed as an argument to the field list. */ + void addField(KexiDBField* field); + /** Inserts the KexiDBField object passed as the second argument + into the field list at the position defined by the first argument. */ + void insertField(uint index, KexiDBField* field); + /** Removes the KexiDBField object passed as an argument from the field list. */ + void removeField(KexiDBField* field); + /** Removes all KexiDBField objects from the fieldlist. */ + void clear(); + /** Set the fieldlist to the as argument passed list of fields. */ + void setFields(KexiDBFieldList* fieldlist); + /** Creates and returns list that contain fields selected by name. */ + KexiDBFieldList* subList(QValueList list); + + private: + ::KexiDB::FieldList* m_fieldlist; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbmodule.cpp b/kexi/plugins/scripting/kexidb/kexidbmodule.cpp new file mode 100644 index 00000000..36f7b71f --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbmodule.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * kexidbmodule.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbmodule.h" +#include "kexidbdrivermanager.h" +#include "kexidbconnection.h" + +//#include +//#include +#include
+ +#include + +// The as version() published versionnumber of this kross-module. +#define KROSS_KEXIDB_VERSION 1 + +extern "C" +{ + /** + * Exported an loadable function as entry point to use + * the \a KexiDBModule. + */ + Kross::Api::Object* KDE_EXPORT init_module(Kross::Api::Manager* manager) + { + return new Kross::KexiDB::KexiDBModule(manager); + } +} + +using namespace Kross::KexiDB; + +KexiDBModule::KexiDBModule(Kross::Api::Manager* /*manager*/) + : Kross::Api::Module("KexiDB") + //, m_manager(manager) +{ + //kdDebug() << "Kross::KexiDB::KexiDBModule Ctor" << endl; + addChild( "version", new Kross::Api::Variant(KROSS_KEXIDB_VERSION) ); + addChild( new KexiDBDriverManager() ); +} + +KexiDBModule::~KexiDBModule() +{ + //kdDebug() << "Kross::KexiDB::KexiDBModule Dtor" << endl; +} + +const QString KexiDBModule::getClassName() const +{ + return "Kross::KexiDB::KexiDBModule"; +} + +Kross::Api::Object::Ptr KexiDBModule::get(const QString& name, void* p) +{ + if(name == "KexiDBConnection") { + ::KexiDB::Connection* connection = (::KexiDB::Connection*)p; + if(connection) + return new KexiDBConnection(connection); + } + return 0; +} diff --git a/kexi/plugins/scripting/kexidb/kexidbmodule.h b/kexi/plugins/scripting/kexidb/kexidbmodule.h new file mode 100644 index 00000000..b91b6047 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbmodule.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * kexidbmodule.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBMODULE_H +#define KROSS_KEXIDB_KEXIDBMODULE_H + +#include +#include + +#include + +namespace Kross { namespace Api { + class Manager; +}} + +namespace Kross { + +/** + * KrossKexiDB provides access to the KexiDB database functionality. + */ +namespace KexiDB { + + /** + * \internal + * The KexiDBModule is the implementation of a kross-module. + */ + class KexiDBModule : public Kross::Api::Module + { + public: + KexiDBModule(Kross::Api::Manager* manager); + virtual ~KexiDBModule(); + virtual const QString getClassName() const; + + /** + * \internal + * Variable module-method use to call transparent some functionality + * the module provides. + * + * \param name A name passed to the method. This name is used internaly + * to determinate what the caller likes to do. Each implemented + * module have to implement what should be done. + * \param p A variable pointer passed to the method. It depends on + * the module and the name what this pointer is. + * \return a \a Kross::Api::Object or NULL. + */ + virtual Kross::Api::Object::Ptr get(const QString& name, void* p = 0); + + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbparser.cpp b/kexi/plugins/scripting/kexidb/kexidbparser.cpp new file mode 100644 index 00000000..b022570d --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbparser.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * kexidbparser.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbparser.h" +#include "kexidbschema.h" +#include "kexidbconnection.h" + +#include + +using namespace Kross::KexiDB; + +KexiDBParser::KexiDBParser(KexiDBConnection* connection, ::KexiDB::Parser* parser) + : Kross::Api::Class("KexiDBParser") + , m_connection(connection) + , m_parser(parser) +{ + this->addFunction1< Kross::Api::Variant, Kross::Api::Variant >("parse", this, &KexiDBParser::parse); + this->addFunction0< void >("clear", this, &KexiDBParser::clear); + + this->addFunction0< Kross::Api::Variant >("operation", this, &KexiDBParser::operation); + + this->addFunction0< KexiDBTableSchema >("table", this, &KexiDBParser::table); + this->addFunction0< KexiDBQuerySchema >("query", this, &KexiDBParser::query); + this->addFunction0< KexiDBConnection >("connection", this, &KexiDBParser::connection); + this->addFunction0< Kross::Api::Variant >("statement", this, &KexiDBParser::statement); + + this->addFunction0< Kross::Api::Variant >("errorType", this, &KexiDBParser::errorType); + this->addFunction0< Kross::Api::Variant >("errorMsg", this, &KexiDBParser::errorMsg); + this->addFunction0< Kross::Api::Variant >("errorAt", this, &KexiDBParser::errorAt); +} + +KexiDBParser::~KexiDBParser() +{ +} + +const QString KexiDBParser::getClassName() const +{ + return "Kross::KexiDB::KexiDBParser"; +} + +bool KexiDBParser::parse(const QString& sql) { return m_parser->parse(sql); } +void KexiDBParser::clear() { m_parser->clear(); } +const QString KexiDBParser::operation() { return m_parser->operationString(); } + +KexiDBTableSchema* KexiDBParser::table() { + ::KexiDB::TableSchema* t = m_parser->table(); + return t ? new KexiDBTableSchema(t) : 0; +} + +KexiDBQuerySchema* KexiDBParser::query() { + ::KexiDB::QuerySchema* q = m_parser->query(); + return q ? new KexiDBQuerySchema(q) : 0; +} + +KexiDBConnection* KexiDBParser::connection() { return m_connection; } +const QString KexiDBParser::statement() { return m_parser->statement(); } + +const QString KexiDBParser::errorType() { return m_parser->error().type(); } +const QString KexiDBParser::errorMsg() { return m_parser->error().error(); } +int KexiDBParser::errorAt() { return m_parser->error().at(); } diff --git a/kexi/plugins/scripting/kexidb/kexidbparser.h b/kexi/plugins/scripting/kexidb/kexidbparser.h new file mode 100644 index 00000000..09ac22da --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbparser.h @@ -0,0 +1,95 @@ +/*************************************************************************** + * kexidbparser.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBPARSER_H +#define KROSS_KEXIDB_KEXIDBPARSER_H + +#include + +#include +#include +#include + +#include +#include + +namespace Kross { namespace KexiDB { + + // Forward declaration. + class KexiDBConnection; + class KexiDBTableSchema; + class KexiDBQuerySchema; + + /** + * The KexiDBParser could be used to parse SQL-statements. + * + * Example (in Python) ; + * @code + * # First we need a parser object. + * parser = connection.parser() + * # Parse a SQL-statement. + * parser.parse("SELECT * from table1") + * # The operation could be e.g. SELECT or INSERT. + * if parser.operation() == 'Error': + * raise parser.errorMsg() + * # Print some feedback. + * print "Successfully parsed the SQL-statement %s" % parser.statement() + * @endcode + */ + class KexiDBParser : public Kross::Api::Class + { + public: + KexiDBParser(KexiDBConnection* connection, ::KexiDB::Parser* parser); + virtual ~KexiDBParser(); + virtual const QString getClassName() const; + + private: + + /** Clears previous results and runs the parser on the SQL statement passed as an argument. */ + bool parse(const QString& sql); + /** Clears parsing results. */ + void clear(); + /** Returns the resulting operation. */ + const QString operation(); + + /** Returns the KexiDBTableSchema object on a CREATE TABLE operation. */ + KexiDBTableSchema* table(); + /** Returns the KexiDBQuerySchema object on a SELECT operation. */ + KexiDBQuerySchema* query(); + /** Returns the KexiDBConnection object pointing to the used database connection. */ + KexiDBConnection* connection(); + /** Returns the SQL query statement. */ + const QString statement(); + + /** Returns the type string of the last error. */ + const QString errorType(); + /** Returns the message of the last error. */ + const QString errorMsg(); + /** Returns the position where the last error occurred. */ + int errorAt(); + + private: + KexiDBConnection* m_connection; + ::KexiDB::Parser* m_parser; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbschema.cpp b/kexi/plugins/scripting/kexidb/kexidbschema.cpp new file mode 100644 index 00000000..e07917f3 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbschema.cpp @@ -0,0 +1,197 @@ +/*************************************************************************** + * kexidbschema.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbschema.h" +#include "kexidbfieldlist.h" + +#include +#include + +#include + +using namespace Kross::KexiDB; + +/*************************************************************************** + *KexiDBSchema + */ + +template +KexiDBSchema::KexiDBSchema(const QString& name, ::KexiDB::SchemaData* schema, ::KexiDB::FieldList* fieldlist) + : Kross::Api::Class(name) + , m_schema(schema) + , m_fieldlist(fieldlist) +{ + this->template addFunction0("name", this, &KexiDBSchema::name); + this->template addFunction1("setName", this, &KexiDBSchema::setName); + + this->template addFunction0("caption", this, &KexiDBSchema::caption); + this->template addFunction1("setCaption", this, &KexiDBSchema::setCaption); + + this->template addFunction0("description", this, &KexiDBSchema::description); + this->template addFunction1("setDescription", this, &KexiDBSchema::setDescription); + + this->template addFunction0("fieldlist", this, &KexiDBSchema::fieldlist); +} + +template +KexiDBSchema::~KexiDBSchema() { +} + +template +const QString KexiDBSchema::name() const { + return m_schema->name(); +} + +template +void KexiDBSchema::setName(const QString& name) { + m_schema->setName(name); +} + +template +const QString KexiDBSchema::caption() const { + return m_schema->caption(); +} + +template +void KexiDBSchema::setCaption(const QString& caption) { + m_schema->setCaption(caption); +} + +template +const QString KexiDBSchema::description() const { + return m_schema->description(); +} + +template +void KexiDBSchema::setDescription(const QString& description) { + m_schema->setDescription(description); +} + +template +KexiDBFieldList* KexiDBSchema::fieldlist() const { + return new KexiDBFieldList(m_fieldlist); +} + +/*************************************************************************** + * KexiDBTableSchema + */ + +KexiDBTableSchema::KexiDBTableSchema(::KexiDB::TableSchema* tableschema) + : KexiDBSchema("KexiDBTableSchema", tableschema, tableschema) +{ + this->addFunction0("query", this, &KexiDBTableSchema::query); +} + +KexiDBTableSchema::~KexiDBTableSchema() { +} + +const QString KexiDBTableSchema::getClassName() const { + return "Kross::KexiDB::KexiDBTableSchema"; +} + +::KexiDB::TableSchema* KexiDBTableSchema::tableschema() { + return static_cast< ::KexiDB::TableSchema* >(m_schema); +} + +KexiDBQuerySchema* KexiDBTableSchema::query() { + return new KexiDBQuerySchema( tableschema()->query() ); +} + +/*************************************************************************** + * KexiDBQuerySchema + */ + +KexiDBQuerySchema::KexiDBQuerySchema(::KexiDB::QuerySchema* queryschema) + : KexiDBSchema("KexiDBQuerySchema", queryschema, queryschema) +{ + this->addFunction0("statement", this, &KexiDBQuerySchema::statement); + this->addFunction1("setStatement", this, &KexiDBQuerySchema::setStatement); + this->addFunction1("setWhereExpression", this, &KexiDBQuerySchema::setWhereExpression); +} + +KexiDBQuerySchema::~KexiDBQuerySchema() { +} + +const QString KexiDBQuerySchema::getClassName() const { + return "Kross::KexiDB::KexiDBQuerySchema"; +} + +::KexiDB::QuerySchema* KexiDBQuerySchema::queryschema() { + return static_cast< ::KexiDB::QuerySchema* >(m_schema); +} + +const QString KexiDBQuerySchema::statement() const { + return static_cast< ::KexiDB::QuerySchema* >(m_schema)->statement(); +} + +void KexiDBQuerySchema::setStatement(const QString& statement) { + static_cast< ::KexiDB::QuerySchema* >(m_schema)->setStatement(statement); +} + +bool KexiDBQuerySchema::setWhereExpression(const QString& whereexpression) { + ::KexiDB::BaseExpr* oldexpr = static_cast< ::KexiDB::QuerySchema* >(m_schema)->whereExpression(); + + ///@todo use ::KexiDB::Parser for such kind of parser-functionality. + QString s = whereexpression; + try { + QRegExp re("[\"',]{1,1}"); + while(true) { + s.remove(QRegExp("^[\\s,]+")); + int pos = s.find('='); + if(pos < 0) break; + QString key = s.left(pos).stripWhiteSpace(); + s = s.mid(pos + 1).stripWhiteSpace(); + + QString value; + int sp = s.find(re); + if(sp >= 0) { + if(re.cap(0) == ",") { + value = s.left(sp).stripWhiteSpace(); + s = s.mid(sp+1).stripWhiteSpace(); + } + else { + int ep = s.find(re.cap(0),sp+1); + value = s.mid(sp+1,ep-1); + s = s.mid(ep + 1); + } + } + else { + value = s; + s = QString::null; + } + + ::KexiDB::Field* field = static_cast< ::KexiDB::QuerySchema* >(m_schema)->field(key); + if(! field) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid WHERE-expression: Field \"%1\" does not exists in tableschema \"%2\".").arg(key).arg(m_schema->name())) ); + + QVariant v(value); + if(! v.cast(field->variantType())) + throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(QString("Invalid WHERE-expression: The for Field \"%1\" defined value is of type \"%2\" rather then the expected type \"%3\"").arg(key).arg(v.typeName()).arg(field->variantType())) ); + + static_cast< ::KexiDB::QuerySchema* >(m_schema)->addToWhereExpression(field,v); + } + } + catch(Kross::Api::Exception::Ptr e) { + Kross::krosswarning("Exception in Kross::KexiDB::KexiDBQuerySchema::setWhereExpression: "); + static_cast< ::KexiDB::QuerySchema* >(m_schema)->setWhereExpression(oldexpr); // fallback + return false; + } + return true; +} diff --git a/kexi/plugins/scripting/kexidb/kexidbschema.h b/kexi/plugins/scripting/kexidb/kexidbschema.h new file mode 100644 index 00000000..61b6bc88 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbschema.h @@ -0,0 +1,134 @@ +/*************************************************************************** + * kexidbschema.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBSCHEMA_H +#define KROSS_KEXIDB_KEXIDBSCHEMA_H + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Kross { namespace KexiDB { + + // Forward-declarations. + class KexiDBFieldList; + class KexiDBQuerySchema; + + /** + * The KexiDBSchema object provides common functionality for schemas + * like KexiDBTableSchema or KexiDBQuerySchema. + * + * Example (in Python) ; + * @code + * # Get the tableschema from a KexiDBConnection object. + * tableschema = connection.tableSchema("dept") + * # Print some information. + * print "table=%s description=%s" % (tableschema.name(), tableschema.description()) + * # Get the "SELECT * FROM dept;" queryschema for the table. + * queryschema = tableschema.query() + * # Walk through the fields/columns the queryschema has and print the fieldnames. + * for field in queryschema.fieldlist().fields(): + * print "fieldname=%s" % field.name() + * # Execute the query. The returned KexiDBCursor object could be used then to iterate through the result. + * cursor = connection.executeQuerySchema(queryschema) + * @endcode + */ + template + class KexiDBSchema : public Kross::Api::Class + { + public: + KexiDBSchema(const QString& name, ::KexiDB::SchemaData* schema, ::KexiDB::FieldList* fieldlist); + virtual ~KexiDBSchema(); + + private: + + /** Returns the name of the schema. */ + const QString name() const; + /** Set the name of the schema. */ + void setName(const QString& name); + + /** Returns the caption of the schema. */ + const QString caption() const; + /** Set the caption of the schema. */ + void setCaption(const QString& caption); + + /** Returns a description of the schema. */ + const QString description() const; + /** Set a description of the schema. */ + void setDescription(const QString& description); + + /** Returns the KexiDBFieldList object this schema has. */ + KexiDBFieldList* fieldlist() const; + + protected: + ::KexiDB::SchemaData* m_schema; + ::KexiDB::FieldList* m_fieldlist; + }; + + /** + * The KexiDBTableSchema object implements a KexiDBSchema for tables. + */ + class KexiDBTableSchema : public KexiDBSchema + { + public: + KexiDBTableSchema(::KexiDB::TableSchema* tableschema); + virtual ~KexiDBTableSchema(); + virtual const QString getClassName() const; + ::KexiDB::TableSchema* tableschema(); + + private: + + /** Return the KexiDBQuerySchema object that represents a + "SELECT * FROM this_KexiDBTableSchema_object" SQL-statement. */ + KexiDBQuerySchema* query(); + + }; + + /** + * The KexiDBTableSchema object implements a KexiDBSchema for queries. + */ + class KexiDBQuerySchema : public KexiDBSchema + { + public: + KexiDBQuerySchema(::KexiDB::QuerySchema* queryschema); + virtual ~KexiDBQuerySchema(); + virtual const QString getClassName() const; + ::KexiDB::QuerySchema* queryschema(); + + private: + + /** Returns the SQL-statement of this query schema. */ + const QString statement() const; + /** Set the SQL-statement of this query schema. */ + void setStatement(const QString& statement); + /** Set the where-expression. */ + bool setWhereExpression(const QString& whereexpression); + + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/kexidbtransaction.cpp b/kexi/plugins/scripting/kexidb/kexidbtransaction.cpp new file mode 100644 index 00000000..d4cdff24 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbtransaction.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + * kexidbtransaction.cpp + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 "kexidbtransaction.h" +#include "kexidbconnection.h" +#include + +//#include + +using namespace Kross::KexiDB; + +KexiDBTransaction::KexiDBTransaction(::KexiDB::Transaction& transaction) + : Kross::Api::Class("KexiDBTransaction") + , m_transaction(transaction) +{ + this->addFunction0< Kross::Api::Variant >("isActive", this, &KexiDBTransaction::isActive); + this->addFunction0< Kross::Api::Variant >("isNull", this, &KexiDBTransaction::isNull); +} + +KexiDBTransaction::~KexiDBTransaction() +{ +} + +const QString KexiDBTransaction::getClassName() const +{ + return "Kross::KexiDB::KexiDBTransaction"; +} + +::KexiDB::Transaction& KexiDBTransaction::transaction() +{ + return m_transaction; +} + +bool KexiDBTransaction::isActive() const { return m_transaction.active(); } +bool KexiDBTransaction::isNull() const { return m_transaction.isNull(); } diff --git a/kexi/plugins/scripting/kexidb/kexidbtransaction.h b/kexi/plugins/scripting/kexidb/kexidbtransaction.h new file mode 100644 index 00000000..6a6b5785 --- /dev/null +++ b/kexi/plugins/scripting/kexidb/kexidbtransaction.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * kexidbtransaction.h + * This file is part of the KDE project + * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.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 KROSS_KEXIDB_KEXIDBTRANSACTION_H +#define KROSS_KEXIDB_KEXIDBTRANSACTION_H + +#include + +#include + +#include +#include + +namespace Kross { namespace KexiDB { + + // Forward declaration. + class KexiDBConnection; + + /** + * Transactions are used to ensure that integrity of a database is + * maintained. + */ + class KexiDBTransaction : public Kross::Api::Class + { + public: + KexiDBTransaction(::KexiDB::Transaction& transaction); + virtual ~KexiDBTransaction(); + virtual const QString getClassName() const; + ::KexiDB::Transaction& transaction(); + + private: + + /** Return true if the transaction is active (ie. started). */ + bool isActive() const; + + /** Return true if the transaction is uninitialized (null). */ + bool isNull() const; + + private: + ::KexiDB::Transaction& m_transaction; + }; + +}} + +#endif + diff --git a/kexi/plugins/scripting/kexidb/readme.dox b/kexi/plugins/scripting/kexidb/readme.dox new file mode 100644 index 00000000..c4e33a5b --- /dev/null +++ b/kexi/plugins/scripting/kexidb/readme.dox @@ -0,0 +1,32 @@ +/** @mainpage KrossKexiDB + * + * The Kross KexiDB module provides a scripting bridge to the + * KexiDB library. KexiDB is the database abstraction layer used + * within Kexi to deal with all supported database-backends like + * SQLite, MySQL and Postqre. + * + * The @a KexiDBDriverManager is the manager module which provides + * the entry point to access the KexiDB functionality from + * scripting languages like Python and Ruby. + * + * @see http://www.kexi-project.org/scripting/ + * @see http://kross.dipe.org + * @see http://www.kexi-project.org/wiki/wikiview/index.php?Scripting + * + * @section Legal + * + * @li copyright (C) 2004-2006 by Sebastian Sauer (mail AT dipe DOT 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. + */ diff --git a/kexi/plugins/scripting/kexiscripting/Makefile.am b/kexi/plugins/scripting/kexiscripting/Makefile.am new file mode 100644 index 00000000..ed3e2264 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/Makefile.am @@ -0,0 +1,37 @@ +include $(top_srcdir)/kexi/Makefile.global + +kde_module_LTLIBRARIES = kexihandler_script.la + +kexihandler_script_la_SOURCES = \ + kexiscriptpart.cpp kexiscripteditor.cpp kexiscriptdesignview.cpp + +kexihandler_script_la_LDFLAGS = \ + $(KDE_PLUGIN) -module -no-undefined -Wnounresolved $(all_libraries) $(VER_INFO) + +kexihandler_script_la_LIBADD = \ + $(top_builddir)/lib/kross/main/libkrossmain.la \ + $(top_builddir)/kexi/core/libkexicore.la \ + $(top_builddir)/kexi/widget/libkexiextendedwidgets.la \ + $(top_builddir)/lib/koproperty/libkoproperty.la + +INCLUDES = \ + $(KOFFICE_INCLUDES) \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/kexi/core \ + -I$(top_srcdir)/kexi \ + -I$(top_srcdir)/kexi/widget \ + $(all_includes) + +servicesdir=$(kde_servicesdir)/kexi +services_DATA=kexiscripthandler.desktop + +rcdir = $(kde_datadir)/kexi +rc_DATA = kexiscriptpartui.rc kexiscriptpartinstui.rc + +METASOURCES = AUTO + +SUBDIRS = . + +include ../../Makefile.common + +noinst_HEADERS = kexiscriptpart.h kexiscripteditor.h kexiscriptdesignview.h diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp new file mode 100644 index 00000000..ff2f93d0 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.cpp @@ -0,0 +1,337 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch + Copyright (C) 2004-2005 Jaroslaw Staniek + Copyright (C) 2005 Cedric Pasteur + Copyright (C) 2005 Sebastian Sauer + + 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 "kexiscriptdesignview.h" +#include "kexiscripteditor.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/// @internal +class KexiScriptDesignViewPrivate +{ + public: + + /** + * The \a Kross::Api::ScriptAction instance which provides + * us access to the scripting framework Kross. + */ + Kross::Api::ScriptAction* scriptaction; + + /// The \a KexiScriptEditor to edit the scripting code. + KexiScriptEditor* editor; + + /// The \a KoProperty::Set used in the propertyeditor. + KoProperty::Set* properties; + + /// Boolean flag to avoid infinite recursion. + bool updatesProperties; + + /// Used to display statusmessages. + KTextBrowser* statusbrowser; +}; + +KexiScriptDesignView::KexiScriptDesignView(KexiMainWindow *mainWin, QWidget *parent, Kross::Api::ScriptAction* scriptaction) + : KexiViewBase(mainWin, parent, "KexiScriptDesignView") + , d( new KexiScriptDesignViewPrivate() ) +{ + d->scriptaction = scriptaction; + d->updatesProperties = false; + + QSplitter* splitter = new QSplitter(this); + splitter->setOrientation(Vertical); + QHBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(splitter); + + d->editor = new KexiScriptEditor(mainWin, splitter, "ScriptEditor"); + splitter->setFocusProxy(d->editor); + addChildView(d->editor); + setViewWidget(d->editor); + + d->statusbrowser = new KTextBrowser(splitter, "ScriptStatusBrowser"); + d->statusbrowser->setReadOnly(true); + d->statusbrowser->setTextFormat(QTextBrowser::RichText); + //d->browser->setWordWrap(QTextEdit::WidgetWidth); + d->statusbrowser->installEventFilter(this); + splitter->setResizeMode(d->statusbrowser, QSplitter::KeepSize); + + plugSharedAction( "data_execute", this, SLOT(execute()) ); + if(KexiEditor::isAdvancedEditor()) // the configeditor is only in advanced mode avaiable. + plugSharedAction( "script_config_editor", d->editor, SLOT(slotConfigureEditor()) ); + + loadData(); + + d->properties = new KoProperty::Set(this, "KexiScripting"); + connect(d->properties, SIGNAL( propertyChanged(KoProperty::Set&, KoProperty::Property&) ), + this, SLOT( slotPropertyChanged(KoProperty::Set&, KoProperty::Property&) )); + + // To schedule the initialize fixes a crasher in Kate. + QTimer::singleShot(50, this, SLOT( initialize() )); +} + +KexiScriptDesignView::~KexiScriptDesignView() +{ + delete d->properties; + delete d; +} + +Kross::Api::ScriptAction* KexiScriptDesignView::scriptAction() const +{ + return d->scriptaction; +} + +void KexiScriptDesignView::initialize() +{ + updateProperties(); + d->editor->initialize( d->scriptaction ); +} + +void KexiScriptDesignView::updateProperties() +{ + if(d->updatesProperties) + return; + d->updatesProperties = true; + + Kross::Api::Manager* manager = Kross::Api::Manager::scriptManager(); + + QString interpretername = d->scriptaction->getInterpreterName(); + Kross::Api::InterpreterInfo* info = interpretername.isEmpty() ? 0 : manager->getInterpreterInfo(interpretername); + + { + // if interpreter isn't defined or invalid, try to fallback. + QStringList list; + list << "python" << "ruby"; + QStringList::ConstIterator it( list.constBegin() ), end( list.constEnd() ); + while( (! info) && (it != end) ) { + interpretername = (*it); + info = manager->getInterpreterInfo(interpretername); + if(info) + d->scriptaction->setInterpreterName(interpretername); + ++it; + } + } + + if(info) { + d->properties->clear(); + + QStringList interpreters = manager->getInterpreters(); + KoProperty::Property::ListData* proplist = new KoProperty::Property::ListData(interpreters, interpreters); + KoProperty::Property* prop = new KoProperty::Property( + "language", // name + proplist, // ListData + d->scriptaction->getInterpreterName(), // value + i18n("Interpreter"), // caption + i18n("The used scripting interpreter."), // description + KoProperty::List // type + ); + d->properties->addProperty(prop); + + Kross::Api::InterpreterInfo::Option::Map options = info->getOptions(); + Kross::Api::InterpreterInfo::Option::Map::ConstIterator it, end( options.constEnd() ); + for( it = options.constBegin(); it != end; ++it) { + Kross::Api::InterpreterInfo::Option* option = it.data(); + KoProperty::Property* prop = new KoProperty::Property( + it.key().latin1(), // name + d->scriptaction->getOption(it.key(), option->value), // value + option->name, // caption + option->comment, // description + KoProperty::Auto // type + ); + d->properties->addProperty(prop); + } + } + + //propertySetSwitched(); + propertySetReloaded(true); + d->updatesProperties = false; +} + +KoProperty::Set* KexiScriptDesignView::propertySet() +{ + return d->properties; +} + +void KexiScriptDesignView::slotPropertyChanged(KoProperty::Set& /*set*/, KoProperty::Property& property) +{ + if(property.isNull()) + return; + + if(property.name() == "language") { + QString language = property.value().toString(); + kdDebug() << QString("KexiScriptDesignView::slotPropertyChanged() language=%1").arg(language) << endl; + d->scriptaction->setInterpreterName( language ); + // We assume Kross and the HighlightingInterface are using same + // names for the support languages... + d->editor->setHighlightMode( language ); + updateProperties(); + } + else { + bool ok = d->scriptaction->setOption( property.name(), property.value() ); + if(! ok) { + kdWarning() << QString("KexiScriptDesignView::slotPropertyChanged() unknown property '%1'.").arg(property.name()) << endl; + return; + } + } + + setDirty(true); +} + +void KexiScriptDesignView::execute() +{ + d->statusbrowser->clear(); + QTime time; + time.start(); + d->statusbrowser->append( i18n("Execution of the script \"%1\" started.").arg(d->scriptaction->name()) ); + + d->scriptaction->activate(); + if( d->scriptaction->hadException() ) { + QString errormessage = d->scriptaction->getException()->getError(); + d->statusbrowser->append(QString("%2
").arg(QStyleSheet::escape(errormessage)) ); + + QString tracedetails = d->scriptaction->getException()->getTrace(); + d->statusbrowser->append( QStyleSheet::escape(tracedetails) ); + + long lineno = d->scriptaction->getException()->getLineNo(); + if(lineno >= 0) + d->editor->setLineNo(lineno); + } + else { + d->statusbrowser->append( i18n("Successfully executed. Time elapsed: %1ms").arg(time.elapsed()) ); + } +} + +bool KexiScriptDesignView::loadData() +{ + QString data; + if(! loadDataBlock(data)) { + kexipluginsdbg << "KexiScriptDesignView::loadData(): no DataBlock" << endl; + return false; + } + + QString errMsg; + int errLine; + int errCol; + + QDomDocument domdoc; + bool parsed = domdoc.setContent(data, false, &errMsg, &errLine, &errCol); + + if(! parsed) { + kexipluginsdbg << "KexiScriptDesignView::loadData() XML parsing error line: " << errLine << " col: " << errCol << " message: " << errMsg << endl; + return false; + } + + QDomElement scriptelem = domdoc.namedItem("script").toElement(); + if(scriptelem.isNull()) { + kexipluginsdbg << "KexiScriptDesignView::loadData(): script domelement is null" << endl; + return false; + } + + QString interpretername = scriptelem.attribute("language"); + Kross::Api::Manager* manager = Kross::Api::Manager::scriptManager(); + Kross::Api::InterpreterInfo* info = interpretername.isEmpty() ? 0 : manager->getInterpreterInfo(interpretername); + if(info) { + d->scriptaction->setInterpreterName(interpretername); + + Kross::Api::InterpreterInfo::Option::Map options = info->getOptions(); + Kross::Api::InterpreterInfo::Option::Map::ConstIterator it, end = options.constEnd(); + for( it = options.constBegin(); it != end; ++it) { + QString value = scriptelem.attribute( it.data()->name ); + if(! value.isNull()) { + QVariant v(value); + if( v.cast( it.data()->value.type() ) ) // preserve the QVariant's type + d->scriptaction->setOption(it.data()->name, v); + } + } + } + + d->scriptaction->setCode( scriptelem.text() ); + + return true; +} + +KexiDB::SchemaData* KexiScriptDesignView::storeNewData(const KexiDB::SchemaData& sdata, bool &cancel) +{ + KexiDB::SchemaData *s = KexiViewBase::storeNewData(sdata, cancel); + kexipluginsdbg << "KexiScriptDesignView::storeNewData(): new id:" << s->id() << endl; + + if(!s || cancel) { + delete s; + return 0; + } + + if(! storeData()) { + kdWarning() << "KexiScriptDesignView::storeNewData Failed to store the data." << endl; + //failure: remove object's schema data to avoid garbage + KexiDB::Connection *conn = parentDialog()->mainWin()->project()->dbConnection(); + conn->removeObject( s->id() ); + delete s; + return 0; + } + + return s; +} + +tristate KexiScriptDesignView::storeData(bool /*dontAsk*/) +{ + kexipluginsdbg << "KexiScriptDesignView::storeData(): " << parentDialog()->partItem()->name() << " [" << parentDialog()->id() << "]" << endl; + + QDomDocument domdoc("script"); + QDomElement scriptelem = domdoc.createElement("script"); + domdoc.appendChild(scriptelem); + + QString language = d->scriptaction->getInterpreterName(); + scriptelem.setAttribute("language", language); + + Kross::Api::InterpreterInfo* info = Kross::Api::Manager::scriptManager()->getInterpreterInfo(language); + if(info) { + Kross::Api::InterpreterInfo::Option::Map defoptions = info->getOptions(); + QMap& options = d->scriptaction->getOptions(); + QMap::ConstIterator it, end( options.constEnd() ); + for( it = options.constBegin(); it != end; ++it) { + if( defoptions.contains(it.key()) ) { // only remember options which the InterpreterInfo knows about... + scriptelem.setAttribute(it.key(), it.data().toString()); + } + } + } + + QDomText scriptcode = domdoc.createTextNode(d->scriptaction->getCode()); + scriptelem.appendChild(scriptcode); + + return storeDataBlock( domdoc.toString() ); +} + +#include "kexiscriptdesignview.moc" + diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h new file mode 100644 index 00000000..cee1ed76 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscriptdesignview.h @@ -0,0 +1,124 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch + Copyright (C) 2004-2005 Jaroslaw Staniek + Copyright (C) 2005 Cedric Pasteur + Copyright (C) 2005 Sebastian Sauer + + 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 KEXISCRIPTDESIGNVIEW_H +#define KEXISCRIPTDESIGNVIEW_H + +#include + +#include +#include + +// Forward declarations. +class KexiScriptContainer; +class KexiScriptEditor; +class KexiScriptDesignViewPrivate; + +namespace Kross { namespace Api { + class ScriptAction; +}} + +/** + * The KexiScriptDesignView class provides the \a KexiViewBase to + * manage script modules in the design-view. The design-view + * is used to be able to view and edit the scripting code via + * a \a KexiScriptEditor instance. + */ +class KexiScriptDesignView : public KexiViewBase +{ + Q_OBJECT + + public: + + /** + * Constructor. + */ + KexiScriptDesignView(KexiMainWindow *mainWin, QWidget *parent, Kross::Api::ScriptAction* scriptaction); + + /** + * Destructor. + */ + virtual ~KexiScriptDesignView(); + + /** + * \return the \a Kross::Api::ScriptAction this \a KexiScriptDesignView + * is responsible for. + */ + Kross::Api::ScriptAction* scriptAction() const; + + /** + * \return a property set for this view. + */ + virtual KoProperty::Set* propertySet(); + + /** + * Try to call \a storeData with new data we like to store. On + * success the matching \a KexiDB::SchemaData is returned. + * + * \param sdata The source \a KexiDB::SchemaData instance. + * \param cancel Cancel on failure and don't try to clean + * possible temporary created data up. + * \return The matching \a KexiDB::SchemaData instance or NULL + * if storing failed. + */ + virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel); + + /** + * Try to store the modified data in the already opened and + * currently used \a KexiDB::SchemaData instance. + */ + virtual tristate storeData(bool dontAsk = false); + + private slots: + + /** + * Deferred initialization. + */ + void initialize(); + + /** + * Handle changes in the property editor. + */ + void slotPropertyChanged(KoProperty::Set& set, KoProperty::Property& property); + + /** + * Update the \a KoProperty::Property::Dict propertymap of the + * interpreter-dependent options. + */ + void updateProperties(); + + /** + * Execute the scripting code. + */ + void execute(); + + private: + KexiScriptDesignViewPrivate* d; + + /** + * Load the data from XML source and fill the internally + * used \a Kross::Api::ScriptContainer instance. + */ + bool loadData(); +}; + +#endif diff --git a/kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp new file mode 100644 index 00000000..a638af36 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.cpp @@ -0,0 +1,104 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch + Copyright (C) 2004-2005 Jaroslaw Staniek + Copyright (C) 2005 Cedric Pasteur + Copyright (C) 2005 Sebastian Sauer + + 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 "kexiscripteditor.h" + +#include + +#include +//#include +//#include +//#include +//#include +#include + +#include + +/// \internal d-pointer class +class KexiScriptEditor::Private +{ + public: + Kross::Api::ScriptAction* scriptaction; + Private() : scriptaction(0) {} +}; + +KexiScriptEditor::KexiScriptEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name) + : KexiEditor(mainWin, parent, name) + , d( new Private() ) +{ +} + +KexiScriptEditor::~KexiScriptEditor() +{ + delete d; +} + +bool KexiScriptEditor::isInitialized() const +{ + return d->scriptaction != 0; +} + +void KexiScriptEditor::initialize(Kross::Api::ScriptAction* scriptaction) +{ + d->scriptaction = scriptaction; + Q_ASSERT(d->scriptaction); + + disconnect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged())); + + QString code = d->scriptaction->getCode(); + if(code.isNull()) { + // If there is no code we just add some information. +///@todo remove after release + code = "# " + QStringList::split("\n", i18n( + "This note will appear for a user in the script's source code " + "as a comment. Keep every row not longer than 60 characters and use '\n.'", + + "This is Technology Preview (BETA) version of scripting\n" + "support in Kexi. The scripting API may change in details\n" + "in the next Kexi version.\n" + "For more information and documentation see\n%1" + ).arg("http://www.kexi-project.org/scripting/"), true).join("\n# ") + "\n"; + } + KexiEditor::setText(code); + // We assume Kross and the HighlightingInterface are using same + // names for the support languages... + setHighlightMode(d->scriptaction->getInterpreterName()); + + clearUndoRedo(); + KexiEditor::setDirty(false); + connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged())); +} + +void KexiScriptEditor::slotTextChanged() +{ + KexiScriptEditor::setDirty(true); + if(d->scriptaction) + d->scriptaction->setCode( KexiEditor::text() ); +} + +void KexiScriptEditor::setLineNo(long lineno) +{ + setCursorPosition(lineno, 0); +} + +#include "kexiscripteditor.moc" + diff --git a/kexi/plugins/scripting/kexiscripting/kexiscripteditor.h b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.h new file mode 100644 index 00000000..1ef02ff9 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscripteditor.h @@ -0,0 +1,77 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch + Copyright (C) 2004-2005 Jaroslaw Staniek + Copyright (C) 2005 Cedric Pasteur + Copyright (C) 2005 Sebastian Sauer + + 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 KEXISCRIPTEDITOR_H +#define KEXISCRIPTEDITOR_H + +#include + +namespace Kross { namespace Api { + class ScriptAction; +}} + +/** + * The KexiEditor class embeds text editor + * for editing scripting code. + */ +class KexiScriptEditor : public KexiEditor +{ + Q_OBJECT + + public: + + /** + * Constructor. + */ + KexiScriptEditor(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0); + + /** + * Destructor. + */ + virtual ~KexiScriptEditor(); + + /** + * \returns true if this editor is already initialized (\a initialize was + * called) else false is returned. + */ + bool isInitialized() const; + + /** + * Initializes the editor. Call this if you like to start + * with a clear editor instance. Thinks like the language + * highlighter will be reset, undo/redo are cleared and + * setDirty(false) is set. + */ + void initialize(Kross::Api::ScriptAction* scriptaction); + + public slots: + void slotTextChanged(); + void setLineNo(long); + + private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; +}; + +#endif diff --git a/kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop b/kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop new file mode 100644 index 00000000..66e5f9fb --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscripthandler.desktop @@ -0,0 +1,105 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Kexi/Handler + +GenericName=Scripts +GenericName[bg]=Скриптове +GenericName[br]=Urzhiaouegoù +GenericName[ca]=Seqüències +GenericName[cs]=Skripty +GenericName[cy]=Sgriptiau +GenericName[da]=Scripter +GenericName[de]=Skripte +GenericName[el]=Σενάρια +GenericName[eo]=Skriptoj +GenericName[es]=Guiones +GenericName[et]=Skriptid +GenericName[eu]=Script-ak +GenericName[fa]=دست‌نوشته‌ها +GenericName[fi]=Skriptit +GenericName[fy]=Skripts +GenericName[ga]=Scripteanna +GenericName[gl]=Programas +GenericName[he]=תסריטים +GenericName[hr]=Skripte +GenericName[hu]=Szkriptek +GenericName[is]=Skriftur +GenericName[it]=Script +GenericName[ja]=スクリプト +GenericName[km]=ស្គ្រីប​ +GenericName[lt]=Scenarijai +GenericName[lv]=Skripti +GenericName[ms]=Skrip +GenericName[nb]=Skript +GenericName[nds]=Skripten +GenericName[ne]=स्क्रिप्टहरू +GenericName[nn]=Skript +GenericName[pl]=Skrypty +GenericName[pt]=Programas +GenericName[ru]=Сценарии +GenericName[se]=Skriptat +GenericName[sk]=Skripty +GenericName[sl]=Skripti +GenericName[sr]=Скрипте +GenericName[sr@Latn]=Skripte +GenericName[sv]=Skript +GenericName[uk]=Скрипти +GenericName[uz]=Skriptlar +GenericName[uz@cyrillic]=Скриптлар +GenericName[zh_CN]=脚本 +GenericName[zh_TW]=命令稿 +Name=Scripts +Name[bg]=Скриптове +Name[br]=Urzhiaouegoù +Name[ca]=Seqüències +Name[cs]=Skripty +Name[cy]=Sgriptiau +Name[da]=Scripter +Name[de]=Skripte +Name[el]=Σενάρια +Name[eo]=Skriptoj +Name[es]=Guiones +Name[et]=Skriptid +Name[eu]=Script-ak +Name[fa]=دست‌نوشته‌ها +Name[fi]=Skriptit +Name[fy]=Skripts +Name[ga]=Scripteanna +Name[gl]=Programas +Name[he]=תסריטים +Name[hr]=Skripte +Name[hu]=Szkriptek +Name[is]=Skriftur +Name[it]=Script +Name[ja]=スクリプト +Name[km]=ស្គ្រីប​ +Name[lt]=Scenarijai +Name[lv]=Skripti +Name[ms]=Skrip +Name[nb]=Skript +Name[nds]=Skripten +Name[ne]=स्क्रिप्टहरू +Name[nn]=Skript +Name[pl]=Skrypty +Name[pt]=Programas +Name[ru]=Сценарии +Name[se]=Skriptat +Name[sk]=Skripty +Name[sl]=Skripti +Name[sr]=Скрипте +Name[sr@Latn]=Skripte +Name[sv]=Skript +Name[uk]=Скрипти +Name[uz]=Skriptlar +Name[uz@cyrillic]=Скриптлар +Name[zh_CN]=脚本 +Name[zh_TW]=命令稿 +X-KDE-Library=kexihandler_script +X-KDE-ParentApp=kexi +X-Kexi-PartVersion=2 +X-Kexi-TypeName=script +X-Kexi-TypeMime=kexi/script +X-Kexi-ItemIcon=script +X-Kexi-SupportsExecution=true +X-Kexi-SupportsDataExport=false +X-Kexi-SupportsPrinting=false diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp new file mode 100644 index 00000000..d650e958 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.cpp @@ -0,0 +1,201 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch + Copyright (C) 2004 Cedric Pasteur + Copyright (C) 2005 Sebastian Sauer + + 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 "kexiscriptpart.h" +#include "kexiscriptdesignview.h" + +#include "kexiviewbase.h" +#include "keximainwindow.h" +#include "kexiproject.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/// \internal +class KexiScriptPart::Private +{ + public: + Kross::Api::ScriptGUIClient* scriptguiclient; +}; + +KexiScriptPart::KexiScriptPart(QObject *parent, const char *name, const QStringList &l) + : KexiPart::Part(parent, name, l) + , d( new Private() ) +{ + d->scriptguiclient = 0; + + // REGISTERED ID: + m_registeredPartID = (int)KexiPart::ScriptObjectType; + + 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.", + "script"); + m_names["instanceCaption"] = i18n("Script"); + m_supportedViewModes = Kexi::DesignViewMode; +} + +KexiScriptPart::~KexiScriptPart() +{ + delete d->scriptguiclient; + delete d; +} + +bool KexiScriptPart::execute(KexiPart::Item* item, QObject* sender) +{ + Q_UNUSED(sender); + + if(! item) { + kdWarning() << "KexiScriptPart::execute: Invalid item." << endl; + return false; + } + + KexiDialogBase* dialog = new KexiDialogBase(m_mainWin); + dialog->setId( item->identifier() ); + KexiScriptDesignView* view = dynamic_cast( createView(dialog, dialog, *item, Kexi::DesignViewMode) ); + if(! view) { + kdWarning() << "KexiScriptPart::execute: Failed to create a view." << endl; + return false; + } + + Kross::Api::ScriptAction* scriptaction = view->scriptAction(); + if(scriptaction) { + + const QString dontAskAgainName = "askExecuteScript"; + KConfig* config = KGlobal::config(); + QString dontask = config->readEntry(dontAskAgainName).lower(); + + bool exec = (dontask == "yes"); + if( !exec && dontask != "no" ) { + exec = KMessageBox::warningContinueCancel(0, + i18n("Do you want to execute the script \"%1\"?\n\nScripts obtained from unknown sources can contain dangerous code.").arg(scriptaction->text()), + i18n("Execute Script?"), KGuiItem(i18n("Execute"), "exec"), + dontAskAgainName, KMessageBox::Notify | KMessageBox::Dangerous + ) == KMessageBox::Continue; + } + + if(exec) { + //QTimer::singleShot(10, scriptaction, SLOT(activate())); + d->scriptguiclient->executeScriptAction( scriptaction ); + } + } + + view->deleteLater(); // not needed any longer. + return true; +} + +void KexiScriptPart::initPartActions() +{ + if(m_mainWin) { + // At this stage the KexiPart::Part::m_mainWin should be defined, so + // that we are able to use it's KXMLGUIClient. + + // Initialize the ScriptGUIClient. + d->scriptguiclient = new Kross::Api::ScriptGUIClient( m_mainWin ); + + // Publish the KexiMainWindow singelton instance. At least the KexiApp + // scripting-plugin depends on this instance and loading the plugin will + // fail if it's not avaiable. + if(! Kross::Api::Manager::scriptManager()->hasChild("KexiMainWindow")) { + Kross::Api::Manager::scriptManager()->addQObject(m_mainWin, "KexiMainWindow"); + + // Add the KAction's provided by the ScriptGUIClient to the + // KexiMainWindow. + //FIXME: fix+use createSharedPartAction() whyever it doesn't work as expected right now... + QPopupMenu* popup = m_mainWin->findPopupMenu("tools"); + if(popup) { + KAction* execscriptaction = d->scriptguiclient->action("executescriptfile"); + if(execscriptaction) + execscriptaction->plug( popup ); + KAction* configscriptaction = d->scriptguiclient->action("configurescripts"); + if(configscriptaction) + configscriptaction->plug( popup ); + KAction* scriptmenuaction = d->scriptguiclient->action("installedscripts"); + if(scriptmenuaction) + scriptmenuaction->plug( popup ); + /* + KAction* execscriptmenuaction = d->scriptguiclient->action("executedscripts"); + if(execscriptmenuaction) + execscriptmenuaction->plug( popup ); + KAction* loadedscriptmenuaction = d->scriptguiclient->action("loadedscripts"); + if(loadedscriptmenuaction) + loadedscriptmenuaction->plug( popup ); + */ + } + } + } +} + +void KexiScriptPart::initInstanceActions() +{ + //createSharedAction(Kexi::DesignViewMode, i18n("Execute Script"), "player_play", 0, "data_execute"); + createSharedAction(Kexi::DesignViewMode, i18n("Configure Editor..."), "configure", 0, "script_config_editor"); +} + +KexiViewBase* KexiScriptPart::createView(QWidget *parent, KexiDialogBase* dialog, KexiPart::Item& item, int viewMode, QMap*) +{ + QString partname = item.name(); + if( ! partname.isNull() ) { + KexiMainWindow *win = dialog->mainWin(); + if(!win || !win->project() || !win->project()->dbConnection()) + return 0; + + Kross::Api::ScriptActionCollection* collection = d->scriptguiclient->getActionCollection("projectscripts"); + if(! collection) { + collection = new Kross::Api::ScriptActionCollection( i18n("Scripts"), d->scriptguiclient->actionCollection(), "projectscripts" ); + d->scriptguiclient->addActionCollection("projectscripts", collection); + } + + const char* name = partname.latin1(); + Kross::Api::ScriptAction::Ptr scriptaction = collection->action(name); + if(! scriptaction) { + scriptaction = new Kross::Api::ScriptAction(partname); + collection->attach(scriptaction); //TODO remove again on unload! + } + + if(viewMode == Kexi::DesignViewMode) { + return new KexiScriptDesignView(win, parent, scriptaction); + } + } + return 0; +} + +QString KexiScriptPart::i18nMessage(const QCString& englishMessage) const +{ + if (englishMessage=="Design of object \"%1\" has been modified.") + return i18n("Design of script \"%1\" has been modified."); + if (englishMessage=="Object \"%1\" already exists.") + return i18n("Script \"%1\" already exists."); + return englishMessage; +} + +K_EXPORT_COMPONENT_FACTORY( kexihandler_script, KGenericFactory("kexihandler_script") ) + +#include "kexiscriptpart.moc" diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpart.h b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.h new file mode 100644 index 00000000..ddba0d72 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpart.h @@ -0,0 +1,100 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch + Copyright (C) 2004 Cedric Pasteur + Copyright (C) 2005 Sebastian Sauer + + 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 KEXISCRIPTPART_H +#define KEXISCRIPTPART_H + +#include +#include + +#include +#include +#include + +/** + * Kexi Scripting Plugin. + */ +class KexiScriptPart : public KexiPart::Part +{ + Q_OBJECT + + public: + + /** + * Constructor. + * + * \param parent The parent QObject this part is child of. + * \param name The name this part has. + * \param args Optional list of arguments passed to this part. + */ + KexiScriptPart(QObject *parent, const char *name, const QStringList& args); + + /** + * Destructor. + */ + virtual ~KexiScriptPart(); + + /** + * Implementation of the \a KexiPart::Part::execute method used to + * execute the passed \p item instance. + */ + virtual bool execute(KexiPart::Item* item, QObject* sender = 0); + + /** + * \return the i18n message for the passed \p englishMessage string. + */ + virtual QString i18nMessage(const QCString& englishMessage) const; + + protected: + + /** + * Create a new view. + * + * \param parent The parent QWidget the new view is displayed in. + * \param dialog The \a KexiDialogBase the view is child of. + * \param item The \a KexiPart::Item this view is for. + * \param viewMode The viewmode we like to have a view for. + */ + virtual KexiViewBase* createView(QWidget *parent, + KexiDialogBase* dialog, + KexiPart::Item& item, + int viewMode = Kexi::DesignViewMode, + QMap* staticObjectArgs = 0); + + /** + * Initialize the part's actions. + */ + virtual void initPartActions(); + + /** + * Initialize the instance actions. + */ + virtual void initInstanceActions(); + + private: + /// \internal d-pointer class. + class Private; + /// \internal d-pointer instance. + Private* const d; +}; + +#endif + diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc b/kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc new file mode 100644 index 00000000..16124c34 --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpartinstui.rc @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc b/kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc new file mode 100644 index 00000000..171d643f --- /dev/null +++ b/kexi/plugins/scripting/kexiscripting/kexiscriptpartui.rc @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/kexi/plugins/scripting/scripts/Makefile.am b/kexi/plugins/scripting/scripts/Makefile.am new file mode 100644 index 00000000..b63eedee --- /dev/null +++ b/kexi/plugins/scripting/scripts/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = exportxhtml importxhtml projectdocumentor copycenter python diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py new file mode 100644 index 00000000..3718512f --- /dev/null +++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.py @@ -0,0 +1,644 @@ +""" +Copy Center + +Description: +Python script to copy data between different datastores. + +Author: +Sebastian Sauer + +Copyright: +Dual-licensed under LGPL v2+higher and the BSD license. +""" + +class CopyCenter: + + class Plugin: + def __init__(self, plugin): + self.plugin = plugin + self.name = plugin.name + self.source = self.load("Source") + self.destination = self.load("Destination") + + def load(self, plugintype): + instance = None + try: + if hasattr(self.plugin, plugintype): + return getattr(self.plugin, plugintype)(self.plugin) + except: + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + return None + + def __init__(self, scriptpath): + self.scriptpath = scriptpath + self.homepath = self.getHomePath() + self.plugins = {} + + import os + import sys + if not os.path.exists(scriptpath): + print "The Path %s does not exist" % scriptpath + else: + import re + regexp = re.compile('^CopyCenterPlugin(.*)\\.py$') + for f in os.listdir(scriptpath): + file = os.path.join(scriptpath, f) + if not os.path.isfile(file): continue + m = regexp.match(f) + if not m: continue + print "Plugin name=%s file=%s" % (m.group(1),file) + mylocals = {} + try: + execfile(file, globals(), mylocals) + if mylocals.has_key("CopyCenterPlugin"): + plugin = mylocals.get("CopyCenterPlugin")(self) + self.plugins[plugin.name] = self.Plugin(plugin) + except: + print "Failed to import file=%s" % file + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + + def getHomePath(self): + """ Return the homedirectory. """ + import os + try: + home = os.getenv("HOME") + if not home: + import pwd + user = os.getenv("USER") or os.getenv("LOGNAME") + if not user: + pwent = pwd.getpwuid(os.getuid()) + else: + pwent = pwd.getpwnam(user) + home = pwent[6] + return home + except (KeyError, ImportError): + return os.curdir + +class Copierer: + def __init__(self): pass + def appendProgressMessage(self,messagetext): pass + def writeSuccess(self,record,rowcount): pass + def writeFailed(self,record): pass + +def runGuiApp(copycenter, name): + import qt + import sys + + #-------------------------------------------------------------------- + + class ListViewDialog(qt.QDialog): + def __init__(self, parent, caption): + qt.QDialog.__init__(self, parent, "ProgressDialog", 1) + self.parent = parent + self.setCaption(caption) + layout = qt.QVBoxLayout(self) + box = qt.QVBox(self) + box.setMargin(2) + layout.addWidget(box) + self.listview = qt.QListView(box) + self.listview.setAllColumnsShowFocus(True) + self.listview.header().setStretchEnabled(True,0) + btnbox = qt.QHBox(box) + btnbox.setMargin(6) + btnbox.setSpacing(6) + self.okbtn = qt.QPushButton(btnbox) + self.okbtn.setText("Ok") + #qt.QObject.connect(okbtn, qt.SIGNAL("clicked()"), self.okClicked) + self.cancelbtn = qt.QPushButton(btnbox) + self.cancelbtn.setText("Cancel") + qt.QObject.connect(self.cancelbtn, qt.SIGNAL("clicked()"), self.close) + box.setMinimumSize(qt.QSize(460,380)) + def addItem(self,valuelist,afteritem = None): + if afteritem == None: + item = qt.QListViewItem(self.listview) + else: + item = qt.QListViewItem(self.listview,afteritem) + i = 0 + for value in valuelist: + item.setText(i,value) + i += 1 + return item + + #-------------------------------------------------------------------- + + class CopyJobWidget(qt.QVBox): + def __init__(self,dialog,parent): + self.dialog = dialog + qt.QVBox.__init__(self,parent) + self.setSpacing(6) + typebox = qt.QHBox(self) + typebox.setSpacing(6) + label = qt.QLabel("Job File:",typebox) + self.jobfilecombobox = qt.QComboBox(typebox) + typebox.setStretchFactor(self.jobfilecombobox,1) + self.jobfilecombobox.setEditable(True) + self.jobfilecombobox.insertItem("") + label.setBuddy(self.jobfilecombobox) + qt.QObject.connect(self.jobfilecombobox, qt.SIGNAL("textChanged(const QString&)"), self.jobfilecomboboxChanged) + + import os + import re + for f in os.listdir(self.dialog.copycenter.homepath): + file = os.path.join(self.dialog.copycenter.homepath,f) + if os.path.isfile(file) and re.search(".+\\.copycenterjob.xml$",f): + self.jobfilecombobox.insertItem(file) + + loadbtn = qt.QPushButton(typebox) + loadbtn.setText("Open...") + qt.QObject.connect(loadbtn, qt.SIGNAL("clicked()"), self.openClicked) + savebtn = qt.QPushButton(typebox) + savebtn.setText("Save...") + qt.QObject.connect(savebtn, qt.SIGNAL("clicked()"), self.saveClicked) + + self.listview = qt.QListView(self) + self.listview.setAllColumnsShowFocus(True) + self.listview.setSorting(-1) + self.listview.setDefaultRenameAction(qt.QListView.Reject) + self.listview.header().setClickEnabled(False) + self.listview.addColumn("Name") + self.listview.addColumn("Value") + qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.doubleClicked) + #qt.QObject.connect(self.listview, qt.SIGNAL("itemRenamed(QListViewItem*, int, const QString&)"), self.itemRenamed) + + def doubleClicked(self, **args): + print "CopyJobWidget.doubleClicked" + item = self.listview.selectedItem() + if item and item.parent(): item.startRename(1) + + def readOptions(self,domnode,plugininst): + print "CopyJobWidget.readOptions plugintype=\"%s\"" % plugininst.plugintype + for node in domnode.childNodes: + if node.nodeType == node.ELEMENT_NODE: + v = node.getAttribute("value") + plugininst.options[node.nodeName] = v + print "Option \"%s\" has value \"%s\" now." % (node.nodeName, v) + + def jobfilecomboboxChanged(self, **args): + print "CopyJobWidget.jobfilecomboboxChanged" + import os + import xml.dom.minidom + filename = str(self.jobfilecombobox.currentText()) + if not os.path.isfile(filename): return + domdoc = xml.dom.minidom.parse(filename) + try: + elements = domdoc.getElementsByTagName("CopyCenterJob")[0] + sourcenode = elements.getElementsByTagName("Source")[0] + destinationnode = elements.getElementsByTagName("Destination")[0] + except: + raise "The XML-file \"%s\" does not contain a valid copy-job." % filename + + sourcepluginname = str(sourcenode.getAttribute('plugin')) + if not self.dialog.sourcedata.combobox.listBox().findItem(sourcepluginname,qt.Qt.ExactMatch): + raise "There exists no plugin with the name \"%s\"." % sourcepluginname + self.dialog.sourcedata.combobox.setCurrentText(sourcepluginname) + + destinationpluginname = str(destinationnode.getAttribute('plugin')) + if not self.dialog.destinationdata.combobox.listBox().findItem(destinationpluginname,qt.Qt.ExactMatch): + raise "There exists no plugin with the name \"%s\"." % destinationpluginname + self.dialog.destinationdata.combobox.setCurrentText(destinationpluginname) + + self.readOptions(sourcenode,self.dialog.getSourcePluginImpl()) + self.readOptions(destinationnode,self.dialog.getDestinationPluginImpl()) + self.maybeUpdate() + + def openClicked(self): + text = str(self.jobfilecombobox.currentText()) + if text == "": text = self.dialog.copycenter.homepath + filename = str(qt.QFileDialog.getOpenFileName(text,"*.copycenterjob.xml;;*",self.dialog)) + if filename != "": self.jobfilecombobox.setCurrentText(filename) + + def escape(self,s): + return s.replace("&", "&").replace("'", "'").replace("<", "<").replace(">", ">").replace('"', """) + + def writeOptions(self,writer,pluginname,plugininst): + print "CopyJobWidget.writeOptions" + writer.write("<%s plugin=\"%s\">\n" % (plugininst.plugintype, pluginname)) + for optionname in plugininst.options: + value = self.escape( unicode(plugininst.options[optionname]).encode("utf-8") ) + writer.write("\t<%s value=\"%s\" />\n" % (optionname,value)) + writer.write("\n" % plugininst.plugintype) + + def saveClicked(self): + text = str(self.jobfilecombobox.currentText()) + if text == "": + import os + text = os.path.join(self.dialog.copycenter.homepath,"default.copycenterjob.xml") + filename = str(qt.QFileDialog.getSaveFileName(text,"*.copycenterjob.xml;;*",self.dialog)) + if str(filename) == "": return + f = open(filename, "w") + f.write("\n") + f.write("\n") + sourcepluginname = self.dialog.sourcedata.combobox.currentText() + self.writeOptions(f, sourcepluginname, self.dialog.getSourcePluginImpl()) + destinationpluginname = self.dialog.destinationdata.combobox.currentText() + self.writeOptions(f, destinationpluginname, self.dialog.getDestinationPluginImpl()) + f.write("\n") + f.close() + print "File \%s\" successfully written." % filename + + def addItem(self, pluginimpl, afteritem = None, parentitem = None): + #print "CopyJobWidget.addItem" + class ListViewItem(qt.QListViewItem): + def __init__(self, pluginimpl, listview, parentitem = None, afteritem = None): + self.pluginimpl = pluginimpl + if parentitem == None: + qt.QListViewItem.__init__(self,listview) + self.setOpen(True) + else: + if afteritem == None: + qt.QListViewItem.__init__(self,parentitem) + else: + qt.QListViewItem.__init__(self,parentitem,afteritem) + self.setRenameEnabled(1,True) + def startRename(self, columnindex): + qt.QListViewItem.startRename(self,columnindex) + #lineedit = self.listView().viewport().child("qt_renamebox") + #if lineedit: + # regexp = qt.QRegExp("^[_A-Z]+[_A-Z0-9]*$", False) + # v = qt.QRegExpValidator(regexp, self.listView()); + # lineedit.setValidator(v) + def okRename(self, columnindex): + if columnindex == 1: + n = str(self.text(0)) + if not self.pluginimpl.options.has_key(n): + raise "No such option \"%s\"" % n + qt.QListViewItem.okRename(self,columnindex) + v = str(qt.QListViewItem.text(self,1)) + print "Option \"%s\" has value \"%s\" now." % (n,v) + self.pluginimpl.options[n] = v + + def text(self, columnindex): + if columnindex == 1: + if qt.QListViewItem.text(self,0).contains("password"): + return "*" * len(str(qt.QListViewItem.text(self,1))) + return qt.QListViewItem.text(self,columnindex) + return ListViewItem(pluginimpl, self.listview, parentitem, afteritem) + + def updateItem(self,pluginname,pluginimpl): + #print "CopyJobWidget.updateItem" + if pluginimpl == None: return + #plugin = self.dialog.plugins[pluginname] + item = self.addItem(pluginimpl) + item.setText(0,"%s: %s" % (pluginimpl.plugintype, pluginname)) + afteritem = None + for i in pluginimpl.options: + afteritem = self.addItem(pluginimpl, afteritem, item) + afteritem.setText(0,str(i)) + afteritem.setText(1,str(pluginimpl.options[i])) + print "CopyJobWidget.updateItem Added item with name \"%s\" and value \"%s\"" % (str(i),str(pluginimpl.options[i])) + pass + + def maybeUpdate(self): + print "CopyJobWidget.maybeUpdate" + self.listview.clear() + try: + self.updateItem(self.dialog.getDestinationPluginName(), self.dialog.getDestinationPluginImpl()) + self.updateItem(self.dialog.getSourcePluginName(), self.dialog.getSourcePluginImpl()) + except: + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + self.listview.clear() + + #-------------------------------------------------------------------- + + class ProgressDialog(qt.QDialog): + def __init__(self, dialog): + self.dialog = dialog + self.starttime = None + qt.QDialog.__init__(self, dialog, "ProgressDialog", 1) + self.setCaption("Copying...") + layout = qt.QVBoxLayout(self) + box = qt.QVBox(self) + box.setSpacing(6) + box.setMargin(6) + layout.addWidget(box) + self.textbrowser = qt.QTextBrowser(box) + self.textbrowser.setWordWrap(qt.QTextEdit.WidgetWidth) + self.textbrowser.setTextFormat(qt.Qt.RichText) + statusbox = qt.QFrame(box) + layout = qt.QGridLayout(statusbox,4,2,0,2) + layout.addWidget(qt.QLabel("Number of records done:",statusbox),0,0) + self.donecounter = 0 + self.donelabel = qt.QLabel("-",statusbox) + layout.addWidget(self.donelabel,0,1) + layout.addWidget(qt.QLabel("Successfully copied records:",statusbox),1,0) + self.successcounter = 0 + self.successlabel = qt.QLabel("-",statusbox) + layout.addWidget(self.successlabel,1,1) + layout.addWidget(qt.QLabel("Failed to copy records:",statusbox),2,0) + self.failedcounter = 0 + self.failedlabel = qt.QLabel("-",statusbox) + layout.addWidget(self.failedlabel,2,1) + layout.addWidget(qt.QLabel("Elapsed time in seconds:",statusbox),3,0) + self.elapsedlabel = qt.QLabel("-",statusbox) + layout.addWidget(self.elapsedlabel,3,1) + btnbox = qt.QHBox(box) + btnbox.setSpacing(6) + self.donebtn = qt.QPushButton(btnbox) + self.donebtn.setText("Done") + self.donebtn.setEnabled(False) + qt.QObject.connect(self.donebtn,qt.SIGNAL("clicked()"),self.close) + self.cancelbtn = qt.QPushButton(btnbox) + self.cancelbtn.setText("Cancel") + qt.QObject.connect(self.cancelbtn,qt.SIGNAL("clicked()"),self.close) + box.setMinimumSize( qt.QSize(500,380) ) + + def updateStates(self): + if self.starttime != None: + self.donelabel.setText(str(self.donecounter)) + self.failedlabel.setText(str(self.failedcounter)) + self.successlabel.setText(str(self.successcounter)) + self.elapsedlabel.setText( str(self.starttime.elapsed() / 1000) ) + self.donelabel.update() + self.failedlabel.update() + self.successlabel.update() + self.elapsedlabel.update() + + def writeSuccess(self, record, rowcount): + self.donecounter += rowcount + self.successcounter += rowcount + qt.qApp.processEvents() + def writeFailed(self, record): + self.donecounter += 1 + self.failedcounter += 1 + qt.qApp.processEvents() + + def startCopy(self): + try: + global Copierer + copierer = Copierer() + copierer.appendProgressMessage = self.textbrowser.append + copierer.writeSuccess = self.writeSuccess + copierer.writeFailed = self.writeFailed + + self.starttime = qt.QTime() + self.updatetimer = qt.QTimer(self) + qt.QObject.connect(self.updatetimer,qt.SIGNAL("timeout()"),self.updateStates) + + # Initialize the source + sourcename = self.dialog.getSourcePluginName() + sourceimpl = self.dialog.getSourcePluginImpl() + self.textbrowser.append("Source: %s" % sourcename) + if sourceimpl == None: + raise "No such source." + try: + sourceimpl.init(copierer) + + # Initialize the destination + destinationname = self.dialog.getDestinationPluginName() + destinationimpl = self.dialog.getDestinationPluginImpl() + self.textbrowser.append("
Destination: %s" % destinationname) + if destinationimpl == None: + raise "No such destination." + try: + destinationimpl.init(copierer) + + self.starttime.start() + self.updatetimer.start(500) + qt.qApp.processEvents() + + # Copy the records + self.textbrowser.append("
Copy the records...") + while True: + record = sourceimpl.read() + if record == None: break + destinationimpl.write(record) + + self.updateStates() + finally: + destinationimpl.finish() + finally: + sourceimpl.finish() + + self.setCaption("Copy done") + self.textbrowser.append("
Copy done.") + except: + self.setCaption("Copy failed") + self.textbrowser.append("Error: %s" % sys.exc_info()[0]) + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + #self.progressbar.setEnabled(False) + self.donebtn.setEnabled(True) + self.cancelbtn.setEnabled(False) + self.updatetimer.stop() + self.starttime = None + + def show(self): + qt.QDialog.show(self) + qt.QTimer.singleShot(10,self.startCopy) + qt.qApp.processEvents() + + def closeEvent(self, closeevent): + if not self.dialog.getSourcePluginImpl().isFinished(): + if qt.QMessageBox.warning(self,"Abort?","Abort the copy?",qt.QMessageBox.Yes,qt.QMessageBox.No) != qt.QMessageBox.Yes: + closeevent.ignore() + return + self.dialog.getSourcePluginImpl().finish() + self.dialog.getDestinationPluginImpl().finish() + closeevent.accept() + + #-------------------------------------------------------------------- + + class DataSelector(qt.QVGroupBox): + def __init__(self, plugintype, title, caption, parent, dialog, items): + self.plugintype = plugintype + self.pluginimpl = None + self.dialog = dialog + self.mainbox = None + + qt.QVGroupBox.__init__(self,title,parent) + self.setInsideMargin(6) + self.setInsideSpacing(0) + + typebox = qt.QHBox(self) + label = qt.QLabel(caption,typebox) + self.combobox = qt.QComboBox(typebox) + for item in items: + self.combobox.insertItem(str(item)) + label.setBuddy(self.combobox) + typebox.setStretchFactor(self.combobox,1) + + self.scrollview = qt.QScrollView(self) + try: + self.scrollview.setResizePolicy(qt.QScrollView.AutoOne) + self.scrollview.setFrameStyle(qt.QFrame.NoFrame); + self.scrollview.setResizePolicy(qt.QScrollView.AutoOneFit); + self.scrollview.viewport().setPaletteBackgroundColor(self.paletteBackgroundColor()) + except: + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + qt.QObject.connect(self.combobox, qt.SIGNAL("activated(int)"), self.activated) + + def updatePlugin(self): + print "DataSelector.updatePlugin" + self.pluginimpl = None + text = str(self.combobox.currentText()) + plugin = self.dialog.copycenter.plugins[text] + self.pluginimpl = getattr(plugin, self.plugintype) + + def removeMainBox(self): + if self.mainbox == None: return + try: + self.scrollview.removeChild(self.mainbox) + self.mainbox.destroy() + except: + pass + self.mainbox = None + + def updateMainBox(self): + print "DataSelector.updateMainBox" + self.removeMainBox() + self.mainbox = qt.QVBox( self.scrollview.viewport() ) + self.mainbox.setSpacing(2) + if self.pluginimpl != None: + try: + self.pluginimpl.createWidget(self.dialog, self.mainbox) + except: + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + self.mainbox.setStretchFactor(qt.QWidget(self.mainbox), 1) + self.mainbox.show() + self.scrollview.addChild(self.mainbox) + + def activated(self, **args): + self.updatePlugin() + self.updateMainBox() + + def maybeUpdate(self): + print "DataSelector.maybeUpdate" + self.removeMainBox() + qt.QTimer.singleShot(50, self.activated) + + def maybeDone(self): + print "DataSelector.maybeDone" + if self.pluginimpl.widget == None: return + for optionname in self.pluginimpl.options: + self.pluginimpl.options[optionname] = self.pluginimpl.widget.getOptionValue(optionname) + + #-------------------------------------------------------------------- + + class Dialog(qt.QDialog): + def __init__(self, copycenter, parent): + self.copycenter = copycenter + + import qt + import os + import sys + + self.ListViewDialog = ListViewDialog + qt.QDialog.__init__(self, parent, "Dialog", 1, qt.Qt.WDestructiveClose) + self.setCaption("Copy Center") + layout = qt.QVBoxLayout(self) + box = qt.QVBox(self) + box.setMargin(6) + box.setSpacing(6) + layout.addWidget(box) + self.tab = qt.QTabWidget(box) + self.tab.setMargin(6) + box.setStretchFactor(self.tab,1) + + self.jobsbox = CopyJobWidget(self,self.tab) + self.tab.addTab(self.jobsbox,"Jobs") + + self.splitter = qt.QSplitter(self.tab) + + sourceplugins = [] + destinationplugins = [] + for pluginname in self.copycenter.plugins: + if self.copycenter.plugins[pluginname].source != None: + sourceplugins.append(pluginname) + if self.copycenter.plugins[pluginname].destination != None: + destinationplugins.append(pluginname) + sourceplugins.sort() + destinationplugins.sort() + + self.sourcedata = DataSelector( + "source", # id + "Read Data From", # title + "Source:", # caption + self.splitter, self, sourceplugins) + self.destinationdata = DataSelector( + "destination", # id + "Write Data to", # title + "Destination:", # caption + self.splitter, self, destinationplugins) + + btnbox = qt.QHBox(box) + btnbox.setSpacing(6) + okbtn = qt.QPushButton(btnbox) + okbtn.setText("Start Copy") + okbtn.setDefault(True) + qt.QObject.connect(okbtn,qt.SIGNAL("clicked()"),self.startCopy) + cancelbtn = qt.QPushButton(btnbox) + cancelbtn.setText("Cancel") + qt.QObject.connect(cancelbtn,qt.SIGNAL("clicked()"),self.close) + + self.tab.addTab(self.splitter,"Copy") + self.tab.setCurrentPage(1) + + self.helpbrowser = qt.QTextBrowser(self.tab) + self.helpbrowser.setLinkUnderline(False) + self.helpbrowser.setUndoRedoEnabled(False) + self.tab.addTab(self.helpbrowser,"Help") + qt.QObject.connect(self.tab,qt.SIGNAL("currentChanged(QWidget*)"),self.currentTabChanged) + + box.setMinimumSize( qt.QSize(760,500) ) + + defaultfile = os.path.join(self.copycenter.homepath,"default.copycenterjob.xml") + if os.path.isfile(defaultfile): + print "Reading default copy job file: %s" % defaultfile + self.jobsbox.jobfilecombobox.setCurrentText(defaultfile) + + def getSourcePluginName(self): + return str(self.sourcedata.combobox.currentText()) + def getSourcePluginImpl(self): + return self.copycenter.plugins[self.getSourcePluginName()].source + def getDestinationPluginName(self): + return str(self.destinationdata.combobox.currentText()) + def getDestinationPluginImpl(self): + return self.copycenter.plugins[self.getDestinationPluginName()].destination + + def currentTabChanged(self,widget): + if self.tab.currentPage() == self.jobsbox: + # The "Copy" page is done + self.sourcedata.maybeDone() + self.destinationdata.maybeDone() + # Update the "Jobs" page + self.jobsbox.maybeUpdate() + elif self.tab.currentPage() == self.splitter: + # Update the "Copy" page + self.sourcedata.maybeUpdate() + self.destinationdata.maybeUpdate() + elif self.tab.currentPage() == self.helpbrowser and self.helpbrowser.lines() <= 1: + # Update the "Help" page + import os + file = os.path.join(self.copycenter.scriptpath, "readme.html") + if not os.path.isfile(file): return + fh = open(file,'r') + self.helpbrowser.setText( fh.read() ) + fh.close() + + def startCopy(self): + dlg = ProgressDialog(self) + dlg.show() + + #-------------------------------------------------------------------- + + if name == "__main__": + qtapp = qt.QApplication(sys.argv) + else: + qtapp = qt.qApp + dialog = Dialog(copycenter, qtapp.mainWidget()) + dialog.exec_loop() + +import os + +if __name__ == "__main__": + scriptpath = os.getcwd() +else: + scriptpath = os.path.dirname(__name__) + +copycenter = CopyCenter(scriptpath) +runGuiApp(copycenter, __name__) diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc new file mode 100644 index 00000000..e3e758b4 --- /dev/null +++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenter.rc @@ -0,0 +1,10 @@ + + + diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py new file mode 100644 index 00000000..e8241405 --- /dev/null +++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginKexiDB.py @@ -0,0 +1,646 @@ +""" +CopyCenterPlugin to provide 'KexiDB'. + +Description: +This python-script is a plugin for the CopyCenter.py. + +Author: +Sebastian Sauer + +Copyright: +GPL v2 or higher. +""" + +class CopyCenterPlugin: + """ The CopyCenterPlugin to provide abstract access to the 'KexiDB' + framework to CopyCenter.py """ + + name = "Kexi Database" + """ The name this plugin has. The name should be unique and + will be used for displaying a caption. """ + + class Plugin: + """ The implementation of a plugin which is published to the + CopyCenter.py script. While there exists only one instance of + the CopyCenterPlugin class, there will be n instances of this + Plugin class (one for 'source' aka read-data-from and one for + 'destination' aka write-data-to) created from within the + CopyCenter.py. The Source and Destination classes are extending + this Plugin class with specialized functionality. """ + def __init__(self,copycenterplugin): + """ Constructor. """ + self.copycenterplugin = copycenterplugin + self.widget = None + self.options = { + 'autoconnect' : False, + 'project' : '', #'~/test.kexi', + 'driver' : '', #'MySQL', #'SQLite3','MySQL',... + 'file' : '', #'/path/to/mysqlite3dbfile.kexi' + 'hostname' : '127.0.0.1', + 'port' : '', + 'usesocketfile' : False, + 'socketfile' : '', + 'username' : '', + 'password' : '', + 'database' : '', + 'table' : '', + 'fields' : '', + 'where' : '', + } + self.connection = copycenterplugin.Connection(self) + def isFinished(self): + return self.connection.isFinished() + def finish(self): + """ Called if reading is finished.""" + self.connection.finish() + def createWidget(self, dialog, parent): + """ Create and return a widget to modify the plugin settings. """ + return self.copycenterplugin.createWidget(dialog, self, parent) + + class Source(Plugin): + """ Specialization of the Plugin class to implement the + 'source' aka read-data-from functionality. """ + plugintype = "Source" + def init(self,copierer): + """ Called if reading should be initialize. """ + self.connection.init(copierer) + self.connection.initRead() + # Called if a record should be readed. + self.read = self.connection.readRecord + + class Destination(Plugin): + """ Specialization of the Plugin class to implement the + 'destination' aka write-data-to functionality. """ + plugintype = "Destination" + def init(self,copierer): + """ Called if writing should be initialize. """ + self.connection.init(copierer) + self.connection.initWrite() + # Called if a record should be written. + self.write = self.connection.writeRecord + + class Connection: + """ Abstract access to work with KexiDB. """ + def __init__(self,plugin): + self.plugin = plugin + self.copierer = None + self.kexidbconnection = None + self.kexidbcursor = None + self.tableschema = None + self.fieldlist = None + def lastError(self): return self.kexidbconnection.lastError() + def connect(self): return self.kexidbconnection.connect() + def disconnect(self): + if self.kexidbconnection == None or self.kexidbconnection.disconnect(): + self.kexidbconnection = None + return True + return False + def isConnected(self): return self.kexidbconnection != None and self.kexidbconnection.isConnected() + def tableNames(self): return self.kexidbconnection.tableNames() + def hasTableName(self,tablename): return tablename in self.kexidbconnection.tableNames() + def tableSchema(self,tablename): return self.kexidbconnection.tableSchema(tablename) + + def init(self,copierer): + self.copierer = copierer + if self.kexidbconnection == None: + if self.plugin.widget == None: + raise "No connection established." + self.copierer.appendProgressMessage("Trying to connect...") + if self.plugin.widget.driverbox.driver == None: + raise "Invalid driver." + if not self.plugin.widget.connectClicked(): + raise "Failed to connect." + connectiondata = self.kexidbconnection.data() + self.copierer.appendProgressMessage("Connected: %s %s" % (connectiondata.driverName(),connectiondata.serverInfoString())) + + tablename = str(self.plugin.widget.tablebox.tableedit.text()) + if tablename == "": + raise "No table defined" + fields = [ f.strip() for f in str(self.plugin.widget.fieldbox.fieldsedit.text()).split(",") if len(f) > 0 ] + if len(fields) < 1: + raise "No fields defined" + + self.tableschema = self.kexidbconnection.tableSchema(tablename) + if not self.tableschema: raise "No such tableschema \"%s\"" % tablename + self.copierer.appendProgressMessage("Table: %s" % self.tableschema.name()) + + if len(fields) == 1 and fields[0] == "*": + self.fieldlist = self.tableschema.fieldlist() + else: + self.fieldlist = self.tableschema.fieldlist().subList(fields) + if not self.fieldlist: raise "No such fields \"%s\"" % fields + fieldlistnames = self.fieldlist.names() + if len(fieldlistnames) < 1: raise "No valid fields defined for \"%s\"" % fields + self.copierer.appendProgressMessage("Fields: %s" % fieldlistnames) + def finish(self): + if self.plugin.widget == None: + self.disconnect() + else: + self.plugin.widget.disconnectClicked() + self.kexidbcursor = None + self.kexidbconnection = None + self.tableschema = None + self.fieldlist = None + self.copierer = None + def isFinished(self): + return self.copierer == None + + def initRead(self): + print "Initialize read" + #queryschema = self.plugin.copycenterplugin.drivermanager.querySchema() + queryschema = self.tableschema.query() + queryschema.fieldlist().setFields(self.fieldlist) + print "QuerySchema: %s" % queryschema.fieldlist().names() + + whereexpression = str(self.plugin.widget.whereedit.text()) + if whereexpression != "": + print "WHERE-expression: %s" % whereexpression + if not queryschema.setWhereExpression(whereexpression): + raise "Invalid WHERE-expression." + + #print "QuerySchema statement=%s" % queryschema.statement() + self.kexidbcursor = self.kexidbconnection.executeQuerySchema(queryschema) + if not self.kexidbcursor: + raise "Failed to create cursor." + if not self.kexidbcursor.moveFirst(): + raise "The cursor has no records to read from." + + def readRecord(self): + if self.kexidbcursor == None or self.kexidbcursor.eof(): + return None + record = [] + for i in range( self.kexidbcursor.fieldCount() ): + record.append( self.kexidbcursor.value(i) ) + self.kexidbcursor.moveNext() + #print "read record: %s" % record + return record + + def initWrite(self): + print "Initialize write" + + def writeRecord(self,record): + print "write record: %s" % record + if self.kexidbconnection.insertRecord(self.fieldlist,record): + print "=> insert successfully" + self.copierer.writeSuccess(record, 1) + else: + print "=> insert failed: %s" % self.kexidbconnection.lastError() + self.copierer.writeFailed(record) + #import time + #time.sleep(1) + return True + + def __init__(self, copycenter): + """ Constructor. """ + import krosskexidb + self.drivermanager = krosskexidb.DriverManager() + self.copycenter = copycenter + + def createWidget(self, dialog, plugin, parent): + """ Each plugin may provide a qt.QWidget back to the + CopyCenter.py. The widget will be used to configure our + plugin settings. """ + + import qt + import os + import re + + self.dialog = dialog + self.mainbox = None + class ProjectBox(qt.QHBox): + def __init__(self,main,copycenterplugin,plugin,parent): + self.main = main + self.copycenterplugin = copycenterplugin + self.plugin = plugin + + qt.QHBox.__init__(self,parent) + prjlabel = qt.QLabel("Project File:",self) + self.prjcombo = qt.QComboBox(self) + self.prjcombo.setEditable(True) + self.prjcombo.insertItem("") + + path = copycenterplugin.copycenter.homepath + for f in os.listdir(path): + file = os.path.join(path,f) + if os.path.isfile(file) and re.search(".+\\.(kexi|kexis|kexic)$",f): + self.prjcombo.insertItem(os.path.join("~",f)) + + prjlabel.setBuddy(self.prjcombo) + prjsavebtn = qt.QPushButton("...",self) + qt.QObject.connect(prjsavebtn, qt.SIGNAL("clicked()"),self.buttonClicked) + qt.QObject.connect(self.prjcombo, qt.SIGNAL("textChanged(const QString&)"), self.main.projectChanged) + self.setStretchFactor(self.prjcombo,1) + def buttonClicked(self): + text = str(self.prjcombo.currentText()) + if text == "": + text = self.copycenterplugin.copycenter.homepath + elif re.search("^\\~(\\/|\\\\)",text): + import os + text = os.path.join(self.copycenterplugin.copycenter.homepath,text[2:]) + if self.plugin.plugintype == "Source": + filename = qt.QFileDialog.getOpenFileName(text,"*.kexi *.kexis *.kexic;;*",self.copycenterplugin.dialog) + else: # "Destination": + filename = qt.QFileDialog.getSaveFileName(text,"*.kexi *.kexis *.kexic;;*",self.copycenterplugin.dialog) + if str(filename) != "": self.prjcombo.setCurrentText(str(filename)) + + class DriverBox(qt.QVBox): + def __init__(self,main,parent): + qt.QVBox.__init__(self,parent) + self.main = main + self.copycenterplugin = main.copycenterplugin + self.plugin = main.plugin + self.driver = None + + driverbox = qt.QHBox(self) + driverlabel = qt.QLabel("Driver:",driverbox) + self.drivercombo = qt.QComboBox(driverbox) + self.drivercombo.insertItem("") + for driver in self.copycenterplugin.drivermanager.driverNames(): + self.drivercombo.insertItem(driver) + + qt.QObject.connect(self.drivercombo, qt.SIGNAL("activated(int)"), self.activated) + driverlabel.setBuddy(self.drivercombo) + driverbox.setStretchFactor(self.drivercombo,1) + + self.box = qt.QVBox(self) + self.mainbox = None + + def activated(self,index): + drivertext = str(self.drivercombo.currentText()) + + self.box.hide() + if self.mainbox: + self.mainbox.hide() + self.mainbox.destroy() + self.mainbox = None + if index == 0 or drivertext == "": + self.driver = None + self.box.show() + self.main.updateConnectButtons() + return False + + self.driver = self.copycenterplugin.drivermanager.driver(drivertext) + + mainbox = qt.QVBox(self.box) + mainbox.setSpacing(2) + + if self.driver.isFileDriver(): + filebox = qt.QHBox(mainbox) + filelabel = qt.QLabel("File:",filebox) + self.fileedit = qt.QLineEdit(self.plugin.options['file'],filebox) + filelabel.setBuddy(self.fileedit) + filebox.setStretchFactor(self.fileedit,1) + filebtn = qt.QPushButton("...",filebox) + qt.QObject.connect(filebtn, qt.SIGNAL("clicked()"), self.fileClicked) + else: + hostbox = qt.QHBox(mainbox) + hostlabel = qt.QLabel("Hostname:",hostbox) + self.hostedit = qt.QLineEdit(self.plugin.options['hostname'],hostbox) + hostlabel.setBuddy(self.hostedit) + hostbox.setStretchFactor(self.hostedit,1) + + portbox = qt.QHBox(mainbox) + portlabel = qt.QLabel("Port:",portbox) + self.portedit = qt.QLineEdit(self.plugin.options['port'],portbox) + portlabel.setBuddy(self.portedit) + portbox.setStretchFactor(self.portedit,1) + + sockbox = qt.QHBox(mainbox) + self.sockfilecheckbox = qt.QCheckBox("Socket File:",sockbox) + qt.QObject.connect(self.sockfilecheckbox, qt.SIGNAL("toggled(bool)"), self.sockfilecheckboxClicked) + self.sockfilebox = qt.QHBox(sockbox) + self.sockfileedit = qt.QLineEdit(self.plugin.options['socketfile'],self.sockfilebox) + self.sockfilebox.setEnabled(False) + sockfilebtn = qt.QPushButton("...",self.sockfilebox) + self.sockfilecheckbox.setChecked( str(self.plugin.options['usesocketfile']) == str(True) ) + qt.QObject.connect(sockfilebtn, qt.SIGNAL("clicked()"), self.sockfileClicked) + self.sockfilebox.setStretchFactor(self.sockfileedit,1) + sockbox.setStretchFactor(self.sockfilebox,1) + + userbox = qt.QHBox(mainbox) + userlabel = qt.QLabel("Username:",userbox) + self.useredit = qt.QLineEdit(self.plugin.options['username'],userbox) + userlabel.setBuddy(self.useredit) + userbox.setStretchFactor(self.useredit,1) + + passbox = qt.QHBox(mainbox) + passlabel = qt.QLabel("Password:",passbox) + self.passedit = qt.QLineEdit(self.plugin.options['password'],passbox) + self.passedit.setEchoMode(qt.QLineEdit.Password) + passlabel.setBuddy(self.passedit) + passbox.setStretchFactor(self.passedit,1) + + dbbox = qt.QHBox(mainbox) + dblabel = qt.QLabel("Database:",dbbox) + self.dbedit = qt.QLineEdit(self.plugin.options['database'],dbbox) + dblabel.setBuddy(self.dbedit) + dbbox.setStretchFactor(self.dbedit,1) + #self.tablecombo.setText("") + + self.mainbox = mainbox + self.mainbox.show() + self.box.show() + self.main.updateConnectButtons() + return True + + def fileClicked(self): + text = str(self.fileedit.text()) + if text == "": text = self.copycenterplugin.copycenter.homepath + if self.plugin.plugintype == "Source": + filename = qt.QFileDialog.getOpenFileName(text,"*",self.copycenterplugin.dialog) + else: # "Destination": + filename = qt.QFileDialog.getSaveFileName(text,"*",self.copycenterplugin.dialog) + if str(filename) != "": self.fileedit.setText(str(filename)) + def sockfilecheckboxClicked(self,checked): + self.sockfilebox.setEnabled(checked) + + def sockfileClicked(self): + text = str(self.sockfileedit.text()) + if text == "": text = self.copycenterplugin.copycenter.homepath + if self.plugin.plugintype == "Source": + filename = qt.QFileDialog.getOpenFileName(text,"*",self.copycenterplugin.dialog) + else: # "Destination": + filename = qt.QFileDialog.getSaveFileName(text,"*",self.copycenterplugin.dialog) + if str(filename) != "": self.sockfileedit.setText(str(filename)) + + class TableBox(qt.QHBox): + def __init__(self,copycenterplugin,plugin,parent): + qt.QHBox.__init__(self,parent) + self.copycenterplugin = copycenterplugin + self.plugin = plugin + tablelabel = qt.QLabel("Table:",self) + self.tableedit = qt.QLineEdit(self.plugin.options['table'],self) + self.tablebtn = qt.QPushButton("...",self) + self.tablebtn.setEnabled(False) + qt.QObject.connect(self.tablebtn, qt.SIGNAL("clicked()"), self.buttonClicked) + tablelabel.setBuddy(self.tableedit) + self.setStretchFactor(self.tableedit,1) + def buttonClicked(self): + ListViewDialog = self.copycenterplugin.dialog.ListViewDialog + class TableDialog(ListViewDialog): + def __init__(self,tablebox): + ListViewDialog.__init__(self,tablebox,"Tables") + self.mainwidget = tablebox + self.listview.addColumn("Name") + text = str(self.mainwidget.tableedit.text()) + item = None + for table in self.mainwidget.plugin.connection.tableNames(): + if item == None: + item = qt.QListViewItem(self.listview,table) + else: + item = qt.QListViewItem(self.listview,item,table) + if table == text: + self.listview.setSelected(item,True) + self.listview.ensureItemVisible(item) + qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.okClicked) + qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked) + def okClicked(self): + item = self.listview.selectedItem() + if item == None: + self.mainwidget.tableedit.setText("") + else: + self.mainwidget.tableedit.setText(item.text(0)) + self.close() + dialog = TableDialog(self) + dialog.show() + + class FieldBox(qt.QHBox): + def __init__(self,copycenterplugin,plugin,parent): + qt.QHBox.__init__(self,parent) + self.copycenterplugin = copycenterplugin + self.plugin = plugin + self.tablename = "" + fieldslabel = qt.QLabel("Fields:",self) + self.fieldsedit = qt.QLineEdit(self.plugin.options['fields'],self) + self.setStretchFactor(self.fieldsedit,1) + fieldslabel.setBuddy(self.fieldsedit) + self.fieldsbtn = qt.QPushButton("...",self) + self.fieldsbtn.setEnabled(False) + qt.QObject.connect(self.fieldsbtn, qt.SIGNAL("clicked()"), self.fieldsClicked) + def fieldsClicked(self): + ListViewDialog = self.copycenterplugin.dialog.ListViewDialog + class FieldsDialog(ListViewDialog): + def __init__(self, fieldbox): + ListViewDialog.__init__(self,fieldbox,"Fields") + self.fieldbox = fieldbox + self.listview.setSelectionMode(qt.QListView.Multi) + self.listview.setSorting(-1) + self.listview.header().setClickEnabled(False) + self.listview.addColumn("Name") + self.listview.addColumn("Type") + self.listview.addColumn("Options") + fieldslist = str(self.fieldbox.fieldsedit.text()).split(",") + allfields = ("*" in fieldslist) + tableschema = self.fieldbox.plugin.connection.tableSchema(self.fieldbox.tablename) + item = None + for field in tableschema.fieldlist().fields(): + opts = [] + for opt in ("isAutoInc","isNotNull","isNotEmpty"): + if getattr(field,opt)(): + opts.append(opt[2:]) + item = self.addItem(( field.name(),field.type(),",".join(opts) ),item) + if allfields or field.name() in fieldslist: + self.listview.setSelected(item,True) + qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked) + def okClicked(self): + selitems = [] + item = self.listview.firstChild() + while item: + if item.isSelected(): + selitems.append(str(item.text(0))) + item = item.nextSibling() + self.fieldbox.fieldsedit.setText(",".join(selitems)) + self.close() + dialog = FieldsDialog(self) + dialog.show() + def tableChanged(self, text): + self.tablename = str(text) + if self.plugin.connection.isConnected(): + if self.plugin.connection.hasTableName(self.tablename): + self.fieldsbtn.setEnabled(True) + return + self.fieldsbtn.setEnabled(False) + + class MainBox(qt.QHBox): + def __init__(self,copycenterplugin,plugin,parent): + qt.QHBox.__init__(self,parent) + self.copycenterplugin = copycenterplugin + self.plugin = plugin + + self.prjbox = ProjectBox(self,copycenterplugin,plugin,parent) + self.driverbox = DriverBox(self,parent) + + statusbar = qt.QHBox(parent) + statusbar.setSpacing(2) + #self.statuslabel = qt.QLabel("Disconnected",statusbar) + #statusbar.setStretchFactor(self.statuslabel,1) + statusbar.setStretchFactor(qt.QWidget(statusbar),1) + self.connectbtn = qt.QPushButton("Connect",statusbar) + self.connectbtn.setEnabled(False) + qt.QObject.connect(self.connectbtn, qt.SIGNAL("clicked()"),self.connectClicked) + self.disconnectbtn = qt.QPushButton("Disconnect",statusbar) + self.disconnectbtn.setEnabled(False) + qt.QObject.connect(self.disconnectbtn, qt.SIGNAL("clicked()"),self.disconnectClicked) + + #self.connectionbox = ConnectionBox(copycenterplugin,plugin,parent) + self.tablebox = TableBox(copycenterplugin,plugin,parent) + self.fieldbox = FieldBox(copycenterplugin,plugin,parent) + qt.QObject.connect(self.tablebox.tableedit, qt.SIGNAL("textChanged(const QString&)"), self.fieldbox.tableChanged) + + if self.plugin.options['project'] != '': + self.prjbox.prjcombo.setCurrentText(self.plugin.options['project']) + + if self.plugin.options['driver'] != '': + try: + item = str(self.driverbox.drivercombo.listBox().findItem(self.plugin.options['driver'],qt.Qt.ExactMatch).text()) + self.driverbox.drivercombo.setCurrentText(item) + self.driverbox.activated(item) + except: + pass + + if self.plugin.plugintype == "Destination": + #typebox = qt.QHBox(parent) + #label = qt.QLabel("Operation:",typebox) + #combobox = qt.QComboBox(typebox) + #combobox.insertItem("Append") + #combobox.insertItem("Replace") + #combobox.insertItem("Update") + #combobox.insertItem("Update/Insert") + #combobox.insertItem("Insert new") + #label.setBuddy(combobox) + #typebox.setStretchFactor(combobox,1) + pass + elif self.plugin.plugintype == "Source": + wherebox = qt.QHBox(parent) + wherelabel = qt.QLabel("Where:",wherebox) + self.whereedit = qt.QLineEdit(self.plugin.options['where'],wherebox) + + #orderbox = qt.QHBox(parent) + #orderlabel = qt.QLabel("Order By:",orderbox) + #orderedit = qt.QLineEdit("",orderbox) + + #errbox = qt.QHBox(parent) + #errlabel = qt.QLabel("On Error:",errbox) + #errcombo = qt.QComboBox(errbox) + #errcombo.insertItem("Ask") + #errcombo.insertItem("Skip") + #errcombo.insertItem("Abort") + #errlabel.setBuddy(errcombo) + #errbox.setStretchFactor(errcombo,1) + + if self.plugin.options['autoconnect']: + self.connectClicked() + + def projectChanged(self, text): + #if self.driverbox.drivercombo.currentItem() != 0: + # self.driverbox.drivercombo.setCurrentItem(0) + + file = str(text) + import os + if re.search("^\\~(\\/|\\\\)",file): + file = os.path.join(self.copycenterplugin.copycenter.homepath,file[2:]) + if file == "" or not os.path.isfile(file): + self.driverbox.drivercombo.setCurrentItem(0) + self.driverbox.activated(0) + return + + connectiondata = self.copycenterplugin.drivermanager.createConnectionDataByFile(file) + if connectiondata == None: + raise "Unsupported file." + + drivername = connectiondata.driverName().lower() + print "driver: %s" % drivername + for i in range(1,self.driverbox.drivercombo.count()): + if drivername == self.driverbox.drivercombo.text(i).lower(): + self.driverbox.drivercombo.setCurrentItem(i) + self.driverbox.activated(i) + break + + if self.driverbox.driver != None: + if self.driverbox.driver.isFileDriver(): + self.driverbox.fileedit.setText(connectiondata.fileName()) + else: # server + self.driverbox.hostedit.setText(connectiondata.hostName()) + self.driverbox.portedit.setText(str(connectiondata.port())) + self.driverbox.sockfilecheckbox.setChecked(connectiondata.localSocketFileUsed()) + self.driverbox.sockfileedit.setText(connectiondata.localSocketFileName()) + self.driverbox.useredit.setText(connectiondata.userName()) + self.driverbox.passedit.setText(connectiondata.password()) + self.driverbox.dbedit.setText(connectiondata.databaseName()) + + def connectClicked(self): + if self.driverbox.driver == None: + print "No driver selected." + return False + connectiondata = self.copycenterplugin.drivermanager.createConnectionData() + if self.driverbox.driver.isFileDriver(): + file = str(self.driverbox.fileedit.text()) + if file == "" or not os.path.isfile(file): + qt.QMessageBox.critical(self,"Failed to connect","There exists no such database file \"%s\"" % file) + return False + connectiondata.setFileName(file) + connectiondata.setDatabaseName(file) + else: + connectiondata.setHostName(str(self.driverbox.hostedit.text())) + connectiondata.setPort(str(self.driverbox.portedit.text())) + connectiondata.setLocalSocketFileUsed(self.driverbox.sockfilecheckbox.isChecked()) + connectiondata.setLocalSocketFileName(str(self.driverbox.sockfileedit.text())) + connectiondata.setPassword(str(self.driverbox.passedit.text())) + connectiondata.setUserName(str(self.driverbox.useredit.text())) + connectiondata.setDatabaseName(str(self.driverbox.dbedit.text())) + print "Creating connection" + connection = self.driverbox.driver.createConnection(connectiondata) + print "Trying to connect" + if not connection.connect(): + qt.QMessageBox.critical(self,"Failed to connect",connection.lastError()) + return False + print "Use database \"%s\"" % connectiondata.databaseName() + if not connection.useDatabase( connectiondata.databaseName() ): + qt.QMessageBox.critical(self,"Failed to connect",connection.lastError()) + return False + print "dbnames = %s" % connection.databaseNames() + print "tablenames = %s" % connection.tableNames() + #self.useDatabase(connection, filename) + + self.plugin.connection.kexidbconnection = connection + self.updateConnectButtons() + return True + + def disconnectClicked(self): + if not self.plugin.connection.disconnect(): + qt.QMessageBox.critical(self,"Failed to disconnect",self.plugin.connection.lastError()) + return + self.updateConnectButtons() + + def updateConnectButtons(self): + connected = self.plugin.connection.isConnected() + self.prjbox.setEnabled(not connected) + self.driverbox.setEnabled(not connected) + self.connectbtn.setEnabled( (not connected) and (self.driverbox.driver != None) ) + self.disconnectbtn.setEnabled(connected) + self.tablebox.tablebtn.setEnabled(connected) + self.fieldbox.tableChanged(self.tablebox.tableedit.text()) + + def getOptionValue(self,optionname): + try: + if optionname == 'project': return str(self.prjbox.prjcombo.currentText()) + elif optionname == 'driver': return str(self.driverbox.drivercombo.currentText()) + elif optionname == 'file': return str(self.driverbox.fileedit.text()) + elif optionname == 'hostname': return str(self.driverbox.hostedit.text()) + elif optionname == 'port': return str(self.driverbox.portedit.text()) + elif optionname == 'usesocketfile': return str(self.driverbox.sockfilecheckbox.isChecked()) + elif optionname == 'socketfile': return str(self.driverbox.sockfileedit.text()) + elif optionname == 'username': return str(self.driverbox.useredit.text()) + elif optionname == 'password': return str(self.driverbox.passedit.text()) + elif optionname == 'database': return str(self.driverbox.dbedit.text()) + elif optionname == 'table': return str(self.tablebox.tableedit.text()) + elif optionname == 'fields': return str(self.fieldbox.fieldsedit.text()) + elif optionname == 'where': return str(self.whereedit.text()) + except: + pass + return "" + + mainbox = MainBox(self,plugin,parent) + plugin.widget = mainbox + return mainbox + diff --git a/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py new file mode 100644 index 00000000..985d757d --- /dev/null +++ b/kexi/plugins/scripting/scripts/copycenter/CopyCenterPluginQtSQL.py @@ -0,0 +1,495 @@ +""" +CopyCenterPlugin to provide 'QtSQL'. + +Description: +This python-script is a plugin for the CopyCenter.py. + +Author: +Sebastian Sauer + +Copyright: +GPL v2 or higher. +""" + +class CopyCenterPlugin: + """ The CopyCenterPlugin to provide 'QtSQL' to CopyCenter.py """ + + name = "QtSQL Database" + """ The name this plugin has. The name should be unique and + will be used for displaying a caption. """ + + class Plugin: + def _init_(self,copycenterplugin): + self.copycenterplugin = copycenterplugin + self.widget = None + self.database = None + self.cursor = None + self.isfinished = True + def _init(self,copierer): + self.copierer = copierer + if not self.widget.connectClicked(): + raise "Failed to connect with database." + if self.database == None or not self.database.isOpen(): + raise "Database is not initialized or not opened." + self.copierer.appendProgressMessage("Connected: %s %s@%s:%i %s" % + (str(self.database.driverName()),str(self.database.userName()),str(self.database.hostName()),self.database.port(),str(self.database.databaseName())) ) + self.isfinished = False + def isFinished(self): + return self.isfinished + def finish(self): + self.isfinished = True + self.widget.disconnectClicked() + def createWidget(self,dialog,parent): + return self.copycenterplugin.widget(dialog, self, parent) + + class Source(Plugin): + plugintype = "Source" + def __init__(self,copycenterplugin): + self._init_(copycenterplugin) + self.options = { + 'driver': 'QMYSQL3', #'QMYSQL3','QPSQL7','QODBC3',... + 'hostname': '127.0.0.1', + 'port': 3306, + 'username': 'root', #'MyUsername', + 'password': '', #'MySecretPassword', + 'database': '', #'MyQtSQLDatabase', + 'table': '', #'table1', + 'fields': '', #'f1,f2', + 'where': '', + } + def init(self,copierer): + self._init(copierer) + tablename = str(self.widget.tableedit.text()) + wherestatement = str(self.widget.whereedit.text()) + import qt + import qtsql + self.cursor = qtsql.QSqlCursor(tablename,True,self.database) + self.cursor.setFilter(wherestatement) + if not self.cursor.select(): + raise "Select on cursor failed.
%s
%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) ) + self.fieldlist = [] + for fieldname in str(self.widget.fieldedit.text()).split(","): + fn = fieldname.strip() + if fn != "": + field = self.cursor.field(fn) + if not field: + raise "There exists no such field \"%s\" in the table \"%s\"." % (fn,tablename) + self.fieldlist.append(str(field.name())) + if len(self.fieldlist) < 1: + raise "No fields for table \"%s\" defined." % tablename + copierer.appendProgressMessage("SQL: %s" % str(self.cursor.executedQuery())) + + def read(self): + if not self.cursor.next(): + return None + record = [] + for fieldname in self.fieldlist: + record.append( unicode(self.cursor.value(fieldname).toString()).encode("latin-1") ) + #print "read record: %s" % record + return record + + class Destination(Plugin): + plugintype = "Destination" + def __init__(self,copycenterplugin): + self._init_(copycenterplugin) + self.options = { + 'driver': 'QMYSQL3', #'QMYSQL3','QPSQL7','QODBC3',... + 'hostname': '127.0.0.1', + 'port': 3306, + 'username': 'root', #'MyUsername', + 'password': '', #'MySecretPassword', + 'database': '', #'MyQtSQLDatabase', + 'table': '', #'table2', + 'fields': '', #'field1,field2', + 'operation': 'Insert', #'Insert','Update'... + 'indexfield': '', + } + def init(self,copierer): + self._init(copierer) + import qt + import qtsql + + self.fieldlist = [] + for fieldname in str(self.widget.fieldedit.text()).split(","): + fn = fieldname.strip() + if fn != "": self.fieldlist.append(fn) + + tablename = str(self.widget.tableedit.text()) + self.cursor = qtsql.QSqlCursor(tablename,True,self.database) + { + 0: self.initInsert, + 1: self.initUpdate + }[ self.widget.operationedit.currentItem() ]() + + def initInsert(self): + self.write = self.writeInsert + if not self.cursor.select(): + raise "Select on cursor failed.
%s
%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) ) + for fieldname in self.fieldlist: # check fieldlist + field = self.cursor.field(fieldname) + if not field: raise "There exists no such field \"%s\" in the table \"%s\"." % (fieldname, self.cursor.name()) + self.copierer.appendProgressMessage("Insert SQL: %s" % str(self.cursor.executedQuery())) + + def writeInsert(self, record): + print "insert record: %s" % record + import qt + cursorrecord = self.cursor.primeInsert() + count = len(record) + for i in range(len(self.fieldlist)): + if i == count: break + r = record[i] + if r == None: + v = qt.QVariant() + else: + v = qt.QVariant(r) + cursorrecord.setValue(self.fieldlist[i], v) + rowcount = self.cursor.insert() + if rowcount < 1: + drv = unicode(self.cursor.lastError().driverText()).encode("latin-1") + db = unicode(self.cursor.lastError().databaseText()).encode("latin-1") + print "failed: %s %s" % (drv,db) + self.copierer.writeFailed(record) + else: + self.copierer.writeSuccess(record,rowcount) + #import time + #time.sleep(1) + return True + + def initUpdate(self): + self.write = self.writeUpdate + self.indexfieldname = str(self.widget.indexedit.text()).strip() + if self.indexfieldname == "": raise "No index-field defined." + pkindex = self.cursor.index(self.indexfieldname) + if not pkindex: raise "Invalid index-field defined." + self.cursor.setPrimaryIndex(pkindex) + #self.cursor.setMode( qtsql.QSqlCursor.Insert | qtsql.QSqlCursor.Update ) + self.copierer.appendProgressMessage("Update SQL: %s" % str(self.cursor.executedQuery())) + + def writeUpdate(self, record): + import qt + # determinate the primary-index + try: + idx = self.fieldlist.index(self.indexfieldname) + indexvalue = record[idx] + except: + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + raise "Failed to determinate the value for the primary key." + # select cursor and go to matching record. + wherestatement = "%s = \"%s\"" % (self.indexfieldname, indexvalue) + if not self.cursor.select(wherestatement): + raise "Select on cursor failed.
%s
%s" % ( str(self.cursor.lastError().driverText()),str(self.cursor.lastError().databaseText()) ) + if not self.cursor.next(): + #print "No such record to update !" + return False + # Prepare updating the record. + cursorrecord = self.cursor.primeUpdate() + # Update the fields in the record. + count = len(record) + for i in range(len(self.fieldlist)): + if i == count: break + fieldname = self.fieldlist[i] + if self.indexfieldname != fieldname: # don't update the indexfield! + r = record[i] + if r == None: + v = qt.QVariant() + else: + v = qt.QVariant(r) + cursorrecord.setValue(fieldname, v) + # Write updated record. + rowcount = self.cursor.update() + if rowcount < 1: + self.copierer.writeFailed(record) + else: + self.copierer.writeSuccess(record,rowcount) + print "updated record (rowcount %s): %s" % (rowcount,record) + return True + + def __init__(self, copycenter): + """ Constructor. """ + pass + + def widget(self,dialog,plugin,parent): + """ Each plugin may provide a qt.QWidget back to the + CopyCenter.py. The widget will be used to configure our + plugin settings. """ + + import qt + import os + + self.dialog = dialog + ListViewDialog = self.dialog.ListViewDialog + class TableDialog(ListViewDialog): + def __init__(self, mainwidget): + ListViewDialog.__init__(self,mainwidget,"Tables") + self.mainwidget = mainwidget + self.listview.addColumn("Name") + text = str(self.mainwidget.tableedit.text()) + item = None + for table in self.mainwidget.plugin.database.tables(): + if item == None: + item = qt.QListViewItem(self.listview,table) + else: + item = qt.QListViewItem(self.listview,item,table) + if table == text: + self.listview.setSelected(item,True) + self.listview.ensureItemVisible(item) + qt.QObject.connect(self.listview, qt.SIGNAL("doubleClicked(QListViewItem*, const QPoint&, int)"), self.okClicked) + qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked) + def okClicked(self): + item = self.listview.selectedItem() + if item == None: + self.mainwidget.tableedit.setText("") + else: + self.mainwidget.tableedit.setText(item.text(0)) + self.close() + + class FieldsDialog(ListViewDialog): + def __init__(self, mainwidget): + ListViewDialog.__init__(self,parent,"Fields") + self.mainwidget = mainwidget + self.listview.setSelectionMode(qt.QListView.Multi) + self.listview.setSorting(-1) + self.listview.header().setClickEnabled(False) + self.listview.addColumn("Name") + self.listview.addColumn("Type") + self.listview.addColumn("Options") + tablename = str(self.mainwidget.tableedit.text()) + recinfo = self.mainwidget.plugin.database.recordInfo(tablename) + if recinfo != None: + fieldslist = str(self.mainwidget.fieldedit.text()).split(",") + allfields = ("*" in fieldslist) + item = None + for fieldinfo in recinfo: + opts = "" + for s in ('Required','Calculated'): #,'Generated'): + if getattr(fieldinfo,"is%s" % s)(): opts += "%s " % s + item = self.addItem((fieldinfo.name(), qt.QVariant.typeToName(fieldinfo.type()), opts),item) + if allfields or fieldinfo.name() in fieldslist: + self.listview.setSelected(item,True) + qt.QObject.connect(self.okbtn, qt.SIGNAL("clicked()"), self.okClicked) + def okClicked(self): + selitems = [] + item = self.listview.firstChild() + while item: + if item.isSelected(): + selitems.append(str(item.text(0))) + item = item.nextSibling() + self.mainwidget.fieldedit.setText(",".join(selitems)) + self.close() + + + class MainWidget(qt.QHBox): + def __init__(self,plugin,dialog,parent): + import qt + import qtsql + qt.QHBox.__init__(self,parent) + self.dialog = dialog + self.plugin = plugin + + self.connectionbox = qt.QVBox(parent) + self.connectionbox.setSpacing(2) + + driverbox = qt.QHBox(self.connectionbox) + driverlabel = qt.QLabel("Driver:",driverbox) + self.driveredit = qt.QComboBox(driverbox) + for driver in qtsql.QSqlDatabase.drivers(): + self.driveredit.insertItem(driver) + if self.plugin.options['driver'] == driver: + self.driveredit.setCurrentItem(self.driveredit.count() - 1) + driverlabel.setBuddy(self.driveredit) + driverbox.setStretchFactor(self.driveredit,1) + + hostbox = qt.QHBox(self.connectionbox) + hostlabel = qt.QLabel("Hostname:",hostbox) + self.hostedit = qt.QLineEdit(self.plugin.options['hostname'],hostbox) + hostlabel.setBuddy(self.hostedit) + hostbox.setStretchFactor(self.hostedit,1) + + portbox = qt.QHBox(self.connectionbox) + portlabel = qt.QLabel("Port:",portbox) + self.portedit = qt.QLineEdit(str(self.plugin.options['port']),portbox) + portlabel.setBuddy(self.portedit) + portbox.setStretchFactor(self.portedit,1) + + userbox = qt.QHBox(self.connectionbox) + userlabel = qt.QLabel("Username:",userbox) + self.useredit = qt.QLineEdit(self.plugin.options['username'],userbox) + userlabel.setBuddy(self.useredit) + userbox.setStretchFactor(self.useredit,1) + + passbox = qt.QHBox(self.connectionbox) + passlabel = qt.QLabel("Password:",passbox) + self.passedit = qt.QLineEdit(self.plugin.options['password'],passbox) + self.passedit.setEchoMode(qt.QLineEdit.Password) + passlabel.setBuddy(self.passedit) + passbox.setStretchFactor(self.passedit,1) + + dbbox = qt.QHBox(self.connectionbox) + dblabel = qt.QLabel("Database:",dbbox) + self.dbedit = qt.QLineEdit(self.plugin.options['database'],dbbox) + dblabel.setBuddy(self.dbedit) + dbbox.setStretchFactor(self.dbedit,1) + + statusbar = qt.QHBox(parent) + statusbar.setSpacing(2) + statusbar.setStretchFactor(qt.QWidget(statusbar),1) + self.connectbtn = qt.QPushButton("Connect",statusbar) + qt.QObject.connect(self.connectbtn, qt.SIGNAL("clicked()"),self.connectClicked) + self.disconnectbtn = qt.QPushButton("Disconnect",statusbar) + self.disconnectbtn.setEnabled(False) + qt.QObject.connect(self.disconnectbtn, qt.SIGNAL("clicked()"),self.disconnectClicked) + + tablebox = qt.QHBox(parent) + tablelabel = qt.QLabel("Table:",tablebox) + self.tableedit = qt.QLineEdit(self.plugin.options['table'],tablebox) + qt.QObject.connect(self.tableedit, qt.SIGNAL("textChanged(const QString&)"), self.tableEditChanged) + self.tablebtn = qt.QPushButton("...",tablebox) + self.tablebtn.setEnabled(False) + qt.QObject.connect(self.tablebtn, qt.SIGNAL("clicked()"), self.tableBtnClicked) + tablelabel.setBuddy(self.tableedit) + tablebox.setStretchFactor(self.tableedit,1) + + fieldbox = qt.QHBox(parent) + fieldlabel = qt.QLabel("Fields:",fieldbox) + self.fieldedit = qt.QLineEdit(self.plugin.options['fields'],fieldbox) + self.fieldbtn = qt.QPushButton("...",fieldbox) + self.fieldbtn.setEnabled(False) + qt.QObject.connect(self.fieldbtn, qt.SIGNAL("clicked()"), self.fieldBtnClicked) + fieldlabel.setBuddy(self.fieldedit) + fieldbox.setStretchFactor(self.fieldedit,1) + + if self.plugin.plugintype == "Source": + box = qt.QHBox(parent) + wherelabel = qt.QLabel("Where:",box) + self.whereedit = qt.QLineEdit(self.plugin.options['where'],box) + wherelabel.setBuddy(self.whereedit) + box.setStretchFactor(self.whereedit,1) + elif self.plugin.plugintype == "Destination": + + class OperationBox(qt.QVBox): + def __init__(self, mainwidget, parent): + self.mainwidget = mainwidget + qt.QVBox.__init__(self, parent) + opbox = qt.QHBox(self) + operationlabel = qt.QLabel("Operation:",opbox) + self.mainwidget.operationedit = qt.QComboBox(opbox) + for op in ('Insert','Update'): + self.mainwidget.operationedit.insertItem(op) + if self.mainwidget.plugin.options['operation'] == op: + self.mainwidget.operationedit.setCurrentItem(self.mainwidget.operationedit.count() - 1) + operationlabel.setBuddy(self.mainwidget.operationedit) + opbox.setStretchFactor(self.mainwidget.operationedit,1) + self.box = None + qt.QObject.connect(self.mainwidget.operationedit, qt.SIGNAL("activated(int)"), self.operationActivated) + self.operationActivated() + def operationActivated(self, **args): + if self.box: + self.box.hide() + self.box.destroy() + self.box = None + def showInsert(self): + pass + def showUpdate(self): + self.box = qt.QHBox(self) + indexlabel = qt.QLabel("Indexfield:", self.box) + self.mainwidget.indexedit = qt.QLineEdit(self.mainwidget.plugin.options['indexfield'], self.box) + indexlabel.setBuddy(self.mainwidget.indexedit) + self.box.setStretchFactor(self.mainwidget.indexedit,1) + { + 0: showInsert, + 1: showUpdate, + }[ self.mainwidget.operationedit.currentItem() ](self) + if self.box != None: self.box.show() + OperationBox(self,parent) + + def tableEditChanged(self,text): + if self.plugin.database != None and self.plugin.database.isOpen(): + if str(text) in self.plugin.database.tables(): + self.fieldbtn.setEnabled(True) + return + self.fieldbtn.setEnabled(False) + + def tableBtnClicked(self): + dialog = TableDialog(self) + dialog.show() + + def fieldBtnClicked(self): + dialog = FieldsDialog(self) + dialog.show() + + def updateConnectState(self): + connected = self.plugin.database != None and self.plugin.database.isOpen() + self.connectionbox.setEnabled(not connected) + self.connectbtn.setEnabled(not connected) + self.disconnectbtn.setEnabled(connected) + self.tablebtn.setEnabled(connected) + self.tableEditChanged(self.tableedit.text()) + + def getOptionValue(self,optionname): + try: + if optionname == 'driver': return str(self.driveredit.currentText()) + if optionname == 'hostname': return str(self.hostedit.text()) + if optionname == 'port': return str(self.portedit.text()) + if optionname == 'username': return str(self.useredit.text()) + if optionname == 'password': return str(self.passedit.text()) + if optionname == 'database': return str(self.dbedit.text()) + if optionname == 'table': return str(self.tableedit.text()) + if optionname == 'fields': return str(self.fieldedit.text()) + if optionname == 'where': return str(self.whereedit.text()) + if optionname == 'operation': return str(self.operationedit.currentText()) + if optionname == 'indexfield': return str(self.indexedit.text()) + except: + import traceback + print "".join( traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2]) ) + return "" + + def connectClicked(self): + if self.plugin.database != None and self.plugin.database.isOpen(): + print "already connected. not needed to reconnect..." + self.updateConnectState() + return True + print "trying to connect..." + + import qtsql + drivername = str(self.driveredit.currentText()) + print "drivername: %s" % drivername + connectionname = "CopyCenter%s" % self.plugin.plugintype + print "connectionname: %s" % connectionname + self.plugin.database = qtsql.QSqlDatabase.addDatabase(drivername,connectionname) + if not self.plugin.database: + qt.QMessageBox.critical(self,"Failed to connect","Failed to create database for driver \"%s\"" % drivername) + return False + + hostname = str(self.hostedit.text()) + self.plugin.database.setHostName(hostname) + + portnumber = int(str(self.portedit.text())) + self.plugin.database.setPort(portnumber) + + username = str(self.useredit.text()) + self.plugin.database.setUserName(username) + + password = str(self.passedit.text()) + self.plugin.database.setPassword(password) + + databasename = str(self.dbedit.text()) + self.plugin.database.setDatabaseName(databasename) + + if not self.plugin.database.open(): + qt.QMessageBox.critical(self,"Failed to connect","%s

%s
" % (self.plugin.database.lastError().driverText(),self.plugin.database.lastError().databaseText())) + return False + print "database is opened now!" + self.updateConnectState() + return True + + def disconnectClicked(self): + print "trying to disconnect..." + if self.plugin.database: + self.plugin.database.close() + self.plugin.database = None + print "database is closed now!" + self.updateConnectState() + + plugin.widget = MainWidget(plugin,self.dialog,parent) + return plugin.widget diff --git a/kexi/plugins/scripting/scripts/copycenter/Makefile.am b/kexi/plugins/scripting/scripts/copycenter/Makefile.am new file mode 100644 index 00000000..d46928ed --- /dev/null +++ b/kexi/plugins/scripting/scripts/copycenter/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/kexi/Makefile.global + +scriptsdir = $(kde_datadir)/kexi/scripts/copycenter +scripts_SCRIPTS = CopyCenter.py CopyCenterPluginQtSQL.py CopyCenterPluginKexiDB.py CopyCenter.rc readme.html diff --git a/kexi/plugins/scripting/scripts/copycenter/readme.html b/kexi/plugins/scripting/scripts/copycenter/readme.html new file mode 100644 index 00000000..2aff6152 --- /dev/null +++ b/kexi/plugins/scripting/scripts/copycenter/readme.html @@ -0,0 +1,20 @@ + +

Copy Center

+ +Version 1.2 + +

Python script to copy data between database backends. The flexible +plugin-architecture allows transparent copies between different backends.

+ +
    +
  • Read+write Kexi Databases. This includes all database backends supported by Kexi (like SQLite, MySQL or PostgreSQL).
  • +
  • Read+write QtSQL Databases. MySQL, PostgreSQL and UnixODBC are supported. There might even be more like Oracle in the commercial Qt version or 3rd party backends.
  • +
  • Runs embedded in Kexi (from the tools=>scripts menu) as well as independent of Kexi (use "krossrunner ~/.kde/share/apps/kexi/scripts/copycenter/CopyCenter.py" or python direct).
  • +
  • Depends only on PyQt. PyKDE is not used at all and Kross (included in KOffice 1.5) is optional.
  • +
+ +Author: (C)2006 Sebastian Sauer (mail at dipe dot org)
+Website: http://www.kde-files.org/content/show.php?content=35251
+License: GPL v2 or higher
+ + diff --git a/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py new file mode 100644 index 00000000..cace0340 --- /dev/null +++ b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.py @@ -0,0 +1,196 @@ +""" +Export table or query data. + +Description: +This script exports a KexiDB table or query to different fileformats. + +Author: +Sebastian Sauer + +Copyright: +Dual-licensed under LGPL v2+higher and the BSD license. +""" + +class Datasource: + def __init__(self): + import kexiapp + keximainwindow = kexiapp.get("KexiAppMainWindow") + + try: + self.connection = keximainwindow.getConnection() + except: + raise "No connection established. Please open a project before." + + self.schema = None + + def getSources(self): + sources = [] + for table in self.connection.tableNames(): + sources.append("Tables/%s" % table) + for query in self.connection.queryNames(): + sources.append("Queries/%s" % query) + sources.sort() + return sources + + def setSource(self, source): + s = source.split("/",1) + if s[0] == "Tables": + self.schema = self.connection.tableSchema( s[1] ) + self.queryschema = self.schema.query() + elif s[0] == "Queries": + self.schema = self.connection.querySchema( s[1] ) + self.queryschema = self.schema + self.cursor = None + return self.schema != None + + def name(self): + return self.schema.name() + + def caption(self): + return self.schema.caption() + + def description(self): + return self.schema.description() + + def header(self): + h = [] + for field in self.schema.fieldlist().fields(): + s = field.caption() + if s == None or s == "": + s = field.name() + h.append(s) + return h + + def getNext(self): + if not self.cursor: + self.cursor = self.connection.executeQuerySchema( self.queryschema ) + if not self.cursor: + raise "Failed to execute queryschema." + if not self.cursor.moveFirst(): + raise "Failed to move cursor to first record." + if self.cursor.eof(): + self.cursor = None + return None + items = [] + for i in range( self.cursor.fieldCount() ): + items.append( self.cursor.value(i) ) + self.cursor.moveNext() + return items + +class HtmlExporter: + def __init__(self, datasource): + self.datasource = datasource + + def htmlescape(self, text): + import string + return string.replace(string.replace(string.replace(str(text),'&','&'),'<','<'),'>','>') + + def write(self, output, style): + name = self.datasource.name() + + output.write("\n") + output.write("\n") + output.write("\n") + output.write("%s\n" % name) + output.write("\n") + output.write("\n") + output.write("

%s

\n" % name) + + caption = self.datasource.caption() + if caption and caption != name: + output.write("caption: %s
\n" % caption) + + description = self.datasource.description() + if description: + output.write("description: %s
\n" % description) + + #import datetime + #output.write("date: %s
" % datetime.datetime.now()) + + output.write("\n") + + output.write("") + for h in self.datasource.header(): + output.write("" % h) + output.write("") + + while 1 == 1: + items = self.datasource.getNext() + if items == None: break + output.write("") + for item in items: + u = unicode(str(self.htmlescape(item)),"latin-1") + output.write("" % u.encode("utf-8")) + output.write("\n") + output.write("
%s
%s
\n") + output.write("\n") + +class GuiApp: + def __init__(self, datasource): + self.datasource = datasource + + try: + import gui + except: + raise "Import of the Kross GUI module failed." + + self.dialog = gui.Dialog("Export XHTML") + self.dialog.addLabel(self.dialog, "Export a table- or query-datasource to a XHTML-file.") + + datasourceitems = self.datasource.getSources() + self.datasourcelist = self.dialog.addList(self.dialog, "Datasource:", datasourceitems) + + styleitems = ["Plain", "Paper", "Blues"] + self.stylelist = self.dialog.addList(self.dialog, "Style:", styleitems) + + #queryframe = Tkinter.Frame(frame) + #queryframe.pack() + #Tkinter.Label(queryframe, text="Table or query to export:").pack(side=Tkinter.LEFT) + #self.querycontent = Tkinter.StringVar() + #self.query = apply(Tkinter.OptionMenu, (queryframe, self.querycontent) + tuple( self.datasource.getSources() )) + #self.query.pack(side=Tkinter.LEFT) + + self.file = self.dialog.addFileChooser(self.dialog, + "File:", + gui.getHome() + "/kexidata.xhtml", + (('XHTML files', '*.xhtml'),('All files', '*'))) + + btnframe = self.dialog.addFrame(self.dialog) + self.dialog.addButton(btnframe, "Export", self.doExport) + self.dialog.addButton(btnframe, "Cancel", self.dialog.close) + + self.dialog.show() + + def doExport(self): + file = str( self.file.get() ) + query = str( self.datasourcelist.get() ) + print "Exporting '%s' to file '%s' ..." % (query,file) + + if not self.datasource.setSource(query): + raise "Invalid datasource selected." + #return + + style = str( self.stylelist.get() ) + + f = open(file, "w") + global HtmlExporter + exporter = HtmlExporter(self.datasource) + exporter.write(f, style) + f.close() + + print "Successfully exported '%s' to file %s" % (query,file) + self.dialog.close() + +GuiApp( Datasource() ) diff --git a/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc new file mode 100644 index 00000000..11c1dcdf --- /dev/null +++ b/kexi/plugins/scripting/scripts/exportxhtml/ExportXHTML.rc @@ -0,0 +1,8 @@ + + + diff --git a/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am b/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am new file mode 100644 index 00000000..1c7b9ca6 --- /dev/null +++ b/kexi/plugins/scripting/scripts/exportxhtml/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/kexi/Makefile.global + +scriptsdir = $(kde_datadir)/kexi/scripts/exportxhtml +scripts_SCRIPTS = ExportXHTML.py ExportXHTML.rc diff --git a/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py new file mode 100755 index 00000000..200b3dee --- /dev/null +++ b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.py @@ -0,0 +1,434 @@ +""" +Import data from a XHTML file to a KexiDB table. + +Description: +This script implements import of data from a XHTML file to a KexiDB table. The +table needs to be an already existing table the data should be added to. + +Author: +Sebastian Sauer + +Copyright: +Dual-licensed under LGPL v2+higher and the BSD license. +""" + +class SaxInput: + """ The inputsource we like to import the data from. This class + provides us abstract access to the SAX XML parser we use internaly + to import data from the XML-file. """ + + xmlfile = None + """ The XML file we should read the content from. """ + + def __init__(self): + """ Constructor. """ + + # try to import the xml.sax python module. + try: + import xml.sax.saxlib + import xml.sax.saxexts + except: + raise "Import of the python xml.sax.saxlib module failed. This module is needed by the ImportXHTML python script." + + def read(self, outputwriter): + """ Start reading and parsing the XML-file. """ + + import xml.sax.saxlib + import xml.sax.saxexts + + class SaxHandler(xml.sax.saxlib.HandlerBase): + """ The SaxHandler is our event-handler SAX calls on + parsing the XML-file. """ + + tablebase = ["html","body","table"] + """ The table-base defines where we will find our table-tag + that holds all the data we are interessted at. The default + is to look at
. """ + + def __init__(self, inputreader, outputwriter): + """ Constructor. """ + + # The to a SaxInput instance pointing inputreader. + self.inputreader = inputreader + # The to a KexiDBOutput instance pointing outputwriter. + self.outputwriter = outputwriter + # The hierachy-level in the DOM-tree we are in. + self.level = 0 + # Defines if we are in the with tablebase defined DOM-element. + self.intable = False + + # Points to a KexiDBOutput.Record instance if we are in a DOM-element that defines a record. + self.record = None + # Points to a KexiDBOutput.Field instance if we are in a record's field. + self.field = None + + def startDocument(self): + sys.stdout.write('=> Starting parsing\n') + + def endDocument(self): + sys.stdout.write('=> Fineshed parsing\n') + + def startElement(self, name, attrs): + """ This method is called by SAX if a DOM-element starts. """ + + if self.level < len(self.tablebase): + if self.tablebase[self.level] != name: + self.intable = False + else: + self.intable = True + self.level += 1 + if not self.intable: + return + + # Print some debugging-output to stdout. + for idx in range(self.level): sys.stdout.write(' ') + sys.stdout.write('Element: %s' % name) + for attrName in attrs.keys(): + sys.stdout.write(' %s="%s"' % (attrName,attrs.get(attrName))) + sys.stdout.write('\n') + + # handle tr-, th- and td-tags inside the table. + if name == "tr" and (self.level == len(self.tablebase) + 1): + self.record = self.outputwriter.Record() + elif name == "td" and (self.level == len(self.tablebase) + 2): + self.field = self.outputwriter.Field() + elif name == "th" and (self.level == len(self.tablebase) + 2): + self.field = self.outputwriter.Field() + + def endElement(self, name): + """ This method is called by SAX if a DOM-Element ends. """ + + self.level -= 1 + #sys.stdout.write('EndElement:%s level:%s len(self.tablebase):%s\n' % (name,self.level,len(self.tablebase))) + + if self.record != None: + # a record is defined. so, we are looking for the matching + # end-tags to close a record or a field. + if name == "tr" and (self.level == len(self.tablebase)): + self.outputwriter.write(self.record) + self.record = None + self.field = None + elif name == "td" and (self.level == len(self.tablebase) + 1): + #if self.field == None: + # raise "Unexpected closing " + self.record.setField( self.field ) + self.field = None + elif name == "th" and (self.level == len(self.tablebase) + 1): + #if self.field == None: + # raise "Unexpected closing " + self.record.setHeader( self.field ) + self.field = None + + def characters(self, chars, offset, length): + """ This method is called by SAX if the text-content of a DOM-Element + was parsed. """ + + if self.field != None: + # the xml-data is unicode and we need to encode it + # to latin-1 cause KexiDB deals only with latin-1. + u = unicode(chars[offset:offset+length]) + self.field.append(u.encode("latin-1")) + + # start the job + outputwriter.begin() + # create saxhandler to handle parsing events. + handler = SaxHandler(self, outputwriter) + # we need a sax-parser and connect it with the handler. + parser = xml.sax.saxexts.make_parser() + parser.setDocumentHandler(handler) + # open the XML-file, parse the content and close the file again. + f = file(self.xmlfile, 'r') + parser.parseFile(f) + f.close() + # job is done + outputwriter.end() + +class KexiDBOutput: + """ The destination target we like to import the data to. This class + provides abstract access to the KexiDB module. """ + + class Result: + """ Holds some informations about the import-result. """ + def __init__(self, outputwriter): + self.outputwriter = outputwriter + # number of records successfully imported. + self.successcount = 0 + # number of records where import failed. + self.failedcount = 0 + + def addLog(self, record, state): + import datetime + date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M.%S") + self.outputwriter.logfile.write("%s (%s) %s\n" % (date,state,str(record))) + + def success(self, record): + """ Called if a record was written successfully. """ + print "SUCCESS: %s" % str(record) + self.successcount += 1 + if hasattr(self.outputwriter,"logfile"): + self.addLog(record, "Success") + + def failed(self, record): + """ Called if we failed to write a record. """ + print "FAILED: %s" % str(record) + self.failedcount += 1 + if hasattr(self.outputwriter,"logfile"): + self.addLog(record, "Failed") + + class Record: + """ A Record in the dataset. """ + def __init__(self): + self.fields = [] + def setHeader(self, headerfield): + self.fields.append( headerfield ) + self.isHeader = True + def setField(self, field): + self.fields.append( field ) + def __str__(self): + s = "[" + for f in self.fields: + s += "%s, " % str(f) + return s + "]" + + class Field: + """ A field in a record. """ + def __init__(self): + self.content = [] + def append(self, content): + self.content.append( content ) + def __str__(self): + return "".join(self.content) + + def __init__(self): + """ Constructor. """ + import kexiapp + keximainwindow = kexiapp.get("KexiAppMainWindow") + + try: + self.connection = keximainwindow.getConnection() + except: + raise "No connection established. Please open a project before." + + self.fieldlist = None + self.headerrecord = None + self.mapping = {} + + def begin(self): + """ Called before parsing starts. """ + print "START JOB" + if self.fieldlist == None: + raise "Invalid tableschema or fieldlist!" + global KexiDBOutput + self.result = KexiDBOutput.Result(self) + if hasattr(self,"logfilename") and self.logfilename != None and self.logfilename != "": + self.logfile = open(self.logfilename,'w') + + def end(self): + """ Called if parsing is fineshed. """ + print "END JOB" + self.logfile = None + self.mapping = {} + #self.headerrecord = None + + def getTables(self): + """ return a list of avaiable tablenames. """ + tables = self.connection.tableNames() + tables.sort() + return tables + + def setTable(self, tablename): + """ Set the tablename we like to import the data to. """ + tableschema = self.connection.tableSchema(tablename) + if tableschema == None: + raise "There exists no table with the name '%s'!" % tablename + self.fieldlist = tableschema.fieldlist() + fields = self.fieldlist.fields() + for field in fields: + print "KexiDBOutput.setTable(%s): %s(%s)" % (tablename,field.name(),field.type()) + print "names=%s" % self.fieldlist.names() + + def setMapping(self, mapping): + """ Set the tablefieldname=xmlcolnr dictonary we should map the data to. """ + self.mapping = mapping + + def setLogFile(self, logfilename): + """ Set the name of the logfile. """ + self.logfilename = logfilename + + def write(self, record): + """ Write the record to the KexiDB table. """ + + if hasattr(record, "isHeader"): + self.headerrecord = record + return + + sys.stdout.write('KexiDBOutput.write:') + for f in record.fields: + sys.stdout.write(' "%s"' % f) + sys.stdout.write('\n') + + if hasattr(self,"onWrite"): + if not self.onWrite(record): + raise RuntimeError() + delattr(self,"onWrite") + self.fieldlist = self.fieldlist.subList( list( self.mapping ) ) + + # Translate a KexiDBOutput.Record into a list of values. + values = [] + for k in self.fieldlist.names(): + values.append( str(record.fields[ int(self.mapping[k]) ]) ) + print "Import values: %s" % values + + try: + if self.connection.insertRecord(self.fieldlist, values): + self.result.success(record) + else: + self.result.failed(record) + except: + err = self.connection.lastError() + raise Exception( "Failed to insert the record:\n%s\n\n%s" % (values,err) ) + #raise Exception( "Failed to insert into table \"%s\" the record:\n%s\n%s" % (self.tableschema.name(),values,self.connection.lastError()) ) + +class GuiApp: + """ The GUI-dialog displayed to let the user define the source + XML-file and the destination KexiDB table. """ + + class InitialDialog: + def __init__(self, guiapp): + self.guiapp = guiapp + self.ok = False + + import gui + self.dialog = gui.Dialog("Import XHTML") + self.dialog.addLabel(self.dialog, "Import data from a XHTML-file to a KexiDB table.\n" + "The destination table needs to be an existing table the data should be added to.") + self.importfile = self.dialog.addFileChooser(self.dialog, + "Source File:", + gui.getHome() + "/kexidata.xhtml", + (('XHTML files', '*.xhtml'),('All files', '*'))) + + self.desttable = self.dialog.addList(self.dialog, "Destination Table:", self.guiapp.outputwriter.getTables()) + + #self.operation = self.dialog.addList(self.dialog, "Operation:", ("Insert","Update","Insert/Update")) + #self.error = self.dialog.addList(self.dialog, "On error:", ("Ask","Skip","Abort")) + + self.logfile = self.dialog.addFileChooser(self.dialog, + "Log File:", + "", + (('Logfiles', '*.log'),('All files', '*'))) + + btnframe = self.dialog.addFrame(self.dialog) + self.dialog.addButton(btnframe, "Next", self.doNext) + self.dialog.addButton(btnframe, "Cancel", self.doCancel) + self.dialog.show() + + def doCancel(self): + """ Called if the Cancel-button was pressed. """ + self.dialog.close() + self.dialog = None + #self.guiapp.InitialDialog + + def doNext(self): + """ Start to import the XML-file into the KexiDB table. """ + + self.guiapp.inputreader.xmlfile = str(self.importfile.get()) + self.guiapp.outputwriter.setTable( str(self.desttable.get()) ) + self.guiapp.outputwriter.setLogFile( str(self.logfile.get()) ) + + try: + self.guiapp.inputreader.read( self.guiapp.outputwriter ) + + msgbox = self.dialog.showMessageBox("info","Import done", + "Successfully imported records: %s\nFailed to import records: %s" % (self.guiapp.outputwriter.result.successcount, self.guiapp.outputwriter.result.failedcount) ) + msgbox.show() + + self.doCancel() + except RuntimeError, e: + pass + #except Exception, e: + # import traceback + # traceback.print_exc() + # msgbox = self.dialog.showMessageBox("error", "Error", e) + # msgbox.show() + + class MapperDialog: + """ The dialog that provides us a way to map + XHTML columns to the destination table. """ + + def __init__(self, outputwriter, record): + self.outputwriter = outputwriter + self.ok = False + fieldlist = outputwriter.fieldlist + + import gui + self.dlg = gui.Dialog("Import XHTML") + self.dlg.addLabel(self.dlg, "Define how the destination table should be mapped to the data from the XHTML file.") + values = ["",] + for i in range(len(record.fields)): + try: + values.append( "%s: %s" % (i,str(outputwriter.headerrecord.fields[i])) ) + except: + values.append( "%s: (%s)" % (i,str(record.fields[i])) ) + + self.items = [] + i = 0 + for field in fieldlist.fields(): + f = self.dlg.addFrame(self.dlg) + + l = self.dlg.addList(f, "%s:" % field.name(), values) + self.items.append( (field,l) ) + + details = "%s:" % str( field.type() ) + if field.isAutoInc(): details += "autoinc," + if field.isUniqueKey(): details += "unique," + if field.isNotNull(): details += "notnull," + if field.isNotEmpty(): details += "notempty," + self.dlg.addLabel(f, "(%s)" % details[:-1]) + + try: + variable = str( record.fields[i] ) + try: + int(variable) + i += 1 + if not field.isAutoInc(): + l.set(i) + except ValueError, e: + if not field.type() in ("Integer","BigInteger","ShortInteger","Float","Double"): + i += 1 + l.set(i) + except: + pass + + btnframe = self.dlg.addFrame(self.dlg) + self.dlg.addButton(btnframe, "Next", self.doNext) + self.dlg.addButton(btnframe, "Cancel", self.dlg.close) + self.dlg.show() + + def doNext(self): + mapping = {} + for item in self.items: + (field,l) = item + fieldname = field.name() + colnr = str( l.get() ).split(":",1)[0] + if colnr.isdigit(): + print "Table field '%s' is mapped to XML column '%s'" % (fieldname,colnr) + mapping[ fieldname ] = colnr + self.outputwriter.setMapping(mapping) + self.ok = True + self.dlg.close() + + def __init__(self, inputreader, outputwriter): + """ Constructor. """ + + self.inputreader = inputreader + self.outputwriter = outputwriter + self.outputwriter.onWrite = self.onWrite + + self.InitialDialog(self) + + def onWrite(self, record): + """ This method got called after the first record got + readed and before we start to import. """ + return self.MapperDialog(self.outputwriter, record).ok + +GuiApp( SaxInput(), KexiDBOutput() ) diff --git a/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc new file mode 100644 index 00000000..0cfe7718 --- /dev/null +++ b/kexi/plugins/scripting/scripts/importxhtml/ImportXHTML.rc @@ -0,0 +1,8 @@ + + + diff --git a/kexi/plugins/scripting/scripts/importxhtml/Makefile.am b/kexi/plugins/scripting/scripts/importxhtml/Makefile.am new file mode 100644 index 00000000..a0a424fa --- /dev/null +++ b/kexi/plugins/scripting/scripts/importxhtml/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/kexi/Makefile.global + +scriptsdir = $(kde_datadir)/kexi/scripts/importxhtml +scripts_SCRIPTS = ImportXHTML.py ImportXHTML.rc diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am b/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am new file mode 100644 index 00000000..9d32e165 --- /dev/null +++ b/kexi/plugins/scripting/scripts/projectdocumentor/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/kexi/Makefile.global + +scriptsdir = $(kde_datadir)/kexi/scripts/projectdocumentor +scripts_SCRIPTS = ProjectDocumentor.py ProjectDocumentor.rc diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py new file mode 100755 index 00000000..89a60301 --- /dev/null +++ b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.py @@ -0,0 +1,186 @@ +""" +Project Documentor + +Description: +This script collects various informations about a Kexi project +and exports them to a HTML file. + +Author: +Sebastian Sauer + +Copyright: +Dual-licensed under LGPL v2+higher and the BSD license. +""" + +class DataProvider: + def __init__(self): + import kexiapp + keximainwindow = kexiapp.get("KexiAppMainWindow") + + try: + self.connection = keximainwindow.getConnection() + except: + raise "No connection established. Please open the project to be documented first." + + def printConnection(self): + condata = self.connection.data() + infos = [] + for item in ("caption", "description", "driverName", "hostName", "port", "userName", "fileName", "dbPath", "localSocketFileName", "serverInfoString"): + result = getattr(condata, item)() + if result != None and result != "" and (item != "port" or result != 0): + infos.append( (item, result) ) + return infos + + def printDriver(self): + driver = self.connection.driver() + result = [ ("Version", "%s.%s" % (driver.versionMajor(),driver.versionMinor())) ] + conlist = driver.connectionsList() + if len(conlist) > 0: + result.append( ("Connections",str(conlist)) ) + return result + + def printDatabases(self): + result = [ ("Current database", self.connection.currentDatabase()) ] + dbnames = self.connection.databaseNames() + if len(dbnames) > 0: + result.append( ("Databases",str(dbnames)) ) + return result + + def printTables(self): + result = [] + for t in self.connection.tableNames(): + tableschema = self.connection.tableSchema(t) + ti = [] + for i in ("name", "caption", "description"): + v = getattr(tableschema,i)() + if v != None and v != "": + ti.append( (i,v) ) + tf = [] + for field in tableschema.fieldlist().fields(): + tfi = [] + for n in ("caption","description","type","subType","typeGroup","length","defaultValue"): + v = getattr(field,n)() + if v != None and v != "": + tfi.append( (n,v) ) + props = [] + for n in ("PrimaryKey","ForeignKey","AutoInc","UniqueKey","NotNull", "NotEmpty","Indexed","Unsigned"): + v = getattr(field,"is%s" % n)() + if v != None and v != "" and v != False and v != 0: + props.append( "%s " % n ) + if len(props) > 0: + tfi.append( ("properties",props) ) + + tf.append( (field.name(), tfi) ) + ti.append( ("fields", tf) ) + if len(ti) > 0: + result.append( (t, ti) ) + return result + + def printQueries(self): + result = [] + for q in self.connection.queryNames(): + queryschema = self.connection.querySchema(q) + qi = [] + for i in ("name", "caption", "description", "statement"): + v = getattr(queryschema,i)() + if v != None and v != "": + qi.append( (i,v) ) + if len(qi) > 0: + result.append( (q, qi) ) + return result + +class GuiApp: + def __init__(self, dataprovider): + self.dataprovider = dataprovider + + try: + import gui + except: + raise "Import of the Kross GUI module failed." + + self.dialog = gui.Dialog("Project Documentor") + + self.dialog.addLabel(self.dialog, "Save information about the project to an HTML file.") + + self.file = self.dialog.addFileChooser(self.dialog, + "File:", + gui.getHome() + "/projectdoc.html", + (('HTML files', '*.html'),('All files', '*'))) + + self.printCheckBoxes = {} + for d in dir(self.dataprovider): + if d.startswith("print"): + self.printCheckBoxes[d] = self.dialog.addCheckBox(self.dialog, d[5:], True) + + #value = getattr(self.dataprovider,d)() + #if value != None and len(value) > 0: + # f.write("

%s

" % d[5:]) + # f.write( self.toHTML(value) ) + + #self.exportProjectdetails = + #self.exportTableschemas = self.dialog.addCheckBox(self.dialog, "Table schemas", True) + #self.exportQueryschemas = self.dialog.addCheckBox(self.dialog, "Query schemas", True) + + btnframe = self.dialog.addFrame(self.dialog) + self.dialog.addButton(btnframe, "Save", self.doSave) + self.dialog.addButton(btnframe, "Cancel", self.dialog.close) + + self.dialog.show() + + def toHTML(self, value): + import types + result = "" + if isinstance(value, types.TupleType): + result += "
    " + if len(value) == 1: + result += "
  • %s
  • " % value + elif len(value) == 2: + result += "
  • %s: %s
  • " % (value[0], self.toHTML(value[1])) + elif len(value) > 2: + for item in value: + i = self.toHTML(item) + if i != "": + result += "
  • %s
  • " % i + result += "
" + elif isinstance(value, types.ListType): + for item in value: + result += "%s" % self.toHTML(item) + else: + result += "%s" % value + return result + + def doSave(self): + file = str( self.file.get() ) + print "Attempting to save project documentation to file: %s" % file + + f = open(file, "w") + + f.write("") + f.write("") + f.write("Project information") + f.write("") + f.write("") + f.write("

Project information

") + + for d in dir(self.dataprovider): + if d.startswith("print"): + print "GuiApp.doSave() CHECK %s" % d + a = self.printCheckBoxes[d] + if a and a.isChecked(): + print "GuiApp.doSave() BEGIN %s" % d + value = getattr(self.dataprovider,d)() + if value != None and len(value) > 0: + f.write("

%s

" % d[5:]) + f.write( self.toHTML(value) ) + print "GuiApp.doSave() END %s" % d + + f.close() + + print "Successfully saved project documentation to file: %s" % file + self.dialog.close() + +GuiApp( DataProvider() ) + diff --git a/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc new file mode 100644 index 00000000..bb0f6c69 --- /dev/null +++ b/kexi/plugins/scripting/scripts/projectdocumentor/ProjectDocumentor.rc @@ -0,0 +1,8 @@ + + + diff --git a/kexi/plugins/scripting/scripts/python/Makefile.am b/kexi/plugins/scripting/scripts/python/Makefile.am new file mode 100644 index 00000000..4b31c35a --- /dev/null +++ b/kexi/plugins/scripting/scripts/python/Makefile.am @@ -0,0 +1,2 @@ +include $(top_srcdir)/kexi/Makefile.global +SUBDIRS = kexiapp diff --git a/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am b/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am new file mode 100644 index 00000000..f0f0492d --- /dev/null +++ b/kexi/plugins/scripting/scripts/python/kexiapp/Makefile.am @@ -0,0 +1,2 @@ +kexiapppythondir = $(kde_datadir)/kexi/kross/python/kexiapp +kexiapppython_SCRIPTS = __init__.py diff --git a/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py b/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py new file mode 100755 index 00000000..b5224304 --- /dev/null +++ b/kexi/plugins/scripting/scripts/python/kexiapp/__init__.py @@ -0,0 +1,25 @@ +""" +Initializer for the krosskexiapp-module. + +Description: +This module provides the entry-point for python scripts +to work with a running Kexi application instance. + +Author: +Sebastian Sauer + +Copyright: +Dual-licensed under LGPL v2+higher and the BSD license. +""" + +try: + import krosskexiapp +except ImportError, e: + raise "Import of the Kross KexiApp module failed.\n%s" % e + +def get(modulename): + return krosskexiapp.get(modulename) + +def currentConnection(): + mainwindow = krosskexiapp.get("KexiAppMainWindow") + return mainwindow.getConnection() -- cgit v1.2.1