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