Kexi API Documentation (2.0 alpha)

kexiproject.cpp

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