kexi

kexiproject.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003 Lucijan Busch <lucijan@kde.org>
00003    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include <qfile.h>
00022 #include <qapplication.h>
00023 #include <qdom.h>
00024 
00025 #include <kmimetype.h>
00026 #include <kdebug.h>
00027 #include <klocale.h>
00028 
00029 #include <kexiutils/identifier.h>
00030 
00031 #include <kexidb/connection.h>
00032 #include <kexidb/cursor.h>
00033 #include <kexidb/driver.h>
00034 #include <kexidb/drivermanager.h>
00035 #include <kexidb/utils.h>
00036 #include <kexidb/parser/parser.h>
00037 #include <kexidb/msghandler.h>
00038 #include <kexidb/dbproperties.h>
00039 #include <kexiutils/utils.h>
00040 
00041 #include "kexiproject.h"
00042 #include "kexipartmanager.h"
00043 #include "kexipartitem.h"
00044 #include "kexipartinfo.h"
00045 #include "kexipart.h"
00046 #include "kexidialogbase.h"
00047 #include "kexi.h"
00048 #include "keximainwindow.h"
00049 #include "kexiblobbuffer.h"
00050 #include "kexiguimsghandler.h"
00051 
00052 #include <assert.h>
00053 
00054 class KexiProject::Private
00055 {
00056     public:
00057         Private()
00058          : data(0)
00059          , itemDictsCache(199)
00060          , unstoredItems(199)
00061          , tempPartItemID_Counter(-1)
00062          , sqlParser(0)
00063          , versionMajor(0)
00064          , versionMinor(0)
00065          , final(false)
00066         {
00067             itemDictsCache.setAutoDelete(true);
00068             unstoredItems.setAutoDelete(true);
00069         }
00070         ~Private() {
00071             delete data;
00072             data=0;
00073             delete sqlParser;
00074         }
00075 
00076         QGuardedPtr<KexiDB::Connection> connection;
00077         QGuardedPtr<KexiProjectData> data;
00078         
00079         QString error_title;
00080 
00082         QIntDict<KexiPart::ItemDict> itemDictsCache;
00083 
00084         QPtrDict<KexiPart::Item> unstoredItems;
00085         int tempPartItemID_Counter; 
00086 
00087         KexiDB::Parser* sqlParser;
00088 
00089         int versionMajor;
00090         int versionMinor;
00091         bool final : 1;
00092 };
00093 
00094 //---------------------------
00095 
00096 /*
00097  Helper for setting temporary error title. 
00098 class KexiProject::ErrorTitle
00099 {
00100     public:
00101     ErrorTitle(KexiProject* p, const QString& msg = QString::null)
00102         : prj(p)
00103         , prev_err_title(p->m_error_title)
00104     { 
00105         p->m_error_title = msg;
00106     }
00107     ~ErrorTitle()
00108     {
00109         prj->m_error_title = prev_err_title;
00110     }
00111     KexiProject* prj;
00112     QString prev_err_title;
00113 };*/
00114 
00115 KexiProject::KexiProject(KexiProjectData *pdata, KexiDB::MessageHandler* handler)
00116  : QObject(), Object(handler)
00117  , d(new Private())
00118 {
00119     d->data = pdata;
00121     Kexi::partManager().lookup();
00122 }
00123 
00124 KexiProject::KexiProject(KexiProjectData *pdata, KexiDB::MessageHandler* handler, 
00125     KexiDB::Connection* conn)
00126  : QObject(), Object(handler)
00127  , d(new Private())
00128 {
00129     d->data = pdata;
00130     if (d->data->connectionData() == d->connection->data())
00131         d->connection = conn;
00132     else
00133         kdWarning() << "KexiProject::KexiProject(): passed connection's data ("
00134             << conn->data()->serverInfoString() << ") is not compatible with project's conn. data ("
00135             << d->data->connectionData()->serverInfoString() << ")" << endl;
00137     Kexi::partManager().lookup();
00138 }
00139 
00140 KexiProject::~KexiProject()
00141 {
00142     closeConnection();
00143     delete d;
00144 }
00145 
00146 KexiDB::Connection *KexiProject::dbConnection() const
00147 {
00148     return d->connection;
00149 }
00150 
00151 KexiProjectData* KexiProject::data() const
00152 {
00153     return d->data;
00154 }
00155 
00156 bool KexiProject::final() const
00157 {
00158     return d->final;
00159 }
00160 
00161 void KexiProject::setFinal(bool set)
00162 {
00163     d->final = set;
00164 }
00165 
00166 int KexiProject::versionMajor() const
00167 {
00168     return d->versionMajor;
00169 }
00170 
00171 int KexiProject::versionMinor() const
00172 {
00173     return d->versionMinor;
00174 }
00175 
00176 tristate
00177 KexiProject::open(bool &incompatibleWithKexi)
00178 {
00179     return openInternal(&incompatibleWithKexi);
00180 }
00181 
00182 tristate
00183 KexiProject::open()
00184 {
00185     return openInternal(0);
00186 }
00187 
00188 tristate
00189 KexiProject::openInternal(bool *incompatibleWithKexi)
00190 {
00191     if (incompatibleWithKexi)
00192         *incompatibleWithKexi = false;
00193     kdDebug() << "KexiProject::open(): " << d->data->databaseName() <<" "<< d->data->connectionData()->driverName  << endl;
00194     KexiDB::MessageTitle et(this, 
00195         i18n("Could not open project \"%1\".").arg(d->data->databaseName()));
00196     
00197     if (!createConnection()) {
00198         kdDebug() << "KexiProject::open(): !createConnection()" << endl;
00199         return false;
00200     }
00201     bool cancel = false;
00202     KexiGUIMessageHandler msgHandler;
00203     if (!d->connection->useDatabase(d->data->databaseName(), true, &cancel, &msgHandler))
00204     {
00205         if (cancel) {
00206             return cancelled;
00207         }
00208         kdDebug() << "KexiProject::open(): !d->connection->useDatabase() " 
00209             << d->data->databaseName() <<" "<< d->data->connectionData()->driverName  << endl;
00210 
00211         if (d->connection->errorNum() == ERR_NO_DB_PROPERTY) {
00213             if (/*supported?*/ !d->data->connectionData()->driverName.lower().startsWith("sqlite")) {
00215                 if (incompatibleWithKexi)
00216                     *incompatibleWithKexi = true;
00217             }
00218             else
00219                 setError(d->connection);
00220             closeConnection();
00221             return false;
00222         }
00223 
00224         setError(d->connection);
00225         closeConnection();
00226         return false;
00227     }
00228 
00229     if (!initProject())
00230         return false;
00231 
00232     return createInternalStructures(/*insideTransaction*/true);
00233 }
00234 
00235 tristate
00236 KexiProject::create(bool forceOverwrite)
00237 {
00238     KexiDB::MessageTitle et(this, 
00239         i18n("Could not create project \"%1\".").arg(d->data->databaseName()));
00240         
00241     if (!createConnection())
00242         return false;
00243     if (!checkWritable())
00244         return false;
00245     if (d->connection->databaseExists( d->data->databaseName() )) {
00246         if (!forceOverwrite)
00247             return cancelled;
00248         if (!d->connection->dropDatabase( d->data->databaseName() )) {
00249             setError(d->connection);
00250             closeConnection();
00251             return false;
00252         }
00253         kdDebug() << "--- DB '" << d->data->databaseName() << "' dropped ---"<< endl;
00254     }
00255     if (!d->connection->createDatabase( d->data->databaseName() )) {
00256         setError(d->connection);
00257         closeConnection();
00258         return false;
00259     }
00260     kdDebug() << "--- DB '" << d->data->databaseName() << "' created ---"<< endl;
00261     // and now: open
00262     if (!d->connection->useDatabase(d->data->databaseName()))
00263     {
00264         kdDebug() << "--- DB '" << d->data->databaseName() << "' USE ERROR ---"<< endl;
00265         setError(d->connection);
00266         closeConnection();
00267         return false;
00268     }
00269     kdDebug() << "--- DB '" << d->data->databaseName() << "' used ---"<< endl;
00270 
00271     //<add some data>
00272     KexiDB::Transaction trans = d->connection->beginTransaction();
00273     if (trans.isNull())
00274         return false;
00275 
00276     if (!createInternalStructures(false))
00277         return false;
00278 
00279     //add some metadata
00281     KexiDB::DatabaseProperties &props = d->connection->databaseProperties();
00282     if (!props.setValue("kexiproject_major_ver", d->versionMajor)
00283         || !props.setCaption("kexiproject_major_ver", i18n("Project major version"))
00284         || !props.setValue("kexiproject_minor_ver", d->versionMinor)
00285         || !props.setCaption("kexiproject_minor_ver", i18n("Project minor version"))
00286         || !props.setValue("project_caption", d->data->caption())
00287         || !props.setCaption("project_caption", i18n("Project caption"))
00288         || !props.setValue("project_desc", d->data->description())
00289         || !props.setCaption("project_desc", i18n("Project description")) )
00290         return false;
00291 
00292 /*  KexiDB::TableSchema *t_db = d->connection->tableSchema("kexi__db");
00293     //caption:
00294     if (!t_db)
00295         return false;
00296 
00297     if (!KexiDB::replaceRow(*d->connection, t_db, "db_property", "project_caption", 
00298         "db_value", QVariant( d->data->caption() ), KexiDB::Field::Text)
00299      || !KexiDB::replaceRow(*d->connection, t_db, "db_property", "project_desc", 
00300         "db_value", QVariant( d->data->description() ), KexiDB::Field::Text) )
00301         return false;
00302 */
00303     if (trans.active() && !d->connection->commitTransaction(trans))
00304         return false;
00305     //</add some data>
00306 
00307     return initProject();
00308 }
00309 
00310 bool KexiProject::createInternalStructures(bool insideTransaction)
00311 {
00312     KexiDB::TransactionGuard tg;
00313     if (insideTransaction) {
00314         tg.setTransaction( d->connection->beginTransaction() );
00315         if (tg.transaction().isNull())
00316             return false;
00317     }
00318 
00319     //Get information about kexiproject version.
00320     //kexiproject version is a version of data layer above kexidb layer.
00321     KexiDB::DatabaseProperties &props = d->connection->databaseProperties();
00322     bool ok;
00323     int storedMajorVersion = props.value("kexiproject_major_ver").toInt(&ok);
00324     if (!ok)
00325         storedMajorVersion = 0;
00326     int storedMinorVersion = props.value("kexiproject_minor_ver").toInt(&ok);
00327     if (!ok)
00328         storedMinorVersion = 1;
00329 
00330     bool containsKexi__blobsTable = d->connection->drv_containsTable("kexi__blobs");
00331 
00333     if (storedMajorVersion<=0) {
00334         d->versionMajor = KEXIPROJECT_VERSION_MAJOR;
00335         d->versionMinor = KEXIPROJECT_VERSION_MINOR;
00336         //For compatibility for projects created before Kexi 1.0 beta 1:
00337         //1. no kexiproject_major_ver and kexiproject_minor_ver -> add them
00338         if (!props.setValue("kexiproject_major_ver", d->versionMajor)
00339             || !props.setCaption("kexiproject_major_ver", i18n("Project major version"))
00340             || !props.setValue("kexiproject_minor_ver", d->versionMinor)
00341             || !props.setCaption("kexiproject_minor_ver", i18n("Project minor version")) ) {
00342             return false;
00343         }
00344 
00345         //2. "kexi__blobs" table had no "o_folder_id" column -> add it (blobs will be lost...)
00346         if (containsKexi__blobsTable) {
00347             if (!d->connection->executeSQL(QString::fromLatin1("DROP TABLE kexi__blobs")))//lowlevel
00348                 return false;
00349             containsKexi__blobsTable = false;
00350         }
00351     }
00352     if (storedMajorVersion!=d->versionMajor || storedMajorVersion!=d->versionMinor) {
00354         d->versionMajor = storedMajorVersion;
00355         d->versionMinor = storedMinorVersion;
00356     }
00357 
00358     KexiDB::InternalTableSchema *t_blobs = new KexiDB::InternalTableSchema("kexi__blobs");
00359     t_blobs->addField( new KexiDB::Field("o_id", KexiDB::Field::Integer, 
00360         KexiDB::Field::PrimaryKey | KexiDB::Field::AutoInc, KexiDB::Field::Unsigned) )
00361     .addField( new KexiDB::Field("o_data", KexiDB::Field::BLOB) )
00362     .addField( new KexiDB::Field("o_name", KexiDB::Field::Text ) )
00363     .addField( new KexiDB::Field("o_caption", KexiDB::Field::Text ) )
00364     .addField( new KexiDB::Field("o_mime", KexiDB::Field::Text, KexiDB::Field::NotNull) )
00365     .addField( new KexiDB::Field("o_folder_id", 
00366         KexiDB::Field::Integer, 0, KexiDB::Field::Unsigned) //references kexi__gallery_folders.f_id
00367                                             //If null, the BLOB only points to virtual "All" folder
00368                                             //WILL BE USED in Kexi >=1.1
00369     );
00370 
00371     //*** create global BLOB container, if not present
00372     if (containsKexi__blobsTable) {
00374         d->connection->insertInternalTableSchema(t_blobs);
00375     }
00376     else {
00377 //      if (!d->connection->createTable( t_blobs, false/*!replaceExisting*/ )) {
00378         if (!d->connection->createTable( t_blobs, true/*replaceExisting*/ )) {
00379             delete t_blobs;
00380             return false;
00381         }
00382     }
00383 
00384     //Store default part infos.
00385     //Infos for other parts (forms, reports...) are created on demand in KexiDialogBase::storeNewData()
00386     KexiDB::InternalTableSchema *t_parts = new KexiDB::InternalTableSchema("kexi__parts"); //newKexiDBSystemTableSchema("kexi__parts");
00387     t_parts->addField( 
00388         new KexiDB::Field("p_id", KexiDB::Field::Integer, KexiDB::Field::PrimaryKey | KexiDB::Field::AutoInc, KexiDB::Field::Unsigned) 
00389     )
00390     .addField( new KexiDB::Field("p_name", KexiDB::Field::Text) )
00391     .addField( new KexiDB::Field("p_mime", KexiDB::Field::Text ) )
00392     .addField( new KexiDB::Field("p_url", KexiDB::Field::Text ) );
00393 
00394     bool containsKexi__partsTable = d->connection->drv_containsTable("kexi__parts");
00395     bool partsTableOk = true;
00396     if (containsKexi__partsTable) {
00398         d->connection->insertInternalTableSchema(t_parts);
00399     }
00400     else {
00401         partsTableOk = d->connection->createTable( t_parts, true/*replaceExisting*/ );
00402 
00403         KexiDB::FieldList *fl = t_parts->subList("p_id", "p_name", "p_mime", "p_url");
00404         if (partsTableOk)
00405             partsTableOk = d->connection->insertRecord(*fl, QVariant(1), QVariant("Tables"), 
00406                 QVariant("kexi/table"), QVariant("http://koffice.org/kexi/"));
00407 
00408         if (partsTableOk)
00409             partsTableOk = d->connection->insertRecord(*fl, QVariant(2), QVariant("Queries"), 
00410                 QVariant("kexi/query"), QVariant("http://koffice.org/kexi/"));
00411     }
00412 
00413     if (!partsTableOk) {
00414         delete t_parts;
00415         return false;
00416     }
00417 
00418     if (insideTransaction) {
00419         if (tg.transaction().active() && !tg.commit())
00420             return false;
00421     }
00422     return true;
00423 }
00424 
00425 bool
00426 KexiProject::createConnection()
00427 {
00428     if (d->connection)
00429         return true;
00430 
00431     clearError();
00432 //  closeConnection();//for sanity
00433     KexiDB::MessageTitle et(this);
00434     
00435     KexiDB::Driver *driver = Kexi::driverManager().driver(d->data->connectionData()->driverName);
00436     if(!driver) {
00437         setError(&Kexi::driverManager());
00438         return false;
00439     }
00440 
00441     d->connection = driver->createConnection(*d->data->connectionData());
00442     if (!d->connection)
00443     {
00444         kdDebug() << "KexiProject::open(): uuups failed " << driver->errorMsg()  << endl;
00445         setError(driver);
00446         return false;
00447     }
00448 
00449     if (!d->connection->connect())
00450     {
00451         setError(d->connection);
00452         kdDebug() << "KexiProject::createConnection(): error connecting: " << (d->connection ? d->connection->errorMsg() : QString::null) << endl;
00453         closeConnection();
00454         return false;
00455     }
00456 
00457     //re-init BLOB buffer
00459     KexiBLOBBuffer::setConnection(d->connection);
00460     return true;
00461 }
00462 
00463 
00464 void
00465 KexiProject::closeConnection()
00466 {
00467     if (!d->connection)
00468         return;
00469 
00470     delete d->connection; //this will also clear connection for BLOB buffer
00471     d->connection = 0;
00472 }
00473 
00474 bool
00475 KexiProject::initProject()
00476 {
00477 //  emit dbAvailable();
00478     kdDebug() << "KexiProject::open(): checking project parts..." << endl;
00479     
00480     if (!Kexi::partManager().checkProject(d->connection)) {
00481         setError(Kexi::partManager().error() ? (KexiDB::Object*)&Kexi::partManager() : (KexiDB::Connection*)d->connection);
00482         return false;
00483     }
00484 
00485 // !@todo put more props. todo - creator, created date, etc. (also to KexiProjectData)
00486     KexiDB::DatabaseProperties &props = d->connection->databaseProperties();
00487     QString str( props.value("project_caption").toString() );
00488     if (!str.isEmpty())
00489         d->data->setCaption( str );
00490     str = props.value("project_desc").toString();
00491     if (!str.isEmpty())
00492         d->data->setDescription( str );
00493 /*  KexiDB::RowData data;
00494     QString sql = "select db_value from kexi__db where db_property='%1'";
00495     if (d->connection->querySingleRecord( sql.arg("project_caption"), data ) && !data[0].toString().isEmpty())
00496         d->data->setCaption(data[0].toString());
00497     if (d->connection->querySingleRecord( sql.arg("project_desc"), data) && !data[0].toString().isEmpty())
00498         d->data->setDescription(data[0].toString());*/
00499 
00500     return true;
00501 }
00502 
00503 bool
00504 KexiProject::isConnected()
00505 {
00506     if(d->connection && d->connection->isDatabaseUsed())
00507         return true;
00508 
00509     return false;
00510 }
00511 
00512 KexiPart::ItemDict*
00513 KexiProject::items(KexiPart::Info *i)
00514 {
00515     kdDebug() << "KexiProject::items()" << endl;
00516     if(!i || !isConnected())
00517         return 0;
00518 
00519     //trying in cache...
00520     KexiPart::ItemDict *dict = d->itemDictsCache[ i->projectPartID() ];
00521     if (dict)
00522         return dict;
00523     //retrieve:
00524     KexiDB::Cursor *cursor = d->connection->executeQuery(
00525         "SELECT o_id, o_name, o_caption  FROM kexi__objects WHERE o_type = " 
00526         + QString::number(i->projectPartID()));//, KexiDB::Cursor::Buffered);
00527 //  kdDebug() << "KexiProject::items(): cursor handle is:" << cursor << endl;
00528     if(!cursor)
00529         return 0;
00530 
00531     dict = new KexiPart::ItemDict(1009);
00532     dict->setAutoDelete(true);
00533 
00534     for(cursor->moveFirst(); !cursor->eof(); cursor->moveNext())
00535     {
00536         KexiPart::Item *it = new KexiPart::Item();
00537         bool ok;
00538         int ident = cursor->value(0).toInt(&ok);
00539         QString objName( cursor->value(1).toString() );
00540         
00541         if ( ok && (ident>0) && !d->connection->isInternalTableSchema(objName) 
00542             && KexiUtils::isIdentifier(objName) )
00543         {
00544             it->setIdentifier(ident);
00545             it->setMimeType(i->mimeType()); //js: may be not null???
00546             it->setName(objName);
00547             it->setCaption(cursor->value(2).toString());
00548         }
00549         dict->insert(it->identifier(), it);
00550 //      kdDebug() << "KexiProject::items(): ITEM ADDED == "<<objName <<" id="<<ident<<endl;
00551     }
00552 
00553     d->connection->deleteCursor(cursor);
00554 //  kdDebug() << "KexiProject::items(): end with count " << dict->count() << endl;
00555     d->itemDictsCache.insert( i->projectPartID(), dict );
00556     return dict;
00557 }
00558 
00559 KexiPart::ItemDict*
00560 KexiProject::itemsForMimeType(const QCString &mimeType)
00561 {
00562     KexiPart::Info *info = Kexi::partManager().infoForMimeType(mimeType);
00563     return items(info);
00564 }
00565 
00566 void
00567 KexiProject::getSortedItems(KexiPart::ItemList& list, KexiPart::Info *i)
00568 {
00569     list.clear();
00570     KexiPart::ItemDict* dict = items(i);
00571     if (!dict)
00572         return;
00573     for (KexiPart::ItemDictIterator it(*dict); it.current(); ++it)
00574         list.append(it.current());
00575 }
00576 
00577 void
00578 KexiProject::getSortedItemsForMimeType(KexiPart::ItemList& list, const QCString &mimeType)
00579 {
00580     KexiPart::Info *info = Kexi::partManager().infoForMimeType(mimeType);
00581     getSortedItems(list, info);
00582 }
00583 
00584 void
00585 KexiProject::addStoredItem(KexiPart::Info *info, KexiPart::Item *item)
00586 {
00587     if (!info || !item)
00588         return;
00589     KexiPart::ItemDict *dict = items(info);
00590     item->setNeverSaved( false );
00591     d->unstoredItems.take(item); //no longer unstored
00592     dict->insert( item->identifier(), item );
00593     //let's update e.g. navigator
00594     emit newItemStored(*item);
00595 }
00596 
00597 KexiPart::Item*
00598 KexiProject::itemForMimeType(const QCString &mimeType, const QString &name)
00599 {
00600     KexiPart::ItemDict *dict = itemsForMimeType(mimeType);
00601     if (!dict)
00602         return 0;
00603     const QString l_name = name.lower();
00604     for (KexiPart::ItemDictIterator it( *dict ); it.current(); ++it) {
00605         if (it.current()->name().lower()==l_name)
00606             return it.current();
00607     }
00608     return 0;
00609 }
00610 
00611 KexiPart::Item*
00612 KexiProject::item(KexiPart::Info *i, const QString &name)
00613 {
00614     KexiPart::ItemDict *dict = items(i);
00615     if (!dict)
00616         return 0;
00617     const QString l_name = name.lower();
00618     for (KexiPart::ItemDictIterator it( *dict ); it.current(); ++it) {
00619         if (it.current()->name().lower()==l_name)
00620             return it.current();
00621     }
00622     return 0;
00623 }
00624 
00625 KexiPart::Item*
00626 KexiProject::item(int identifier)
00627 {
00628     KexiPart::ItemDict *dict;
00629     for (QIntDictIterator<KexiPart::ItemDict> it(d->itemDictsCache); (dict = it.current()); ++it) {
00630         KexiPart::Item *item = dict->find(identifier);
00631         if (item)
00632             return item;
00633     }
00634     return 0;
00635 }
00636 
00637 /*void KexiProject::clearMsg()
00638 {
00639     clearError();
00640 //  d->error_title=QString::null;
00641 }
00642 
00643 void KexiProject::setError(int code, const QString &msg )
00644 {
00645     Object::setError(code, msg);
00646     if (Object::error())
00647         ERRMSG(d->error_title, this);
00648 //      emit error(d->error_title, this);
00649 }
00650 
00651 
00652 void KexiProject::setError( const QString &msg )
00653 {
00654     Object::setError(msg);
00655     if (Object::error())
00656         ERRMSG(d->error_title, this);
00657 //      emit error(d->error_title, this);
00658 }
00659 
00660 void KexiProject::setError( KexiDB::Object *obj )
00661 {
00662     if (!obj)
00663         return;
00664     Object::setError(obj);
00665     if (Object::error())
00666         ERRMSG(d->error_title, obj);
00667 //      emit error(d->error_title, obj);
00668 }
00669 
00670 void KexiProject::setError(const QString &msg, const QString &desc)
00671 {
00672     Object::setError(msg); //ok?
00673     ERRMSG(msg, desc); //not KexiDB-related
00674 //  emit error(msg, desc); //not KexiDB-related
00675 }
00676 */
00677 
00678 KexiPart::Part *KexiProject::findPartFor(KexiPart::Item& item)
00679 {
00680     clearError();
00681     KexiDB::MessageTitle et(this);
00682     KexiPart::Part *part = Kexi::partManager().partForMimeType(item.mimeType());
00683     if (!part)
00684         setError(&Kexi::partManager());
00685     return part;
00686 }
00687 
00688 KexiDialogBase* KexiProject::openObject(KexiMainWindow *wnd, KexiPart::Item& item, 
00689     int viewMode, QMap<QString,QString>* staticObjectArgs)
00690 {
00691     clearError();
00692     KexiDB::MessageTitle et(this);
00693     KexiPart::Part *part = findPartFor(item);
00694     if (!part)
00695         return 0;
00696     KexiDialogBase *dlg  = part->openInstance(wnd, item, viewMode, staticObjectArgs);
00697     if (!dlg) {
00698         if (part->lastOperationStatus().error())
00699             setError(i18n("Opening object \"%1\" failed.").arg(item.name())+"<br>"
00700                 +part->lastOperationStatus().message, 
00701                 part->lastOperationStatus().description);
00702         return 0;
00703     }
00704     return dlg;
00705 }
00706 
00707 KexiDialogBase* KexiProject::openObject(KexiMainWindow *wnd, const QCString &mimeType, 
00708     const QString& name, int viewMode)
00709 {
00710     KexiPart::Item *it = itemForMimeType(mimeType, name);
00711     return it ? openObject(wnd, *it, viewMode) : 0;
00712 }
00713 
00714 bool KexiProject::checkWritable()
00715 {
00716     if (!d->connection->isReadOnly())
00717         return true;
00718     setError(futureI18n("This project is opened as read only."));
00719     return false;
00720 }
00721 
00722 bool KexiProject::removeObject(KexiMainWindow *wnd, KexiPart::Item& item)
00723 {
00724     clearError();
00725     KexiDB::MessageTitle et(this);
00726     if (!checkWritable())
00727         return false;
00728     KexiPart::Part *part = findPartFor(item);
00729     if (!part)
00730         return false;
00731     if (!item.neverSaved() && !part->remove(wnd, item)) {
00732         //js TODO check for errors
00733         return false;
00734     }
00735     if (!item.neverSaved()) {
00736         KexiDB::TransactionGuard tg( *d->connection );
00737         if (!tg.transaction().active()) {
00738             setError(d->connection);
00739             return false;
00740         }
00741         if (!d->connection->removeObject( item.identifier() )) {
00742             setError(d->connection);
00743             return false;
00744         }
00745         if (!tg.commit()) {
00746             setError(d->connection);
00747             return false;
00748         }
00749     }
00750     emit itemRemoved(item);
00751 
00752     //now: remove this item from cache
00753     if (part->info()) {
00754         KexiPart::ItemDict *dict = d->itemDictsCache[ part->info()->projectPartID() ];
00755         if (!(dict && dict->remove( item.identifier() )))
00756             d->unstoredItems.remove(&item);//remove temp.
00757     }
00758     return true;
00759 }
00760 
00761 bool KexiProject::renameObject( KexiMainWindow *wnd, KexiPart::Item& item, const QString& _newName )
00762 {
00763     KexiUtils::WaitCursor wait;
00764     clearError();
00765     QString newName = _newName.stripWhiteSpace();
00766     {
00767         KexiDB::MessageTitle et(this);
00768         if (newName.isEmpty()) {
00769             setError( i18n("Could not set empty name for this object.") );
00770             return false;
00771         }
00772         if (this->itemForMimeType(item.mimeType(), newName)!=0) {
00773             setError( i18n("Could not use this name. Object with name \"%1\" already exists.")
00774                 .arg(newName) );
00775             return false;
00776         }
00777     }
00778 
00779     KexiDB::MessageTitle et(this, 
00780         i18n("Could not rename object \"%1\".").arg(item.name()) );
00781     if (!checkWritable())
00782         return false;
00783     KexiPart::Part *part = findPartFor(item);
00784     if (!part)
00785         return false;
00786     KexiDB::TransactionGuard tg( *d->connection );
00787     if (!tg.transaction().active()) {
00788         setError(d->connection);
00789         return false;
00790     }
00791     if (!part->rename(wnd, item, newName)) {
00792         setError(part->lastOperationStatus().message, part->lastOperationStatus().description);
00793         return false;
00794     }
00795     if (!d->connection->executeSQL( "update kexi__objects set o_name="
00796         + d->connection->driver()->valueToSQL( KexiDB::Field::Text, newName )
00797         + " where o_id=" + QString::number(item.identifier()) )) {
00798         setError(d->connection);
00799         return false;
00800     }
00801     if (!tg.commit()) {
00802         setError(d->connection);
00803         return false;
00804     }
00805     QCString oldName( item.name().latin1() );
00806     item.setName( newName );
00807     emit itemRenamed(item, oldName);
00808     return true;
00809 }
00810 
00811 KexiPart::Item* KexiProject::createPartItem(KexiPart::Info *info, const QString& suggestedCaption)
00812 {
00813     clearError();
00814     KexiDB::MessageTitle et(this);
00815     KexiPart::Part *part = Kexi::partManager().part(info);
00816     if (!part) {
00817         setError(&Kexi::partManager());
00818         return 0;
00819     }
00820 
00821     KexiPart::ItemDict *dict = items(info);
00822 
00823     //find new, unique default name for this item
00824     int n;
00825     QString new_name;
00826     QString base_name;
00827     if (suggestedCaption.isEmpty()) {
00828         n = 1;
00829         base_name = part->instanceName();
00830     }
00831     else {
00832         n = 0; //means: try not to add 'n'
00833         base_name = KexiUtils::string2Identifier(suggestedCaption).lower();
00834     }
00835     base_name = KexiUtils::string2Identifier(base_name).lower();
00836     KexiPart::ItemDictIterator it(*dict);
00837     QPtrDictIterator<KexiPart::Item> itUnstored(d->unstoredItems);
00838     do {
00839         new_name = base_name;
00840         if (n>=1)
00841             new_name += QString::number(n);
00842         for (it.toFirst(); it.current(); ++it) {
00843             if (it.current()->name().lower()==new_name)
00844                 break;
00845         }
00846         if ( it.current() ) {
00847             n++;
00848             continue; //stored exists!
00849         }
00850         for (itUnstored.toFirst(); itUnstored.current(); ++itUnstored) {
00851             if (itUnstored.current()->name().lower()==new_name)
00852                 break;
00853         }
00854         if ( !itUnstored.current() )
00855             break; //unstored doesn't exist
00856         n++;
00857     } while (n<1000/*sanity*/);
00858 
00859     if (n>=1000)
00860         return 0;
00861 
00862     QString new_caption( suggestedCaption.isEmpty() ? part->instanceCaption() : suggestedCaption);
00863     if (n>=1)
00864         new_caption += QString::number(n);
00865 
00866     KexiPart::Item *item = new KexiPart::Item();
00867     item->setIdentifier( --d->tempPartItemID_Counter );//temporary
00868     item->setMimeType(info->mimeType());
00869     item->setName(new_name);
00870     item->setCaption(new_caption);
00871     item->setNeverSaved(true);
00872     d->unstoredItems.insert(item, item);
00873     return item;
00874 }
00875 
00876 KexiPart::Item* KexiProject::createPartItem(KexiPart::Part *part, const QString& suggestedCaption)
00877 {
00878     return createPartItem(part->info(), suggestedCaption);
00879 }
00880 
00881 void KexiProject::deleteUnstoredItem(KexiPart::Item *item)
00882 {
00883     if (!item)
00884         return;
00885     d->unstoredItems.remove(item);
00886 }
00887 
00888 KexiDB::Parser* KexiProject::sqlParser()
00889 {
00890     if (!d->sqlParser) {
00891         if (!d->connection)
00892             return 0;
00893         d->sqlParser = new KexiDB::Parser(d->connection);
00894     }
00895     return d->sqlParser;
00896 }
00897 
00898 static const QString warningNoUndo = i18n("Warning: entire project's data will be removed.");
00899 
00900 /*static*/
00901 KexiProject*
00902 KexiProject::createBlankProject(bool &cancelled, KexiProjectData* data,
00903     KexiDB::MessageHandler* handler)
00904 {
00905     cancelled = false;
00906     KexiProject *prj = new KexiProject( new KexiProjectData(*data), handler );
00907 
00908     bool ok = true;
00909     tristate res = prj->create(false);
00910     if (~res) {
00912         if (KMessageBox::Yes != KMessageBox::warningYesNo(0, "<qt>"+i18n(
00913             "The project %1 already exists.\n"
00914             "Do you want to replace it with a new, blank one?")
00915                 .arg(prj->data()->infoString())+"\n"+warningNoUndo+"</qt>",
00916             QString::null, KGuiItem(i18n("Replace")), KStdGuiItem::cancel() ))
00917 //todo add serverInfoString() for server-based prj
00918         {
00919             delete prj;
00920             cancelled = true;
00921             return 0;
00922         }
00923         res = prj->create(true/*overwrite*/);
00924     }
00925     ok = res;
00926     if (!ok) {
00927         delete prj;
00928         return 0;
00929     }
00930     kdDebug() << "KexiProject::createBlankProject(): new project created --- " << endl;
00931 //todo? Kexi::recentProjects().addProjectData( data );
00932 
00933     return prj;
00934 }
00935 
00936 /*static*/
00937 tristate KexiProject::dropProject(KexiProjectData* data, 
00938     KexiDB::MessageHandler* handler, bool dontAsk)
00939 {
00940     if (!dontAsk && KMessageBox::Yes != KMessageBox::warningYesNo(0, 
00941         i18n("Do you want to drop the project \"%1\"?").arg(data->objectName())+"\n"+warningNoUndo ))
00942         return cancelled;
00943 
00944     KexiProject prj( new KexiProjectData(*data), handler );
00945     if (!prj.open())
00946         return false;
00947 
00948     if (prj.dbConnection()->isReadOnly()) {
00949         handler->showErrorMessage(
00950             futureI18n("Could not drop this project. Database connection project is opened as read only."));
00951         return false;
00952     }
00953 
00954     return prj.dbConnection()->dropDatabase();
00955 }
00956 
00957 /*void KexiProject::reloadPartItem( KexiDialogBase* dialog )
00958 {
00959     if (!dialog)
00960         return;
00961 
00962     KexiPart::Item* item = dialog->partItem();
00963 
00964     if (dialog || !d->connection->setQuerySchemaObsolete( queryName ))
00965         return;
00966     KexiPart::Info *partInfo = Kexi::partManager().infoForMimeType("kexi/query");
00967     if (!partInfo)
00968         return; //err?
00969         item(partInfo, queryName);
00970     if (!item)
00971         return; //err?
00972     emit itemSetO
00973 
00974 }*/
00975 
00976 #include "kexiproject.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys