summaryrefslogtreecommitdiffstats
path: root/kexi/core/kexiproject.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /kexi/core/kexiproject.cpp
downloadkoffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz
koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kexi/core/kexiproject.cpp')
-rw-r--r--kexi/core/kexiproject.cpp1023
1 files changed, 1023 insertions, 0 deletions
diff --git a/kexi/core/kexiproject.cpp b/kexi/core/kexiproject.cpp
new file mode 100644
index 000000000..741fb67a4
--- /dev/null
+++ b/kexi/core/kexiproject.cpp
@@ -0,0 +1,1023 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
+ Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#include <qfile.h>
+#include <qapplication.h>
+#include <qdom.h>
+
+#include <kmimetype.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kexiutils/identifier.h>
+
+#include <kexidb/connection.h>
+#include <kexidb/cursor.h>
+#include <kexidb/driver.h>
+#include <kexidb/drivermanager.h>
+#include <kexidb/utils.h>
+#include <kexidb/parser/parser.h>
+#include <kexidb/msghandler.h>
+#include <kexidb/dbproperties.h>
+#include <kexiutils/utils.h>
+
+#include "kexiproject.h"
+#include "kexipartmanager.h"
+#include "kexipartitem.h"
+#include "kexipartinfo.h"
+#include "kexipart.h"
+#include "kexidialogbase.h"
+#include "kexi.h"
+#include "keximainwindow.h"
+#include "kexiblobbuffer.h"
+#include "kexiguimsghandler.h"
+
+#include <assert.h>
+
+class KexiProject::Private
+{
+ public:
+ Private()
+ : data(0)
+ , itemDictsCache(199)
+ , unstoredItems(199)
+ , tempPartItemID_Counter(-1)
+ , sqlParser(0)
+ , versionMajor(0)
+ , versionMinor(0)
+ {
+ itemDictsCache.setAutoDelete(true);
+ unstoredItems.setAutoDelete(true);
+ }
+ ~Private() {
+ delete data;
+ data=0;
+ delete sqlParser;
+ }
+
+ QGuardedPtr<KexiDB::Connection> connection;
+ QGuardedPtr<KexiProjectData> data;
+
+ QString error_title;
+
+ //! a cache for item() method, indexed by project part's ids
+ QIntDict<KexiPart::ItemDict> itemDictsCache;
+
+ QPtrDict<KexiPart::Item> unstoredItems;
+ int tempPartItemID_Counter; //!< helper for getting unique
+ //!< temporary identifiers for unstored items
+ KexiDB::Parser* sqlParser;
+
+ int versionMajor;
+ int versionMinor;
+};
+
+//---------------------------
+
+/*
+ Helper for setting temporary error title.
+class KexiProject::ErrorTitle
+{
+ public:
+ ErrorTitle(KexiProject* p, const QString& msg = QString::null)
+ : prj(p)
+ , prev_err_title(p->m_error_title)
+ {
+ p->m_error_title = msg;
+ }
+ ~ErrorTitle()
+ {
+ prj->m_error_title = prev_err_title;
+ }
+ KexiProject* prj;
+ QString prev_err_title;
+};*/
+
+KexiProject::KexiProject(KexiProjectData *pdata, KexiDB::MessageHandler* handler)
+ : QObject(), Object(handler)
+ , d(new Private())
+{
+ d->data = pdata;
+//! @todo partmanager is outside project, so can be initialised just once:
+ Kexi::partManager().lookup();
+}
+
+KexiProject::KexiProject(KexiProjectData *pdata, KexiDB::MessageHandler* handler,
+ KexiDB::Connection* conn)
+ : QObject(), Object(handler)
+ , d(new Private())
+{
+ d->data = pdata;
+ if (d->data->connectionData() == d->connection->data())
+ d->connection = conn;
+ else
+ kdWarning() << "KexiProject::KexiProject(): passed connection's data ("
+ << conn->data()->serverInfoString() << ") is not compatible with project's conn. data ("
+ << d->data->connectionData()->serverInfoString() << ")" << endl;
+//! @todo partmanager is outside project, so can be initialised just once:
+ Kexi::partManager().lookup();
+}
+
+KexiProject::~KexiProject()
+{
+ closeConnection();
+ delete d;
+}
+
+KexiDB::Connection *KexiProject::dbConnection() const
+{
+ return d->connection;
+}
+
+KexiProjectData* KexiProject::data() const
+{
+ return d->data;
+}
+
+int KexiProject::versionMajor() const
+{
+ return d->versionMajor;
+}
+
+int KexiProject::versionMinor() const
+{
+ return d->versionMinor;
+}
+
+tristate
+KexiProject::open(bool &incompatibleWithKexi)
+{
+ return openInternal(&incompatibleWithKexi);
+}
+
+tristate
+KexiProject::open()
+{
+ return openInternal(0);
+}
+
+tristate
+KexiProject::openInternal(bool *incompatibleWithKexi)
+{
+ if (incompatibleWithKexi)
+ *incompatibleWithKexi = false;
+ kdDebug() << "KexiProject::open(): " << d->data->databaseName() <<" "<< d->data->connectionData()->driverName << endl;
+ KexiDB::MessageTitle et(this,
+ i18n("Could not open project \"%1\".").arg(d->data->databaseName()));
+
+ if (!createConnection()) {
+ kdDebug() << "KexiProject::open(): !createConnection()" << endl;
+ return false;
+ }
+ bool cancel = false;
+ KexiGUIMessageHandler msgHandler;
+ if (!d->connection->useDatabase(d->data->databaseName(), true, &cancel, &msgHandler))
+ {
+ if (cancel) {
+ return cancelled;
+ }
+ kdDebug() << "KexiProject::open(): !d->connection->useDatabase() "
+ << d->data->databaseName() <<" "<< d->data->connectionData()->driverName << endl;
+
+ if (d->connection->errorNum() == ERR_NO_DB_PROPERTY) {
+//<temp>
+//! @todo this is temporary workaround as we have no import driver for SQLite
+ if (/*supported?*/ !d->data->connectionData()->driverName.lower().startsWith("sqlite")) {
+//</temp>
+ if (incompatibleWithKexi)
+ *incompatibleWithKexi = true;
+ }
+ else
+ setError(d->connection);
+ closeConnection();
+ return false;
+ }
+
+ setError(d->connection);
+ closeConnection();
+ return false;
+ }
+
+ if (!initProject())
+ return false;
+
+ return createInternalStructures(/*insideTransaction*/true);
+}
+
+tristate
+KexiProject::create(bool forceOverwrite)
+{
+ KexiDB::MessageTitle et(this,
+ i18n("Could not create project \"%1\".").arg(d->data->databaseName()));
+
+ if (!createConnection())
+ return false;
+ if (!checkWritable())
+ return false;
+ if (d->connection->databaseExists( d->data->databaseName() )) {
+ if (!forceOverwrite)
+ return cancelled;
+ if (!d->connection->dropDatabase( d->data->databaseName() )) {
+ setError(d->connection);
+ closeConnection();
+ return false;
+ }
+ kdDebug() << "--- DB '" << d->data->databaseName() << "' dropped ---"<< endl;
+ }
+ if (!d->connection->createDatabase( d->data->databaseName() )) {
+ setError(d->connection);
+ closeConnection();
+ return false;
+ }
+ kdDebug() << "--- DB '" << d->data->databaseName() << "' created ---"<< endl;
+ // and now: open
+ if (!d->connection->useDatabase(d->data->databaseName()))
+ {
+ kdDebug() << "--- DB '" << d->data->databaseName() << "' USE ERROR ---"<< endl;
+ setError(d->connection);
+ closeConnection();
+ return false;
+ }
+ kdDebug() << "--- DB '" << d->data->databaseName() << "' used ---"<< endl;
+
+ //<add some data>
+ KexiDB::Transaction trans = d->connection->beginTransaction();
+ if (trans.isNull())
+ return false;
+
+ if (!createInternalStructures(/*!insideTransaction*/false))
+ return false;
+
+ //add some metadata
+//! @todo put more props. todo - creator, created date, etc. (also to KexiProjectData)
+ KexiDB::DatabaseProperties &props = d->connection->databaseProperties();
+ if (!props.setValue("kexiproject_major_ver", d->versionMajor)
+ || !props.setCaption("kexiproject_major_ver", i18n("Project major version"))
+ || !props.setValue("kexiproject_minor_ver", d->versionMinor)
+ || !props.setCaption("kexiproject_minor_ver", i18n("Project minor version"))
+ || !props.setValue("project_caption", d->data->caption())
+ || !props.setCaption("project_caption", i18n("Project caption"))
+ || !props.setValue("project_desc", d->data->description())
+ || !props.setCaption("project_desc", i18n("Project description")) )
+ return false;
+
+/* KexiDB::TableSchema *t_db = d->connection->tableSchema("kexi__db");
+ //caption:
+ if (!t_db)
+ return false;
+
+ if (!KexiDB::replaceRow(*d->connection, t_db, "db_property", "project_caption",
+ "db_value", QVariant( d->data->caption() ), KexiDB::Field::Text)
+ || !KexiDB::replaceRow(*d->connection, t_db, "db_property", "project_desc",
+ "db_value", QVariant( d->data->description() ), KexiDB::Field::Text) )
+ return false;
+*/
+ if (trans.active() && !d->connection->commitTransaction(trans))
+ return false;
+ //</add some data>
+
+ return initProject();
+}
+
+bool KexiProject::createInternalStructures(bool insideTransaction)
+{
+ KexiDB::TransactionGuard tg;
+ if (insideTransaction) {
+ tg.setTransaction( d->connection->beginTransaction() );
+ if (tg.transaction().isNull())
+ return false;
+ }
+
+ //Get information about kexiproject version.
+ //kexiproject version is a version of data layer above kexidb layer.
+ KexiDB::DatabaseProperties &props = d->connection->databaseProperties();
+ bool ok;
+ int storedMajorVersion = props.value("kexiproject_major_ver").toInt(&ok);
+ if (!ok)
+ storedMajorVersion = 0;
+ int storedMinorVersion = props.value("kexiproject_minor_ver").toInt(&ok);
+ if (!ok)
+ storedMinorVersion = 1;
+
+ bool containsKexi__blobsTable = d->connection->drv_containsTable("kexi__blobs");
+ int dummy;
+ bool contains_o_folder_id = containsKexi__blobsTable && true == d->connection->querySingleNumber(
+ "SELECT COUNT(o_folder_id) FROM kexi__blobs", dummy, 0, false/*addLimitTo1*/);
+ bool add_folder_id_column = false;
+
+//! @todo what about read-only db access?
+ if (storedMajorVersion<=0) {
+ d->versionMajor = KEXIPROJECT_VERSION_MAJOR;
+ d->versionMinor = KEXIPROJECT_VERSION_MINOR;
+ //For compatibility for projects created before Kexi 1.0 beta 1:
+ //1. no kexiproject_major_ver and kexiproject_minor_ver -> add them
+ if (!d->connection->isReadOnly()) {
+ if (!props.setValue("kexiproject_major_ver", d->versionMajor)
+ || !props.setCaption("kexiproject_major_ver", i18n("Project major version"))
+ || !props.setValue("kexiproject_minor_ver", d->versionMinor)
+ || !props.setCaption("kexiproject_minor_ver", i18n("Project minor version")) ) {
+ return false;
+ }
+ }
+
+ if (containsKexi__blobsTable) {
+//! @todo what to do for readonly connections? Should we alter kexi__blobs in memory?
+ if (!d->connection->isReadOnly()) {
+ if (!contains_o_folder_id) {
+ add_folder_id_column = true;
+ }
+ }
+ }
+ }
+ if (storedMajorVersion!=d->versionMajor || storedMajorVersion!=d->versionMinor) {
+ //! @todo version differs: should we change something?
+ d->versionMajor = storedMajorVersion;
+ d->versionMinor = storedMinorVersion;
+ }
+
+ KexiDB::InternalTableSchema *t_blobs = new KexiDB::InternalTableSchema("kexi__blobs");
+ t_blobs->addField( new KexiDB::Field("o_id", KexiDB::Field::Integer,
+ KexiDB::Field::PrimaryKey | KexiDB::Field::AutoInc, KexiDB::Field::Unsigned) )
+ .addField( new KexiDB::Field("o_data", KexiDB::Field::BLOB) )
+ .addField( new KexiDB::Field("o_name", KexiDB::Field::Text ) )
+ .addField( new KexiDB::Field("o_caption", KexiDB::Field::Text ) )
+ .addField( new KexiDB::Field("o_mime", KexiDB::Field::Text, KexiDB::Field::NotNull) )
+ .addField( new KexiDB::Field("o_folder_id",
+ KexiDB::Field::Integer, 0, KexiDB::Field::Unsigned) //references kexi__gallery_folders.f_id
+ //If null, the BLOB only points to virtual "All" folder
+ //WILL BE USED in Kexi >=2.0
+ );
+
+ //*** create global BLOB container, if not present
+ if (containsKexi__blobsTable) {
+ //! just insert this schema
+ d->connection->insertInternalTableSchema(t_blobs);
+ if (add_folder_id_column && !d->connection->isReadOnly()) {
+ // 2. "kexi__blobs" table contains no "o_folder_id" column -> add it
+ // (by copying table to avoid data loss)
+ KexiDB::TableSchema *kexi__blobsCopy = new KexiDB::TableSchema( *t_blobs );
+ kexi__blobsCopy->setName("kexi__blobs__copy");
+ if (!d->connection->drv_createTable( *kexi__blobsCopy )) {
+ delete kexi__blobsCopy;
+ delete t_blobs;
+ return false;
+ }
+ // 2.1 copy data (insert 0's into o_folder_id column)
+ if (!d->connection->executeSQL(
+ QString::fromLatin1("INSERT INTO kexi__blobs (o_data, o_name, o_caption, o_mime, o_folder_id) "
+ "SELECT o_data, o_name, o_caption, o_mime, 0 FROM kexi__blobs") )
+ // 2.2 remove the original kexi__blobs
+ || !d->connection->executeSQL(QString::fromLatin1("DROP TABLE kexi__blobs")) //lowlevel
+ // 2.3 rename the copy back into kexi__blobs
+ || !d->connection->drv_alterTableName(*kexi__blobsCopy, "kexi__blobs")
+ )
+ {
+ //(no need to drop the copy, ROLLBACK will drop it)
+ delete kexi__blobsCopy;
+ delete t_blobs;
+ return false;
+ }
+ delete kexi__blobsCopy; //not needed - physically renamed to kexi_blobs
+ }
+ }
+ else {
+// if (!d->connection->createTable( t_blobs, false/*!replaceExisting*/ )) {
+ if (!d->connection->isReadOnly()) {
+ if (!d->connection->createTable( t_blobs, true/*replaceExisting*/ )) {
+ delete t_blobs;
+ return false;
+ }
+ }
+ }
+
+ //Store default part infos.
+ //Infos for other parts (forms, reports...) are created on demand in KexiDialogBase::storeNewData()
+ KexiDB::InternalTableSchema *t_parts = new KexiDB::InternalTableSchema("kexi__parts"); //newKexiDBSystemTableSchema("kexi__parts");
+ t_parts->addField(
+ new KexiDB::Field("p_id", KexiDB::Field::Integer, KexiDB::Field::PrimaryKey | KexiDB::Field::AutoInc, KexiDB::Field::Unsigned)
+ )
+ .addField( new KexiDB::Field("p_name", KexiDB::Field::Text) )
+ .addField( new KexiDB::Field("p_mime", KexiDB::Field::Text ) )
+ .addField( new KexiDB::Field("p_url", KexiDB::Field::Text ) );
+
+ bool containsKexi__partsTable = d->connection->drv_containsTable("kexi__parts");
+ bool partsTableOk = true;
+ if (containsKexi__partsTable) {
+ //! just insert this schema
+ d->connection->insertInternalTableSchema(t_parts);
+ }
+ else {
+ if (!d->connection->isReadOnly()) {
+ partsTableOk = d->connection->createTable( t_parts, true/*replaceExisting*/ );
+
+ KexiDB::FieldList *fl = t_parts->subList("p_id", "p_name", "p_mime", "p_url");
+ if (partsTableOk)
+ partsTableOk = d->connection->insertRecord(*fl, QVariant(1), QVariant("Tables"),
+ QVariant("kexi/table"), QVariant("http://koffice.org/kexi/"));
+
+ if (partsTableOk)
+ partsTableOk = d->connection->insertRecord(*fl, QVariant(2), QVariant("Queries"),
+ QVariant("kexi/query"), QVariant("http://koffice.org/kexi/"));
+ }
+ }
+
+ if (!partsTableOk) {
+ delete t_parts;
+ return false;
+ }
+
+ if (insideTransaction) {
+ if (tg.transaction().active() && !tg.commit())
+ return false;
+ }
+ return true;
+}
+
+bool
+KexiProject::createConnection()
+{
+ if (d->connection)
+ return true;
+
+ clearError();
+// closeConnection();//for sanity
+ KexiDB::MessageTitle et(this);
+
+ KexiDB::Driver *driver = Kexi::driverManager().driver(d->data->connectionData()->driverName);
+ if(!driver) {
+ setError(&Kexi::driverManager());
+ return false;
+ }
+
+ int connectionOptions = 0;
+ if (d->data->isReadOnly())
+ connectionOptions |= KexiDB::Driver::ReadOnlyConnection;
+ d->connection = driver->createConnection(*d->data->connectionData(), connectionOptions);
+ if (!d->connection)
+ {
+ kdDebug() << "KexiProject::open(): uuups failed " << driver->errorMsg() << endl;
+ setError(driver);
+ return false;
+ }
+
+ if (!d->connection->connect())
+ {
+ setError(d->connection);
+ kdDebug() << "KexiProject::createConnection(): error connecting: " << (d->connection ? d->connection->errorMsg() : QString::null) << endl;
+ closeConnection();
+ return false;
+ }
+
+ //re-init BLOB buffer
+//! @todo won't work for subsequent connection
+ KexiBLOBBuffer::setConnection(d->connection);
+ return true;
+}
+
+
+bool
+KexiProject::closeConnection()
+{
+ if (!d->connection)
+ return true;
+
+ if (!d->connection->disconnect()) {
+ setError(d->connection);
+ return false;
+ }
+
+ delete d->connection; //this will also clear connection for BLOB buffer
+ d->connection = 0;
+ return true;
+}
+
+bool
+KexiProject::initProject()
+{
+// emit dbAvailable();
+ kdDebug() << "KexiProject::open(): checking project parts..." << endl;
+
+ if (!Kexi::partManager().checkProject(d->connection)) {
+ setError(Kexi::partManager().error() ? (KexiDB::Object*)&Kexi::partManager() : (KexiDB::Connection*)d->connection);
+ return false;
+ }
+
+// !@todo put more props. todo - creator, created date, etc. (also to KexiProjectData)
+ KexiDB::DatabaseProperties &props = d->connection->databaseProperties();
+ QString str( props.value("project_caption").toString() );
+ if (!str.isEmpty())
+ d->data->setCaption( str );
+ str = props.value("project_desc").toString();
+ if (!str.isEmpty())
+ d->data->setDescription( str );
+/* KexiDB::RowData data;
+ QString sql = "select db_value from kexi__db where db_property='%1'";
+ if (d->connection->querySingleRecord( sql.arg("project_caption"), data ) && !data[0].toString().isEmpty())
+ d->data->setCaption(data[0].toString());
+ if (d->connection->querySingleRecord( sql.arg("project_desc"), data) && !data[0].toString().isEmpty())
+ d->data->setDescription(data[0].toString());*/
+
+ return true;
+}
+
+bool
+KexiProject::isConnected()
+{
+ if(d->connection && d->connection->isDatabaseUsed())
+ return true;
+
+ return false;
+}
+
+KexiPart::ItemDict*
+KexiProject::items(KexiPart::Info *i)
+{
+ kdDebug() << "KexiProject::items()" << endl;
+ if(!i || !isConnected())
+ return 0;
+
+ //trying in cache...
+ KexiPart::ItemDict *dict = d->itemDictsCache[ i->projectPartID() ];
+ if (dict)
+ return dict;
+ //retrieve:
+ KexiDB::Cursor *cursor = d->connection->executeQuery(
+ "SELECT o_id, o_name, o_caption FROM kexi__objects WHERE o_type = "
+ + QString::number(i->projectPartID()));//, KexiDB::Cursor::Buffered);
+// kdDebug() << "KexiProject::items(): cursor handle is:" << cursor << endl;
+ if(!cursor)
+ return 0;
+
+ dict = new KexiPart::ItemDict(1009);
+ dict->setAutoDelete(true);
+
+ for(cursor->moveFirst(); !cursor->eof(); cursor->moveNext())
+ {
+ KexiPart::Item *it = new KexiPart::Item();
+ bool ok;
+ int ident = cursor->value(0).toInt(&ok);
+ QString objName( cursor->value(1).toString() );
+
+ if ( ok && (ident>0) && !d->connection->isInternalTableSchema(objName)
+ && KexiUtils::isIdentifier(objName) )
+ {
+ it->setIdentifier(ident);
+ it->setMimeType(i->mimeType()); //js: may be not null???
+ it->setName(objName);
+ it->setCaption(cursor->value(2).toString());
+ }
+ dict->insert(it->identifier(), it);
+// kdDebug() << "KexiProject::items(): ITEM ADDED == "<<objName <<" id="<<ident<<endl;
+ }
+
+ d->connection->deleteCursor(cursor);
+// kdDebug() << "KexiProject::items(): end with count " << dict->count() << endl;
+ d->itemDictsCache.insert( i->projectPartID(), dict );
+ return dict;
+}
+
+KexiPart::ItemDict*
+KexiProject::itemsForMimeType(const QCString &mimeType)
+{
+ KexiPart::Info *info = Kexi::partManager().infoForMimeType(mimeType);
+ return items(info);
+}
+
+void
+KexiProject::getSortedItems(KexiPart::ItemList& list, KexiPart::Info *i)
+{
+ list.clear();
+ KexiPart::ItemDict* dict = items(i);
+ if (!dict)
+ return;
+ for (KexiPart::ItemDictIterator it(*dict); it.current(); ++it)
+ list.append(it.current());
+}
+
+void
+KexiProject::getSortedItemsForMimeType(KexiPart::ItemList& list, const QCString &mimeType)
+{
+ KexiPart::Info *info = Kexi::partManager().infoForMimeType(mimeType);
+ getSortedItems(list, info);
+}
+
+void
+KexiProject::addStoredItem(KexiPart::Info *info, KexiPart::Item *item)
+{
+ if (!info || !item)
+ return;
+ KexiPart::ItemDict *dict = items(info);
+ item->setNeverSaved( false );
+ d->unstoredItems.take(item); //no longer unstored
+ dict->insert( item->identifier(), item );
+ //let's update e.g. navigator
+ emit newItemStored(*item);
+}
+
+KexiPart::Item*
+KexiProject::itemForMimeType(const QCString &mimeType, const QString &name)
+{
+ KexiPart::ItemDict *dict = itemsForMimeType(mimeType);
+ if (!dict)
+ return 0;
+ const QString l_name = name.lower();
+ for (KexiPart::ItemDictIterator it( *dict ); it.current(); ++it) {
+ if (it.current()->name().lower()==l_name)
+ return it.current();
+ }
+ return 0;
+}
+
+KexiPart::Item*
+KexiProject::item(KexiPart::Info *i, const QString &name)
+{
+ KexiPart::ItemDict *dict = items(i);
+ if (!dict)
+ return 0;
+ const QString l_name = name.lower();
+ for (KexiPart::ItemDictIterator it( *dict ); it.current(); ++it) {
+ if (it.current()->name().lower()==l_name)
+ return it.current();
+ }
+ return 0;
+}
+
+KexiPart::Item*
+KexiProject::item(int identifier)
+{
+ KexiPart::ItemDict *dict;
+ for (QIntDictIterator<KexiPart::ItemDict> it(d->itemDictsCache); (dict = it.current()); ++it) {
+ KexiPart::Item *item = dict->find(identifier);
+ if (item)
+ return item;
+ }
+ return 0;
+}
+
+/*void KexiProject::clearMsg()
+{
+ clearError();
+// d->error_title=QString::null;
+}
+
+void KexiProject::setError(int code, const QString &msg )
+{
+ Object::setError(code, msg);
+ if (Object::error())
+ ERRMSG(d->error_title, this);
+// emit error(d->error_title, this);
+}
+
+
+void KexiProject::setError( const QString &msg )
+{
+ Object::setError(msg);
+ if (Object::error())
+ ERRMSG(d->error_title, this);
+// emit error(d->error_title, this);
+}
+
+void KexiProject::setError( KexiDB::Object *obj )
+{
+ if (!obj)
+ return;
+ Object::setError(obj);
+ if (Object::error())
+ ERRMSG(d->error_title, obj);
+// emit error(d->error_title, obj);
+}
+
+void KexiProject::setError(const QString &msg, const QString &desc)
+{
+ Object::setError(msg); //ok?
+ ERRMSG(msg, desc); //not KexiDB-related
+// emit error(msg, desc); //not KexiDB-related
+}
+*/
+
+KexiPart::Part *KexiProject::findPartFor(KexiPart::Item& item)
+{
+ clearError();
+ KexiDB::MessageTitle et(this);
+ KexiPart::Part *part = Kexi::partManager().partForMimeType(item.mimeType());
+ if (!part)
+ setError(&Kexi::partManager());
+ return part;
+}
+
+KexiDialogBase* KexiProject::openObject(KexiMainWindow *wnd, KexiPart::Item& item,
+ int viewMode, QMap<QString,QString>* staticObjectArgs)
+{
+ clearError();
+ if (viewMode!=Kexi::DataViewMode && data()->userMode())
+ return 0;
+
+ KexiDB::MessageTitle et(this);
+ KexiPart::Part *part = findPartFor(item);
+ if (!part)
+ return 0;
+ KexiDialogBase *dlg = part->openInstance(wnd, item, viewMode, staticObjectArgs);
+ if (!dlg) {
+ if (part->lastOperationStatus().error())
+ setError(i18n("Opening object \"%1\" failed.").arg(item.name())+"<br>"
+ +part->lastOperationStatus().message,
+ part->lastOperationStatus().description);
+ return 0;
+ }
+ return dlg;
+}
+
+KexiDialogBase* KexiProject::openObject(KexiMainWindow *wnd, const QCString &mimeType,
+ const QString& name, int viewMode)
+{
+ KexiPart::Item *it = itemForMimeType(mimeType, name);
+ return it ? openObject(wnd, *it, viewMode) : 0;
+}
+
+bool KexiProject::checkWritable()
+{
+ if (!d->connection->isReadOnly())
+ return true;
+ setError(i18n("This project is opened as read only."));
+ return false;
+}
+
+bool KexiProject::removeObject(KexiMainWindow *wnd, KexiPart::Item& item)
+{
+ clearError();
+ if (data()->userMode())
+ return false;
+
+ KexiDB::MessageTitle et(this);
+ if (!checkWritable())
+ return false;
+ KexiPart::Part *part = findPartFor(item);
+ if (!part)
+ return false;
+ if (!item.neverSaved() && !part->remove(wnd, item)) {
+ //js TODO check for errors
+ return false;
+ }
+ if (!item.neverSaved()) {
+ KexiDB::TransactionGuard tg( *d->connection );
+ if (!tg.transaction().active()) {
+ setError(d->connection);
+ return false;
+ }
+ if (!d->connection->removeObject( item.identifier() )) {
+ setError(d->connection);
+ return false;
+ }
+ if (!tg.commit()) {
+ setError(d->connection);
+ return false;
+ }
+ }
+ emit itemRemoved(item);
+
+ //now: remove this item from cache
+ if (part->info()) {
+ KexiPart::ItemDict *dict = d->itemDictsCache[ part->info()->projectPartID() ];
+ if (!(dict && dict->remove( item.identifier() )))
+ d->unstoredItems.remove(&item);//remove temp.
+ }
+ return true;
+}
+
+bool KexiProject::renameObject( KexiMainWindow *wnd, KexiPart::Item& item, const QString& _newName )
+{
+ clearError();
+ if (data()->userMode())
+ return 0;
+
+ KexiUtils::WaitCursor wait;
+ QString newName = _newName.stripWhiteSpace();
+ {
+ KexiDB::MessageTitle et(this);
+ if (newName.isEmpty()) {
+ setError( i18n("Could not set empty name for this object.") );
+ return false;
+ }
+ if (this->itemForMimeType(item.mimeType(), newName)!=0) {
+ setError( i18n("Could not use this name. Object with name \"%1\" already exists.")
+ .arg(newName) );
+ return false;
+ }
+ }
+
+ KexiDB::MessageTitle et(this,
+ i18n("Could not rename object \"%1\".").arg(item.name()) );
+ if (!checkWritable())
+ return false;
+ KexiPart::Part *part = findPartFor(item);
+ if (!part)
+ return false;
+ KexiDB::TransactionGuard tg( *d->connection );
+ if (!tg.transaction().active()) {
+ setError(d->connection);
+ return false;
+ }
+ if (!part->rename(wnd, item, newName)) {
+ setError(part->lastOperationStatus().message, part->lastOperationStatus().description);
+ return false;
+ }
+ if (!d->connection->executeSQL( "update kexi__objects set o_name="
+ + d->connection->driver()->valueToSQL( KexiDB::Field::Text, newName )
+ + " where o_id=" + QString::number(item.identifier()) )) {
+ setError(d->connection);
+ return false;
+ }
+ if (!tg.commit()) {
+ setError(d->connection);
+ return false;
+ }
+ QCString oldName( item.name().latin1() );
+ item.setName( newName );
+ emit itemRenamed(item, oldName);
+ return true;
+}
+
+KexiPart::Item* KexiProject::createPartItem(KexiPart::Info *info, const QString& suggestedCaption)
+{
+ clearError();
+ if (data()->userMode())
+ return 0;
+
+ KexiDB::MessageTitle et(this);
+ KexiPart::Part *part = Kexi::partManager().part(info);
+ if (!part) {
+ setError(&Kexi::partManager());
+ return 0;
+ }
+
+ KexiPart::ItemDict *dict = items(info);
+
+ //find new, unique default name for this item
+ int n;
+ QString new_name;
+ QString base_name;
+ if (suggestedCaption.isEmpty()) {
+ n = 1;
+ base_name = part->instanceName();
+ }
+ else {
+ n = 0; //means: try not to add 'n'
+ base_name = KexiUtils::string2Identifier(suggestedCaption).lower();
+ }
+ base_name = KexiUtils::string2Identifier(base_name).lower();
+ KexiPart::ItemDictIterator it(*dict);
+ QPtrDictIterator<KexiPart::Item> itUnstored(d->unstoredItems);
+ do {
+ new_name = base_name;
+ if (n>=1)
+ new_name += QString::number(n);
+ for (it.toFirst(); it.current(); ++it) {
+ if (it.current()->name().lower()==new_name)
+ break;
+ }
+ if ( it.current() ) {
+ n++;
+ continue; //stored exists!
+ }
+ for (itUnstored.toFirst(); itUnstored.current(); ++itUnstored) {
+ if (itUnstored.current()->name().lower()==new_name)
+ break;
+ }
+ if ( !itUnstored.current() )
+ break; //unstored doesn't exist
+ n++;
+ } while (n<1000/*sanity*/);
+
+ if (n>=1000)
+ return 0;
+
+ QString new_caption( suggestedCaption.isEmpty() ? part->instanceCaption() : suggestedCaption);
+ if (n>=1)
+ new_caption += QString::number(n);
+
+ KexiPart::Item *item = new KexiPart::Item();
+ item->setIdentifier( --d->tempPartItemID_Counter );//temporary
+ item->setMimeType(info->mimeType());
+ item->setName(new_name);
+ item->setCaption(new_caption);
+ item->setNeverSaved(true);
+ d->unstoredItems.insert(item, item);
+ return item;
+}
+
+KexiPart::Item* KexiProject::createPartItem(KexiPart::Part *part, const QString& suggestedCaption)
+{
+ return createPartItem(part->info(), suggestedCaption);
+}
+
+void KexiProject::deleteUnstoredItem(KexiPart::Item *item)
+{
+ if (!item)
+ return;
+ d->unstoredItems.remove(item);
+}
+
+KexiDB::Parser* KexiProject::sqlParser()
+{
+ if (!d->sqlParser) {
+ if (!d->connection)
+ return 0;
+ d->sqlParser = new KexiDB::Parser(d->connection);
+ }
+ return d->sqlParser;
+}
+
+static const QString warningNoUndo = i18n("Warning: entire project's data will be removed.");
+
+/*static*/
+KexiProject*
+KexiProject::createBlankProject(bool &cancelled, KexiProjectData* data,
+ KexiDB::MessageHandler* handler)
+{
+ cancelled = false;
+ KexiProject *prj = new KexiProject( new KexiProjectData(*data), handler );
+
+ tristate res = prj->create(false);
+ if (~res) {
+//! @todo move to KexiMessageHandler
+ if (KMessageBox::Yes != KMessageBox::warningYesNo(0, "<qt>"+i18n(
+ "The project %1 already exists.\n"
+ "Do you want to replace it with a new, blank one?")
+ .arg(prj->data()->infoString())+"\n"+warningNoUndo+"</qt>",
+ QString::null, KGuiItem(i18n("Replace")), KStdGuiItem::cancel() ))
+//todo add serverInfoString() for server-based prj
+ {
+ delete prj;
+ cancelled = true;
+ return 0;
+ }
+ res = prj->create(true/*overwrite*/);
+ }
+ if (res != true) {
+ delete prj;
+ return 0;
+ }
+ kdDebug() << "KexiProject::createBlankProject(): new project created --- " << endl;
+//todo? Kexi::recentProjects().addProjectData( data );
+
+ return prj;
+}
+
+/*static*/
+tristate KexiProject::dropProject(KexiProjectData* data,
+ KexiDB::MessageHandler* handler, bool dontAsk)
+{
+ if (!dontAsk && KMessageBox::Yes != KMessageBox::warningYesNo(0,
+ i18n("Do you want to drop the project \"%1\"?").arg(data->objectName())+"\n"+warningNoUndo ))
+ return cancelled;
+
+ KexiProject prj( new KexiProjectData(*data), handler );
+ if (!prj.open())
+ return false;
+
+ if (prj.dbConnection()->isReadOnly()) {
+ handler->showErrorMessage(
+ i18n("Could not drop this project. Database connection for this project has been opened as read only."));
+ return false;
+ }
+
+ return prj.dbConnection()->dropDatabase();
+}
+
+/*void KexiProject::reloadPartItem( KexiDialogBase* dialog )
+{
+ if (!dialog)
+ return;
+
+ KexiPart::Item* item = dialog->partItem();
+
+ if (dialog || !d->connection->setQuerySchemaObsolete( queryName ))
+ return;
+ KexiPart::Info *partInfo = Kexi::partManager().infoForMimeType("kexi/query");
+ if (!partInfo)
+ return; //err?
+ item(partInfo, queryName);
+ if (!item)
+ return; //err?
+ emit itemSetO
+
+}*/
+
+#include "kexiproject.moc"