diff --git a/kexi/core/kexipartmanager.cpp b/kexi/core/kexipartmanager.cpp index 1a66668a47..7dce40f1d4 100644 --- a/kexi/core/kexipartmanager.cpp +++ b/kexi/core/kexipartmanager.cpp @@ -1,260 +1,260 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include "kexipartmanager.h" #include "kexipart.h" #include "kexipartinfo.h" #include "kexi_global.h" #include #include using namespace KexiPart; Manager::Manager(QObject *parent) : QObject(parent) { m_lookupDone = false; m_partlist.setAutoDelete(true); m_partsByMime.setAutoDelete(false); m_parts.setAutoDelete(false);//KApp will remove parts } void Manager::lookup() { //js: TODO: allow refreshing!!!! (will need calling removeClient() by Part objects) if (m_lookupDone) return; m_lookupDone = true; m_partlist.clear(); m_partsByMime.clear(); m_parts.clear(); KTrader::OfferList tlist = KTrader::self()->query("Kexi/Handler", "[X-Kexi-PartVersion] == " + QString::number(KEXI_PART_VERSION)); KConfig conf("kexirc", true); conf.setGroup("Parts"); QStringList sl_order = QStringList::split( ",", conf.readEntry("Order") );//we'll set parts in defined order const int size = QMAX( tlist.count(), sl_order.count() ); QPtrVector ordered( size*2 ); int offset = size; //we will insert not described parts from #offset //compute order - for(KTrader::OfferList::Iterator it(tlist.begin()); it != tlist.end(); ++it) + for(KTrader::OfferList::ConstIterator it(tlist.constBegin()); it != tlist.constEnd(); ++it) { KService::Ptr ptr = (*it); QString mime = ptr->property("X-Kexi-TypeMime").toString(); kdDebug() << "Manager::lookup(): " << mime << endl; //: disable some parts if needed if (!Kexi::tempShowForms() && mime=="kexi/form") continue; // int idx = sl_order.findIndex( ptr->library() ); if (idx!=-1) ordered.insert(idx, ptr); else //add to end ordered.insert(offset++, ptr); } //fill final list using computed order for (int i = 0; i< (int)ordered.size(); i++) { KService::Ptr ptr = ordered[i]; if (ptr) { Info *info = new Info(ptr); m_partsByMime.insert(info->mime(), info); kdDebug() << "Manager::lookup(): inserting info to " << info->mime() << endl; // m_partsByMime.insert(ptr->property("X-Kexi-TypeMime").toString(), info); m_partlist.append(info); } } } Manager::~Manager() { } Part * Manager::part(Info *i) { if(!i || i->broken()) return 0; kdDebug() << "Manager::part( id = " << i->projectPartID() << " )" << endl; Part *p = m_parts[i->projectPartID()]; if(!p) { kdDebug() << "Manager::part().." << endl; int error=0; p = KParts::ComponentFactory::createInstanceFromService(i->ptr(), this, QString(i->objectName()+"_part").latin1(), QStringList(), &error); if(!p) { kdDebug() << "Manager::part(): failed :( (ERROR #" << error << ")" << endl; kdDebug() << " " << KLibLoader::self()->lastErrorMessage() << endl; i->setBroken(true); setError(i18n("Error during loading part module \"%1\"").arg(i->objectName())); return 0; } DataSource *ds = p->dataSource(); if(ds) { kdDebug() << "Manager::part(): " << i->groupName() << " provides data" << endl; m_datasources.append(ds); } else { kdDebug() << "Manager::part(): " << i->groupName() << " doesn't provide data" << endl; } p->setInfo(i); m_parts.insert(i->projectPartID(),p); emit partLoaded(p); } else { kdDebug() << "Manager::part(): cached: " << i->groupName() << endl; } kdDebug() << "Manager::part(): fine!" << endl; return p; } #if 0 void Manager::unloadPart(Info *i) { m_parts.setAutoDelete(true); m_parts.remove(i->projectPartID()); m_parts.setAutoDelete(false); /* if (!p) return; m_partsByMime.take(i->mime()); m_partlist.removeRef(p);*/ } void Manager::unloadAllParts() { // m_partsByMime.clear(); m_parts.setAutoDelete(true); m_parts.clear(); m_parts.setAutoDelete(false); // m_partlist.clear(); } #endif /*void Manager::removeClients( KexiMainWindow *win ) { if (!win) return; QIntDictIterator it(m_parts); for (;i.current();++it) { i.current()->removeClient(win->guiFactory()); } }*/ Part * Manager::part(const QCString &mime) { return part(m_partsByMime[mime]); } Info * Manager::info(const QCString &mime) { Info *i = m_partsByMime[mime]; if (i) return i; setError(i18n("No parts module for mime type \"%1\"").arg(mime)); return 0; } bool Manager::checkProject(KexiDB::Connection *conn) { // QString errmsg = i18n("Invalid project contents."); //TODO: catch errors! if(!conn->isDatabaseUsed()) { setError(conn); return false; } KexiDB::Cursor *cursor = conn->executeQuery("SELECT * FROM kexi__parts");//, KexiDB::Cursor::Buffered); if(!cursor) { setError(conn); return false; } int id=0; // QStringList parts_found; for(cursor->moveFirst(); !cursor->eof(); cursor->moveNext()) { id++; Info *i = info(cursor->value(2).toCString()); if(!i) { Missing m; m.name = cursor->value(1).toString(); m.mime = cursor->value(2).toCString(); m.url = cursor->value(3).toString(); m_missing.append(m); } else { i->setProjectPartID(cursor->value(0).toInt()); // parts_found+=cursor->value(2).toString(); } } conn->deleteCursor(cursor); #if 0 //js: moved to Connection::createDatabase() //add missing default part entries KexiDB::TableSchema *ts = conn->tableSchema("kexi__parts"); if (!ts) return false; KexiDB::FieldList *fl = ts->subList("p_id", "p_name", "p_mime", "p_url"); if (!fl) return false; if (!parts_found.contains("kexi/table")) { if (!conn->insertRecord(*fl, QVariant(1), QVariant("Tables"), QVariant("kexi/table"), QVariant("http://"))) return false; } if (!parts_found.contains("kexi/query")) { if (!conn->insertRecord(*fl, QVariant(2), QVariant("Queries"), QVariant("kexi/query"), QVariant("http://"))) return false; } #endif return true; } #include "kexipartmanager.moc" diff --git a/kexi/core/kexiprojectset.cpp b/kexi/core/kexiprojectset.cpp index 6eaf98a204..485d58824c 100644 --- a/kexi/core/kexiprojectset.cpp +++ b/kexi/core/kexiprojectset.cpp @@ -1,103 +1,103 @@ /* This file is part of the KDE project Copyright (C) 2003 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kexiprojectset.h" #include "kexi.h" #include #include #include class KexiProjectSetPrivate { public: KexiProjectSetPrivate() { // list.setAutoDelete(true); } KexiProjectData::List list; }; KexiProjectSet::KexiProjectSet() : d(new KexiProjectSetPrivate()) { } KexiProjectSet::KexiProjectSet(KexiDB::ConnectionData &conndata) : d(new KexiProjectSetPrivate()) { KexiDB::Driver *drv = Kexi::driverManager().driver(conndata.driverName); if (!drv) { setError(&Kexi::driverManager()); return; } KexiDB::Connection *conn = drv->createConnection(conndata); if (!conn) { setError(drv); return; } if (!conn->connect()) { setError(conn); delete conn; return; } QStringList dbnames = conn->databaseNames(false/*skip system*/); KexiDBDbg << dbnames.count() << endl; if (conn->error()) { setError(conn); delete conn; return; } delete conn; conn = 0; - for (QStringList::Iterator it = dbnames.begin(); it!=dbnames.end(); ++it) { + for (QStringList::ConstIterator it = dbnames.constBegin(); it!=dbnames.constEnd(); ++it) { // project's caption is just the same as database name - nothing better is available KexiProjectData *pdata = new KexiProjectData(conndata, *it, *it); d->list.append( pdata ); } clearError(); } KexiProjectSet::~KexiProjectSet() { delete d; } void KexiProjectSet::addProjectData(KexiProjectData *data) { d->list.append(data); } KexiProjectData::List KexiProjectSet::list() const { return d->list; } KexiProjectData* KexiProjectSet::findProject(const QString &dbName) const { const QString _dbName = dbName.lower(); QPtrListIterator it( d->list ); for (;it.current();++it) { if (it.current()->databaseName().lower()==_dbName) return it.current(); } return 0; } diff --git a/kexi/core/kexisharedactionhost.cpp b/kexi/core/kexisharedactionhost.cpp index 55dee36ad0..8b1b410002 100644 --- a/kexi/core/kexisharedactionhost.cpp +++ b/kexi/core/kexisharedactionhost.cpp @@ -1,261 +1,261 @@ /* This file is part of the KDE project Copyright (C) 2004 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kexisharedactionhost.h" #include "kexisharedactionhost_p.h" #include "kexiactionproxy.h" #include "kexidialogbase.h" #include "kexi_utils.h" #include #include //! internal class KexiSharedActionHostPrivate::KexiSharedActionHostPrivate(KexiSharedActionHost *h) : QObject(0,"KexiSharedActionHostPrivate") , actionProxies(401) , actionMapper( this ) , volatileActions(401) , enablers(401, false) , host(h) { volatileActions.setAutoDelete(true); connect(&actionMapper, SIGNAL(mapped(const QString &)), this, SLOT(slotAction(const QString &))); } void KexiSharedActionHostPrivate::slotAction(const QString& act_id) { QWidget *w = host->focusWindow(); //focusWidget(); // while (w && !w->inherits("KexiDialogBase") && !w->inherits("KexiDockBase")) // w = w->parentWidget(); KexiActionProxy *proxy = w ? actionProxies[ w ] : 0; if (!proxy || !proxy->activateSharedAction(act_id.latin1())) { //also try to find previous enabler w = enablers[act_id.latin1()]; if (!w) return; proxy = actionProxies[ w ]; if (!proxy) return; proxy->activateSharedAction(act_id.latin1()); } } //-------------------------------------------------- //! dummy host to avoid crashes KexiSharedActionHost KexiSharedActionHost_dummy = KexiSharedActionHost(0); //! default host KexiSharedActionHost* KexiSharedActionHost_defaultHost = &KexiSharedActionHost_dummy; KexiSharedActionHost& KexiSharedActionHost::defaultHost() { return *KexiSharedActionHost_defaultHost; } void KexiSharedActionHost::setAsDefaultHost() { KexiSharedActionHost_defaultHost = this; } //-------------------------------------------------- KexiSharedActionHost::KexiSharedActionHost(KMainWindow* mainWin) : d( new KexiSharedActionHostPrivate(this) ) { d->mainWin = mainWin; } KexiSharedActionHost::~KexiSharedActionHost() { if (KexiSharedActionHost_defaultHost == this) { //default host is destroyed! - restore dummy KexiSharedActionHost_defaultHost = &KexiSharedActionHost_dummy; } delete d; d=0; //! to let takeActionProxyFor() know that we are almost dead :) } void KexiSharedActionHost::setActionAvailable(const char *action_name, bool avail) { KAction *act = d->mainWin->actionCollection()->action(action_name); if (act) { act->setEnabled(avail); } } void KexiSharedActionHost::updateActionAvailable(const char *action_name, bool avail, QObject *obj) { QWidget *fw = d->mainWin->focusWidget(); while (fw && obj!=fw) fw = fw->parentWidget(); if (!fw) return; setActionAvailable(action_name, avail); if (avail) { d->enablers.replace(action_name, fw); } else { d->enablers.take(action_name); } } void KexiSharedActionHost::plugActionProxy(KexiActionProxy *proxy) { // kdDebug() << "KexiSharedActionHost::plugActionProxy():" << proxy->receiver()->name() << endl; d->actionProxies.insert( proxy->receiver(), proxy ); } KMainWindow* KexiSharedActionHost::mainWindow() const { return d->mainWin; } void KexiSharedActionHost::invalidateSharedActions(QObject *o) { bool insideDialogBase = o && (o->inherits("KexiDialogBase") || 0!=Kexi::findParent(o, "KexiDialogBase")); KexiActionProxy *p = o ? d->actionProxies[ o ] : 0; - for (KActionPtrList::Iterator it=d->sharedActions.begin(); it!=d->sharedActions.end(); ++it) { + for (KActionPtrList::ConstIterator it=d->sharedActions.constBegin(); it!=d->sharedActions.constEnd(); ++it) { // setActionAvailable((*it)->name(),p && p->isAvailable((*it)->name())); KAction *a = *it; if (!insideDialogBase && d->mainWin->actionCollection()!=a->parentCollection()) { //o is not KexiDialogBase or its child: // only invalidate action if it comes from mainwindow's KActionCollection // (thus part-actions are untouched when the focus is e.g. in the Property Editor) continue; } const bool avail = p && p->isAvailable(a->name()); KexiVolatileActionData *va = d->volatileActions[ a ]; if (va != 0) { if (p && p->isSupported(a->name())) { QPtrList actions_list; actions_list.append( a ); if (!va->plugged) { va->plugged=true; // d->mainWin->unplugActionList( a->name() ); d->mainWin->plugActionList( a->name(), actions_list ); } } else { if (va->plugged) { va->plugged=false; d->mainWin->unplugActionList( a->name() ); } } } // a->setEnabled(p && p->isAvailable(a->name())); a->setEnabled(avail); // kdDebug() << "Action " << a->name() << (avail ? " enabled." : " disabled.") << endl; } } KexiActionProxy* KexiSharedActionHost::actionProxyFor(QObject *o) const { return d->actionProxies[ o ]; } KexiActionProxy* KexiSharedActionHost::takeActionProxyFor(QObject *o) { if (d) return d->actionProxies.take( o ); return 0; } bool KexiSharedActionHost::acceptsSharedActions(QObject *) { return false; } QWidget* KexiSharedActionHost::focusWindow() { QWidget* fw = d->mainWin->focusWidget(); while (fw && !acceptsSharedActions(fw)) fw = fw->parentWidget(); return fw; } KAction* KexiSharedActionHost::createSharedActionInternal( KAction *action ) { QObject::connect(action,SIGNAL(activated()), &d->actionMapper, SLOT(map())); d->actionMapper.setMapping(action, action->name()); d->sharedActions.append( action ); return action; } /*class KexiAction : public KAction { public: KexiAction(const QString &text, const QIconSet &pix, const KShortcut &cut, const QObject *receiver, const char *slot, KActionCollection *parent, const char *name) : KAction(text,pix,cut,receiver,slot,parent,name) { } QPtrDict unplugged; };*/ KAction* KexiSharedActionHost::createSharedAction(const QString &text, const QString &pix_name, const KShortcut &cut, const char *name, KActionCollection* col, const char *subclassName) { if (subclassName==0) return createSharedActionInternal( new KAction(text, pix_name, cut, 0/*receiver*/, 0/*slot*/, col ? col : d->mainWin->actionCollection(), name) ); else if (qstricmp(subclassName,"KToggleAction")==0) return createSharedActionInternal( new KToggleAction(text, pix_name, cut, 0/*receiver*/, 0/*slot*/, col ? col : d->mainWin->actionCollection(), name) ); else if (qstricmp(subclassName,"KActionMenu")==0) return createSharedActionInternal( new KActionMenu(text, pix_name, col ? col : d->mainWin->actionCollection(), name) ); //TODO: more KAction subclasses return 0; } KAction* KexiSharedActionHost::createSharedAction( KStdAction::StdAction id, const char *name, KActionCollection* col) { return createSharedActionInternal( KStdAction::create( id, name, 0/*receiver*/, 0/*slot*/, col ? col : d->mainWin->actionCollection() ) ); } void KexiSharedActionHost::setActionVolatile( KAction *a, bool set ) { if (!set) { d->volatileActions.remove( a ); return; } if (d->volatileActions[ a ]) return; d->volatileActions.insert( a, new KexiVolatileActionData() ); } #include "kexisharedactionhost_p.moc" diff --git a/kexi/core/kexivalidator.cpp b/kexi/core/kexivalidator.cpp index 574458b133..e62ac2ec37 100644 --- a/kexi/core/kexivalidator.cpp +++ b/kexi/core/kexivalidator.cpp @@ -1,121 +1,121 @@ /* This file is part of the KDE project Copyright (C) 2004 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kexivalidator.h" KexiValidator::KexiValidator(QObject * parent, const char * name) : QValidator(parent,name) , m_acceptsEmptyValue(false) { } KexiValidator::~KexiValidator() { } KexiValidator::Result KexiValidator::check(const QString &valueName, const QVariant& v, QString &message, QString &details) { if (v.isNull() || v.type()==QVariant::String && v.toString().isEmpty()) { if (!m_acceptsEmptyValue) { message = KexiValidator::msgColumnNotEmpty().arg(valueName); return Error; } return Ok; } return internalCheck(valueName, v, message, details); } KexiValidator::Result KexiValidator::internalCheck(const QString & /*valueName*/, const QVariant& /*v*/, QString & /*message*/, QString & /*details*/) { return Error; } QValidator::State KexiValidator::validate ( QString & , int & ) const { return QValidator::Acceptable; } //----------------------------------------------------------- KexiMultiValidator::KexiMultiValidator(QObject* parent, const char * name) : KexiValidator(parent, name) { m_ownedSubValidators.setAutoDelete(true); } KexiMultiValidator::KexiMultiValidator(KexiValidator *validator, QObject * parent, const char * name) : KexiValidator(parent, name) { addSubvalidator(validator); } void KexiMultiValidator::addSubvalidator( KexiValidator* validator, bool owned ) { if (!validator) return; m_subValidators.append(validator); if (owned && !validator->parent()) m_ownedSubValidators.append(validator); } QValidator::State KexiMultiValidator::validate( QString & input, int & pos ) const { if (m_subValidators.isEmpty()) return Invalid; State s; QValueList::const_iterator it; - for ( it=m_subValidators.begin(); it!=m_subValidators.end(); ++it) { + for ( it=m_subValidators.constBegin(); it!=m_subValidators.constEnd(); ++it) { s = (*it)->validate(input, pos); if (s==Intermediate || s==Invalid) return s; } return Acceptable; } void KexiMultiValidator::fixup ( QString & input ) const { QValueList::const_iterator it; - for ( it=m_subValidators.begin(); it!=m_subValidators.end(); ++it) { + for ( it=m_subValidators.constBegin(); it!=m_subValidators.constEnd(); ++it) { (*it)->fixup(input); } } KexiValidator::Result KexiMultiValidator::internalCheck( const QString &valueName, const QVariant& v, QString &message, QString &details) { if (m_subValidators.isEmpty()) return Error; Result r; bool warning = false; QValueList::const_iterator it; for ( it=m_subValidators.begin(); it!=m_subValidators.end(); ++it) { r = (*it)->internalCheck(valueName, v, message, details); if (r==Error) return Error; if (r==Warning) warning = true; } return warning ? Warning : Ok; } diff --git a/kexi/kexidb/connection.cpp b/kexi/kexidb/connection.cpp index 383f903160..01b7114209 100644 --- a/kexi/kexidb/connection.cpp +++ b/kexi/kexidb/connection.cpp @@ -1,2346 +1,2346 @@ /* This file is part of the KDE project Copyright (C) 2003-2004 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kexi_utils.h" namespace KexiDB { //================================================ class ConnectionPrivate { public: ConnectionPrivate(Connection *conn) : m_conn(conn) , m_versionMajor(-1) , m_versionMinor(-1) , m_dont_remove_transactions(false) , m_skip_databaseExists_check_in_useDatabase(false) , m_parser(0) { } ~ConnectionPrivate() { delete m_parser; } void errorInvalidDBContents(const QString& details) { m_conn->setError( ERR_INVALID_DATABASE_CONTENTS, i18n("Invalid database contents. ")+details); } QString strItIsASystemObject() const { return i18n("It is a system object."); } Parser *parser() { return m_parser ? m_parser : (m_parser = new Parser(m_conn)); } Connection *m_conn; /*! Default transaction handle. If transactions are supported: Any operation on database (e.g. inserts) that is started without specifing transaction context, will be performed in the context of this transaction. */ Transaction m_default_trans; QValueList m_transactions; //! Version information for this connection. int m_versionMajor; int m_versionMinor; //! true if rollbackTransaction() and commitTransaction() shouldn't remove //! the transaction object from m_transactions list; used by closeDatabase() bool m_dont_remove_transactions : 1; //! used to avoid endless recursion between useDatabase() and databaseExists() //! when useTemporaryDatabaseIfNeeded() works bool m_skip_databaseExists_check_in_useDatabase : 1; protected: Parser *m_parser; }; }//namespace KexiDB //================================================ using namespace KexiDB; //! static: list of internal KexiDB system table names QStringList KexiDB_kexiDBSystemTableNames; Connection::Connection( Driver *driver, ConnectionData &conn_data ) : QObject() ,KexiDB::Object() ,m_data(&conn_data) ,m_tables_byname(101, false) ,m_queries_byname(101, false) ,d(new ConnectionPrivate(this)) ,m_driver(driver) ,m_is_connected(false) ,m_autoCommit(true) ,m_destructor_started(false) { m_tables.setAutoDelete(true); m_tables_byname.setAutoDelete(false);//m_tables is owner, not me m_kexiDBSystemtables.setAutoDelete(true);//only system tables m_queries.setAutoDelete(true); m_queries_byname.setAutoDelete(false);//m_queries is owner, not me m_cursors.setAutoDelete(true); // d->m_transactions.setAutoDelete(true); //reasonable sizes: TODO m_tables.resize(101); m_queries.resize(101); m_cursors.resize(101); // d->m_transactions.resize(101);//woohoo! so many transactions? m_sql.reserve(0x4000); } void Connection::destroy() { disconnect(); //do not allow the driver to touch me: I will kill myself. m_driver->d->connections.take( this ); } Connection::~Connection() { m_destructor_started = true; // KexiDBDbg << "Connection::~Connection()" << endl; delete d; /* if (m_driver) { if (m_is_connected) { //delete own table schemas m_tables.clear(); //delete own cursors: m_cursors.clear(); } //do not allow the driver to touch me: I will kill myself. m_driver->m_connections.take( this ); }*/ } bool Connection::connect() { clearError(); if (m_is_connected) { setError(ERR_ALREADY_CONNECTED, i18n("Connection already established.") ); return false; } if (!(m_is_connected = drv_connect())) { setError(m_driver->isFileDriver() ? i18n("Could not open \"%1\" project file.").arg(m_data->fileName()) : i18n("Could not connect to \"%1\" database server.").arg(m_data->serverInfoString()) ); } return m_is_connected; } bool Connection::isDatabaseUsed() { return !m_usedDatabase.isEmpty() && m_is_connected && drv_isDatabaseUsed(); } void Connection::clearError() { Object::clearError(); m_sql = QString::null; } bool Connection::disconnect() { clearError(); if (!m_is_connected) return true; if (!closeDatabase()) return false; bool ok = drv_disconnect(); if (ok) m_is_connected = false; return ok; } bool Connection::checkConnected() { if (m_is_connected) { clearError(); return true; } setError(ERR_NO_CONNECTION, i18n("Not connected to the database server.") ); return false; } bool Connection::checkIsDatabaseUsed() { if (isDatabaseUsed()) { clearError(); return true; } setError(ERR_NO_DB_USED, i18n("Currently no database is used.") ); return false; } QStringList Connection::databaseNames(bool also_system_db) { KexiDBDbg << "Connection::databaseNames("<isSystemDatabaseName(*it)) { KexiDBDbg << "add " << endl; non_system_list << (*it); } } return non_system_list; } bool Connection::drv_getDatabasesList( QStringList &list ) { list.clear(); return true; } bool Connection::drv_databaseExists( const QString &dbName, bool ignoreErrors ) { QStringList list = databaseNames(true);//also system if (error()) { return false; } if (list.find( dbName )==list.end()) { if (!ignoreErrors) setError(ERR_OBJECT_NOT_EXISTING, i18n("The database \"%1\" does not exist.").arg(dbName)); return false; } return true; } bool Connection::databaseExists( const QString &dbName, bool ignoreErrors ) { // KexiDBDbg << "Connection::databaseExists(" << dbName << "," << ignoreErrors << ")" << endl; if (!checkConnected()) return false; clearError(); if (m_driver->isFileDriver()) { //for file-based db: file must exists and be accessible //js: moved from useDatabase(): QFileInfo file(dbName); if (!file.exists() || ( !file.isFile() && !file.isSymLink()) ) { if (!ignoreErrors) setError(ERR_OBJECT_NOT_EXISTING, i18n("Database file \"%1\" does not exist.").arg(m_data->fileName()) ); return false; } if (!file.isReadable()) { if (!ignoreErrors) setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not readable.").arg(m_data->fileName()) ); return false; } if (!file.isWritable()) { if (!ignoreErrors) setError(ERR_ACCESS_RIGHTS, i18n("Database file \"%1\" is not writable.").arg(m_data->fileName()) ); return false; } return true; } QString tmpdbName; //some engines need to have opened any database before executing "create database" d->m_skip_databaseExists_check_in_useDatabase = true; if (!useTemporaryDatabaseIfNeeded(tmpdbName)) return false; d->m_skip_databaseExists_check_in_useDatabase = false; bool ret = drv_databaseExists(dbName, ignoreErrors); if (!tmpdbName.isEmpty()) { //whatever result is - now we have to close temporary opened database: if (!closeDatabase()) return false; } return ret; } #define createDatabase_CLOSE \ { if (!closeDatabase()) { \ setError(i18n("Database \"%1\" created but could not be closed after creation.").arg(dbName) ); \ return false; \ } } #define createDatabase_ERROR \ { createDatabase_CLOSE; return false; } /*! See doc/dev/kexidb_issues.txt document, chapter "Table schema, query schema, etc. storage" for database schema documentation (detailed description of kexi__* 'system' tables). */ bool Connection::createDatabase( const QString &dbName ) { if (!checkConnected()) return false; if (databaseExists( dbName )) { setError(ERR_OBJECT_EXISTS, i18n("Database \"%1\" already exists.").arg(dbName) ); return false; } if (m_driver->isSystemDatabaseName( dbName )) { setError(ERR_SYSTEM_NAME_RESERVED, i18n("Cannot create database \"%1\". This name is reserved for system database.").arg(dbName) ); return false; } if (m_driver->isFileDriver()) { //update connection data if filename differs m_data->setFileName( dbName ); } QString tmpdbName; //some engines need to have opened any database before executing "create database" if (!useTemporaryDatabaseIfNeeded(tmpdbName)) return false; //low-level create if (!drv_createDatabase( dbName )) { setError(i18n("Error creating database \"%1\" on the server.").arg(dbName) ); closeDatabase();//sanity return false; } if (!tmpdbName.isEmpty()) { //whatever result is - now we have to close temporary opened database: if (!closeDatabase()) return false; } if (!tmpdbName.isEmpty() || !m_driver->d->isDBOpenedAfterCreate) { //db need to be opened if (!useDatabase( dbName, false/*not yet kexi compatible!*/ )) { setError(i18n("Database \"%1\" created but could not be opened.").arg(dbName) ); return false; } } else { //just for the rule m_usedDatabase = dbName; } Transaction trans; if (m_driver->transactionsSupported()) { trans = beginTransaction(); if (!trans.active()) return false; } //not needed since closeDatabase() rollbacks transaction: TransactionGuard trans_g(this); // if (error()) // return false; //-create system tables schema objects if (!setupKexiDBSystemSchema()) return false; //-physically create system tables TableSchema *ts=m_kexiDBSystemtables.first(); while (ts) { if (!drv_createTable( ts->name() )) createDatabase_ERROR; ts = m_kexiDBSystemtables.next(); } //-create default part info if (!(ts = tableSchema("kexi__parts"))) createDatabase_ERROR; FieldList *fl = ts->subList("p_id", "p_name", "p_mime", "p_url"); if (!fl) createDatabase_ERROR; if (!insertRecord(*fl, QVariant(1), QVariant("Tables"), QVariant("kexi/table"), QVariant("http://koffice.org/kexi/"))) createDatabase_ERROR; if (!insertRecord(*fl, QVariant(2), QVariant("Queries"), QVariant("kexi/query"), QVariant("http://koffice.org/kexi/"))) createDatabase_ERROR; //-insert KexiDB version info: TableSchema *t_db = tableSchema("kexi__db"); if (!t_db) createDatabase_ERROR; if ( !insertRecord(*t_db, "kexidb_major_ver", KexiDB::versionMajor()) || !insertRecord(*t_db, "kexidb_minor_ver", KexiDB::versionMinor())) createDatabase_ERROR; if (trans.active() && !commitTransaction(trans)) createDatabase_ERROR; createDatabase_CLOSE; return true; } #undef createDatabase_CLOSE #undef createDatabase_ERROR bool Connection::useDatabase( const QString &dbName, bool kexiCompatible ) { kdDebug() << "Connection::useDatabase(" << dbName << "," << kexiCompatible <<")" << endl; if (!checkConnected()) return false; if (dbName.isEmpty()) return false; QString my_dbName = dbName; // if (my_dbName.isEmpty()) { // const QStringList& db_lst = databaseNames(); // if (!db_lst.isEmpty()) // my_dbName = db_lst.first(); // } if (m_usedDatabase == my_dbName) return true; //already used if (!d->m_skip_databaseExists_check_in_useDatabase) { if (!databaseExists(my_dbName, false /*don't ignore errors*/)) return false; //database must exist } if (!m_usedDatabase.isEmpty() && !closeDatabase()) //close db if already used return false; m_usedDatabase = ""; if (!drv_useDatabase( my_dbName )) { setError( i18n("Opening database \"%1\" failed").arg( my_dbName ) ); return false; } //-create system tables schema objects if (!setupKexiDBSystemSchema()) return false; if (kexiCompatible && my_dbName.lower()!=anyAvailableDatabaseName().lower()) { //-get global database information int num; static QString notfound_str = i18n("\"%1\" database property not found"); if (!querySingleNumber( "select db_value from kexi__db where db_property=" + m_driver->escapeString(QString("kexidb_major_ver")), num)) { d->errorInvalidDBContents(notfound_str.arg("kexidb_major_ver")); return false; } d->m_versionMajor = num; if (!querySingleNumber( "select db_value from kexi__db where db_property=" + m_driver->escapeString(QString("kexidb_minor_ver")), num)) { d->errorInvalidDBContents(notfound_str.arg("kexidb_minor_ver")); return false; } d->m_versionMinor = num; //** error if major version does not match if (m_driver->versionMajor()!=KexiDB::versionMajor()) { setError(ERR_INCOMPAT_DATABASE_VERSION, i18n("Database version (%1) does not match Kexi application's version (%2)") .arg( QString("%1.%2").arg(versionMajor()).arg(versionMinor()) ) .arg( QString("%1.%2").arg(KexiDB::versionMajor()).arg(KexiDB::versionMinor()) ) ); return false; } if (m_driver->versionMinor()!=KexiDB::versionMinor()) { //js TODO: COMPATIBILITY CODE HERE! //js TODO: CONVERSION CODE HERE (or signal that conversion is needed) } } m_usedDatabase = my_dbName; return true; } bool Connection::closeDatabase() { if (m_usedDatabase.isEmpty()) return true; //no db used if (!checkConnected()) return true; bool ret = true; /*! \todo (js) add CLEVER algorithm here for nested transactions */ if (m_driver->transactionsSupported()) { //rollback all transactions QValueList::ConstIterator it; d->m_dont_remove_transactions=true; //lock! for (it=d->m_transactions.constBegin(); it!= d->m_transactions.constEnd(); ++it) { if (!rollbackTransaction(*it)) {//rollback as much as you can, don't stop on prev. errors ret = false; } else { KexiDBDbg << "Connection::closeDatabase(): transaction rolled back!" << endl; KexiDBDbg << "Connection::closeDatabase(): trans.refcount==" << ((*it).m_data ? QString::number((*it).m_data->refcount) : "(null)") << endl; } } d->m_dont_remove_transactions=false; //unlock! d->m_transactions.clear(); //free trans. data } //delete own cursors: m_cursors.clear(); //delete own schemas m_tables.clear(); m_kexiDBSystemtables.clear(); m_queries.clear(); if (!drv_closeDatabase()) return false; m_usedDatabase = ""; // KexiDBDbg << "Connection::closeDatabase(): " << ret << endl; return ret; } bool Connection::useTemporaryDatabaseIfNeeded(QString &tmpdbName) { if (!m_driver->isFileDriver() && m_driver->beh->USING_DATABASE_REQUIRED_TO_CONNECT && !isDatabaseUsed()) { //we have no db used, but it is required by engine to have used any! tmpdbName = anyAvailableDatabaseName(); if (tmpdbName.isEmpty()) { setError(ERR_NO_DB_USED, i18n("Cannot find any database for temporary connection.") ); return false; } if (!useDatabase(tmpdbName, false)) { setError(errorNum(), i18n("Error during starting temporary connection using \"%1\" database name.") .arg(tmpdbName) ); return false; } } return true; } bool Connection::dropDatabase( const QString &dbName ) { if (!checkConnected()) return false; QString dbToDrop; if (dbName.isEmpty() && m_usedDatabase.isEmpty()) { if (!m_driver->isFileDriver() || (m_driver->isFileDriver() && m_data->fileName().isEmpty()) ) { setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot drop database - name not specified.") ); return false; } //this is a file driver so reuse previously passed filename dbToDrop = m_data->fileName(); } else { if (dbName.isEmpty()) { dbToDrop = m_usedDatabase; } else { if (m_driver->isFileDriver()) //lets get full path dbToDrop = QFileInfo(dbName).absFilePath(); else dbToDrop = dbName; } } if (dbToDrop.isEmpty()) { setError(ERR_NO_NAME_SPECIFIED, i18n("Cannot delete database - name not specified.") ); return false; } if (m_driver->isSystemDatabaseName( dbToDrop )) { setError(ERR_SYSTEM_NAME_RESERVED, i18n("Cannot delete system database \"%1\".").arg(dbToDrop) ); return false; } if (isDatabaseUsed() && m_usedDatabase == dbToDrop) { //we need to close database because cannot drop used this database if (!closeDatabase()) return false; } QString tmpdbName; //some engines need to have opened any database before executing "drop database" if (!useTemporaryDatabaseIfNeeded(tmpdbName)) return false; //ok, now we have access to dropping bool ret = drv_dropDatabase( dbToDrop ); if (!tmpdbName.isEmpty()) { //whatever result is - now we have to close temporary opened database: if (!closeDatabase()) return false; } return ret; } QStringList Connection::tableNames(bool also_system_tables) { QStringList list; if (!isDatabaseUsed()) return list; Cursor *c = executeQuery(QString( "select o_name from kexi__objects where o_type=%1").arg(KexiDB::TableObjectType)); if (!c) return list; for (c->moveFirst(); !c->eof(); c->moveNext()) { QString tname = c->value(0).toString(); //kexi__objects.o_name if (Kexi::isIdentifier( tname )) { list.append(tname); } } deleteCursor(c); if (also_system_tables) { list += Connection::kexiDBSystemTableNames(); } return list; } //! \todo (js): this will depend on KexiDB lib version const QStringList& Connection::kexiDBSystemTableNames() { if (KexiDB_kexiDBSystemTableNames.isEmpty()) { KexiDB_kexiDBSystemTableNames << "kexi__objects" << "kexi__objectdata" << "kexi__fields" // << "kexi__querydata" // << "kexi__queryfields" // << "kexi__querytables" << "kexi__db" ; } return KexiDB_kexiDBSystemTableNames; } int Connection::versionMajor() const { return d->m_versionMinor; } int Connection::versionMinor() const { return d->m_versionMinor; } QValueList Connection::queryIds() { return objectIds(KexiDB::QueryObjectType); } QValueList Connection::objectIds(int objType) { QValueList list; if (!isDatabaseUsed()) return list; Cursor *c = executeQuery(QString("select o_id, o_name from kexi__objects where o_type=%1").arg(objType)); if (!c) return list; for (c->moveFirst(); !c->eof(); c->moveNext()) { QString tname = c->value(1).toString(); //kexi__objects.o_name if (Kexi::isIdentifier( tname )) { list.append(c->value(0).toInt()); //kexi__objects.o_id } } deleteCursor(c); return list; /* switch (objType) { case KexiDB::TableObject: return tableNames(); case KexiDB::QueryObject: return queryNames(); default: ; } return list;*/ } QString Connection::createTableStatement( const KexiDB::TableSchema& tableSchema ) const { // Each SQL identifier needs to be escaped in the generated query. QString sql; sql.reserve(4096); sql = "CREATE TABLE " + escapeIdentifier(tableSchema.name()) + " ("; bool first=true; Field::ListIterator it( tableSchema.m_fields ); Field *field; for (;(field = it.current())!=0; ++it) { if (first) first = false; else sql += ", "; QString v = escapeIdentifier(field->m_name) + " "; if (field->isAutoIncrement() && m_driver->beh->SPECIAL_AUTO_INCREMENT_DEF) { v += m_driver->beh->AUTO_INCREMENT_FIELD_OPTION; } else { v += m_driver->sqlTypeName(field->type()); if (field->isUnsigned()) v += (" " + m_driver->beh->UNSIGNED_TYPE_KEYWORD); if (field->m_length>0) v += QString("(%1)").arg(field->m_length); if (field->isAutoIncrement()) v += (" " + m_driver->beh->AUTO_INCREMENT_FIELD_OPTION); //TODO: here is automatically a single-field key created if (field->isPrimaryKey()) v += " PRIMARY KEY"; if (!field->isPrimaryKey() && field->isUniqueKey()) v += " UNIQUE"; #ifndef Q_WS_WIN #warning IS this ok for all engines?: if (!field->isAutoIncrement() && !field->isPrimaryKey() && field->isNotNull()) #endif if (!field->isAutoIncrement() && !field->isPrimaryKey() && field->isNotNull()) v += " NOT NULL"; //only add not null option if no autocommit is set if (field->defaultValue().isValid()) v += QString(" DEFAULT ") + m_driver->valueToSQL( field, field->m_defaultValue ); } sql += v; } sql += ")"; return sql; } //yeah, it is very efficient: #define C_A(a) , const QVariant& c ## a #define V_A0 m_driver->valueToSQL( tableSchema.field(0), c0 ) #define V_A(a) +","+m_driver->valueToSQL( \ tableSchema.field(a) ? tableSchema.field(a)->type() : Field::Text, c ## a ) #define C_INS_REC(args, vals) \ bool Connection::insertRecord(KexiDB::TableSchema &tableSchema args) {\ KexiDBDbg << "******** " << QString("INSERT INTO ") + \ escapeIdentifier(tableSchema.name()) + \ " VALUES (" + vals + ")" <valueToSQL( flist->first(), c0 ); #define V_A( a ) value += ("," + m_driver->valueToSQL( flist->next(), c ## a )); //#define V_ALAST( a ) valueToSQL( flist->last(), c ## a ) #define C_INS_REC(args, vals) \ bool Connection::insertRecord(FieldList& fields args) \ { \ QString value; \ Field::List *flist = fields.fields(); \ vals \ return executeSQL( \ QString("INSERT INTO ") + \ ((fields.fields()->first() && fields.fields()->first()->table()) ? \ escapeIdentifier(fields.fields()->first()->table()->name()) : \ "??") \ + "(" + fields.sqlFieldsList(m_driver) + ") VALUES (" + value + ")" \ ); \ } C_INS_REC_ALL #undef C_A #undef V_A #undef V_ALAST #undef C_INS_REC #undef C_INS_REC_ALL bool Connection::insertRecord(TableSchema &tableSchema, QValueList& values) { // Each SQL identifier needs to be escaped in the generated query. Field::List *fields = tableSchema.fields(); Field *f = fields->first(); // QString s_val; // s_val.reserve(4096); m_sql = QString::null; QValueList::ConstIterator it = values.constBegin(); int i=0; while (f && (it!=values.end())) { if (m_sql.isEmpty()) m_sql = QString("INSERT INTO ") + escapeIdentifier(tableSchema.name()) + " VALUES ("; else m_sql += ","; m_sql += m_driver->valueToSQL( f, *it ); KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl; ++it; f=fields->next(); } m_sql += ")"; KexiDBDbg<<"******** "<< m_sql << endl; return executeSQL(m_sql); } bool Connection::insertRecord(FieldList& fields, QValueList& values) { // Each SQL identifier needs to be escaped in the generated query. Field::List *flist = fields.fields(); Field *f = flist->first(); if (!f) return false; // QString s_val; // s_val.reserve(4096); m_sql = QString::null; QValueList::ConstIterator it = values.constBegin(); int i=0; while (f && (it!=values.constEnd())) { if (m_sql.isEmpty()) m_sql = QString("INSERT INTO ") + escapeIdentifier(flist->first()->table()->name()) + "(" + fields.sqlFieldsList(m_driver) + ") VALUES ("; else m_sql += ","; m_sql += m_driver->valueToSQL( f, *it ); KexiDBDbg << "val" << i++ << ": " << m_driver->valueToSQL( f, *it ) << endl; ++it; f=flist->next(); } m_sql += ")"; return executeSQL(m_sql); } bool Connection::executeSQL( const QString& statement ) { m_sql = statement; //remember for error handling if (!drv_executeSQL( m_sql )) { m_errorSql = statement; setError(ERR_SQL_EXECUTION_ERROR, i18n("Error while executing SQL statement.")); return false; } return true; } QString Connection::selectStatement( KexiDB::QuerySchema& querySchema ) const { //"SELECT FROM ..." is theoretically allowed " //if (querySchema.fieldCount()<1) // return QString::null; // Each SQL identifier needs to be escaped in the generated query. if (!querySchema.statement().isEmpty()) return querySchema.statement(); QString sql; sql.reserve(4096); // Field::List *fields = querySchema.fields(); uint number = 0; // for (Field *f = fields->first(); f; f = fields->next(), number++) { Field *f; for (Field::ListIterator it = querySchema.fieldsIterator(); (f = it.current()); ++it, number++) { if (querySchema.isColumnVisible(number)) { if (!sql.isEmpty()) sql += QString::fromLatin1(", "); if (f->isQueryAsterisk()) { if (static_cast(f)->isSingleTableAsterisk()) //single-table * sql += (escapeIdentifier(f->table()->name() + QString::fromLatin1(".*"))); else //all-tables * sql += QString::fromLatin1("*"); } else { if (f->isExpression()) { sql += f->expression()->toString(); } else { if (!f->table()) //sanity check return QString::null; QString tableName; int tablePosition = querySchema.tableBoundToColumn(number); if (tablePosition>=0) tableName = querySchema.tableAlias(tablePosition); if (tableName.isEmpty()) tableName = f->table()->name(); sql += (escapeIdentifier(tableName) + "." + escapeIdentifier(f->name())); } QString aliasString = QString(querySchema.columnAlias(number)); if (!aliasString.isEmpty()) sql += (QString::fromLatin1(" AS ") + aliasString); //TODO: add option that allows to omit "AS" keyword } } } sql.prepend("SELECT "); TableSchema::List* tables = querySchema.tables(); if (tables && !tables->isEmpty()) { sql += QString::fromLatin1(" FROM "); QString s_from; TableSchema *table; number = 0; for (TableSchema::ListIterator it(*tables); (table = it.current()); ++it, number++) { if (!s_from.isEmpty()) s_from += QString::fromLatin1(", "); s_from += escapeIdentifier(table->name()); QString aliasString = QString(querySchema.tableAlias(number)); if (!aliasString.isEmpty()) s_from += (QString::fromLatin1(" AS ") + aliasString); } sql += s_from; } QString s_where; s_where.reserve(4096); //JOINS //@todo: we're using WHERE for joins now; user INNER/LEFT/RIGHT JOIN later Relationship *rel; bool wasWhere = false; //for later use for (Relationship::ListIterator it(*querySchema.relationships()); (rel = it.current()); ++it) { if (s_where.isEmpty()) { wasWhere = true; } else s_where += QString::fromLatin1(" AND "); Field::Pair *pair; QString s_where_sub; for (QPtrListIterator p_it(*rel->fieldPairs()); (pair = p_it.current()); ++p_it) { if (!s_where_sub.isEmpty()) s_where_sub += QString::fromLatin1(" AND "); s_where_sub += (pair->first->table()->name() + QString::fromLatin1(".") + escapeIdentifier(pair->first->name()) + QString::fromLatin1(" = ") + escapeIdentifier(pair->second->table()->name()) + QString::fromLatin1(".") + escapeIdentifier(pair->second->name())); } if (rel->fieldPairs()->count()>1) { s_where_sub.prepend("("); s_where_sub += QString::fromLatin1(")"); } s_where += s_where_sub; } //EXPLICITY SPECIFIER WHERE EXPRESION if (querySchema.whereExpression()) { if (wasWhere) { //TODO: () are not always needed s_where = "(" + s_where + ") AND (" + querySchema.whereExpression()->toString() + ")"; } else { s_where = querySchema.whereExpression()->toString(); } } if (!s_where.isEmpty()) sql += QString::fromLatin1(" WHERE ") + s_where; //! \todo (js) add WHERE and other sql parts //(use wasWhere here) return sql; } QString Connection::selectStatement( KexiDB::TableSchema& tableSchema ) const { return selectStatement( *tableSchema.query() ); } Field* Connection::findSystemFieldName(KexiDB::FieldList* fieldlist) { Field *f = fieldlist->fields()->first(); while (f) { if (m_driver->isSystemFieldName( f->name() )) return f; f = fieldlist->fields()->next(); } return 0; } /*! TODO: ? This goes pear-shaped if the query doesn't manage to fill rdata[0]. */ int Connection::lastInsertedAutoIncValue(const QString& aiFieldName, const QString& tableName) { int row_id = drv_lastInsertRowID(); if (m_driver->beh->ROW_ID_FIELD_RETURNS_LAST_AUTOINCREMENTED_VALUE) return row_id; RowData rdata; if (row_id<=0 || !querySingleRecord( QString("select ")+aiFieldName+" from "+tableName+" where "+m_driver->beh->ROW_ID_FIELD_NAME +"="+QString::number(row_id), rdata)) { KexiDBDbg << "Connection::lastInsertedAutoIncValue(): row_id<=0 || !querySingleRecord()" << endl; return -1; } return rdata[0].toInt(); } int Connection::lastInsertedAutoIncValue(const QString& aiFieldName, const KexiDB::TableSchema& table) { return lastInsertedAutoIncValue(aiFieldName,table.name()); } #define createTable_ERR \ { KexiDBDbg << "Connection::createTable(): ERROR!" <fieldCount()<1) { setError(ERR_CANNOT_CREATE_EMPTY_OBJECT, i18n("Cannot create table without fields.")); return false; } if (m_driver->isSystemObjectName( tableSchema->name() )) { setError(ERR_SYSTEM_NAME_RESERVED, i18n("System name \"%1\" cannot be used as table name.") .arg(tableSchema->name())); return false; } Field *sys_field = findSystemFieldName(tableSchema); if (sys_field) { setError(ERR_SYSTEM_NAME_RESERVED, i18n("System name \"%1\" cannot be used as one of fields in \"%2\" table.") .arg(sys_field->name()).arg(tableSchema->name())); return false; } const QString &tableName = tableSchema->name().lower(); if (replaceExisting) { //get previous table (do not retrieve, though) KexiDB::TableSchema *existingTable = m_tables_byname[tableName]; if (existingTable) { if (existingTable == tableSchema) { setError(ERR_OBJECT_EXISTS, i18n("Could not create the same table \"%1\" twice.").arg(tableSchema->name()) ); return false; } //TODO(js): update any structure (e.g. queries) that depend on this table! if (existingTable->id()>0) tableSchema->m_id = existingTable->id(); //copy id from existing table if (!dropTable( existingTable )) return false; } } else { if (this->tableSchema( tableSchema->name() ) != 0) { setError(ERR_OBJECT_EXISTS, i18n("Table \"%1\" already exists.").arg(tableSchema->name()) ); return false; } } /* if (replaceExisting) { //get previous table (do not retrieve, though) KexiDB::TableSchema *existingTable = m_tables_byname.take(name); if (oldTable) { }*/ Transaction trans; if (!beginAutoCommitTransaction(trans)) return false; TransactionGuard tg(trans); if (!drv_createTable(*tableSchema)) createTable_ERR; //add schema data to kexi__* tables if (!storeObjectSchemaData( *tableSchema, true )) createTable_ERR; /* TableSchema *ts = m_tables_byname["kexi__objects"]; if (!ts) return false; FieldList *fl = ts->subList("o_type", "o_name", "o_caption", "o_help"); if (!fl) return false; if (!insertRecord(*fl, QVariant(tableSchema->type()), QVariant(tableSchema->name()), QVariant(tableSchema->caption()), QVariant(tableSchema->description()) )) createTable_ERR; delete fl;*/ // if (!insertRecord(*ts, QVariant()/*autoinc*/, QVariant(tableSchema->type()), QVariant(tableSchema->name()), // QVariant(tableSchema->caption()), QVariant(tableSchema->description()))) // createTable_ERR; /* int obj_id = lastInsertedAutoIncValue("o_id",*ts); if (obj_id<=0) createTable_ERR; KexiDBDbg << "######## obj_id == " << obj_id << endl;*/ TableSchema *ts = m_tables_byname["kexi__fields"]; if (!ts) return false; //for sanity: remove field info is any for this table id if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id())) return false; FieldList *fl = ts->subList( "t_id", "f_type", "f_name", "f_length", "f_precision", "f_constraints", "f_options", "f_default", "f_order", "f_caption", "f_help" ); if (!fl) return false; Field::List *fields = tableSchema->fields(); Field *f = fields->first(); int order = 0; while (f) { QValueList vals; vals << QVariant(tableSchema->id())//obj_id) << QVariant(f->type()) << QVariant(f->name()) << QVariant(f->length()) << QVariant(f->precision()) << QVariant(f->constraints()) << QVariant(f->options()) << QVariant(f->defaultValue()) << QVariant(f->order()) << QVariant(f->caption()) << QVariant(f->description()); if (!insertRecord(*fl, vals )) createTable_ERR; f = fields->next(); order++; } delete fl; //finally: /* if (replaceExisting) { if (existingTable) { m_tables.take(existingTable->id()); delete existingTable; } }*/ //store objects locally: m_tables.insert(tableSchema->id(), tableSchema); m_tables_byname.insert(tableSchema->name().lower(), tableSchema); return commitAutoCommitTransaction(trans); } bool Connection::removeObject( uint objId ) { clearError(); //remove table schema from kexi__* tables if (!KexiDB::deleteRow(*this, m_tables_byname["kexi__objects"], "o_id", objId) //schema entry || !KexiDB::deleteRow(*this, m_tables_byname["kexi__objectdata"], "o_id", objId)) {//data blocks setError(ERR_DELETE_SERVER_ERROR, i18n("Could not remove object's data.")); return false; } return true; } //! Drops a table corresponding to the name in the given schema /*! Drops a table according to the name given by the TableSchema, removing the table and column definitions to kexi__* tables. Checks first that the table is not a system table. TODO: Should check that a database is currently in use? (c.f. createTable) */ bool Connection::dropTable( KexiDB::TableSchema* tableSchema ) { // Each SQL identifier needs to be escaped in the generated query. clearError(); if (!tableSchema) return false; //sanity checks: if (m_driver->isSystemObjectName( tableSchema->name() )) { setError(ERR_SYSTEM_NAME_RESERVED, i18n("Table \"%1\" cannot be removed.") .arg(tableSchema->name()) + "
" + d->strItIsASystemObject()); return false; } Transaction trans; if (!beginAutoCommitTransaction(trans)) return false; TransactionGuard tg(trans); m_sql = "DROP TABLE " + escapeIdentifier(tableSchema->name()); if (!executeSQL(m_sql)) return false; TableSchema *ts = m_tables_byname["kexi__fields"]; if (!KexiDB::deleteRow(*this, ts, "t_id", tableSchema->id())) //field entries return false; //remove table schema from kexi__objects table if (!removeObject( tableSchema->id() )) { return false; } //TODO(js): update any structure (e.g. queries) that depend on this table! m_tables_byname.remove(tableSchema->name().lower()); m_tables.remove(tableSchema->id()); return commitAutoCommitTransaction(trans); } bool Connection::dropTable( const QString& table ) { clearError(); TableSchema* ts = tableSchema( table ); if (!ts) { setError(ERR_OBJECT_NOT_EXISTING, i18n("Table \"%1\" does not exist.") .arg(table)); return false; } return dropTable(ts); } bool Connection::alterTable( TableSchema& tableSchema, TableSchema& newTableSchema ) { clearError(); if (&tableSchema == &newTableSchema) { setError(ERR_OBJECT_THE_SAME, i18n("Could not alter table \"%1\" using the same table.") .arg(tableSchema.name())); return false; } //TODO(js): implement real altering //TODO(js): update any structure (e.g. query) that depend on this table! bool ok, empty; #if 0//TODO ucomment: empty = isEmpty( tableSchema, ok ) && ok; #else empty = true; #endif if (empty) { ok = createTable(&newTableSchema, true/*replace*/); } return ok; } bool Connection::dropQuery( KexiDB::QuerySchema* querySchema ) { clearError(); if (!querySchema) return false; Transaction trans; if (!beginAutoCommitTransaction(trans)) return false; TransactionGuard tg(trans); /* TableSchema *ts = m_tables_byname["kexi__querydata"]; if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id())) return false; ts = m_tables_byname["kexi__queryfields"]; if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id())) return false; ts = m_tables_byname["kexi__querytables"]; if (!KexiDB::deleteRow(*this, ts, "q_id", querySchema->id())) return false;*/ //remove query schema from kexi__objects table if (!removeObject( querySchema->id() )) { return false; } //TODO(js): update any structure that depend on this table! m_queries_byname.remove(querySchema->name().lower()); m_queries.remove(querySchema->id()); return commitAutoCommitTransaction(trans); } bool Connection::dropQuery( const QString& query ) { clearError(); QuerySchema* qs = querySchema( query ); if (!qs) { setError(ERR_OBJECT_NOT_EXISTING, i18n("Query \"%1\" does not exist.") .arg(query)); return false; } return dropQuery(qs); } bool Connection::drv_createTable( const KexiDB::TableSchema& tableSchema ) { m_sql = createTableStatement(tableSchema); KexiDBDbg<<"******** "<d->features & Driver::IgnoreTransactions) return true; if (!m_autoCommit) return true; // commit current transaction (if present) for drivers // that allow single transaction per connection if (m_driver->d->features & Driver::SingleTransactions) { if (!commitTransaction(d->m_default_trans, true)) return false; //we have real error } else if (!(m_driver->d->features & Driver::MultipleTransactions)) { return true; //no trans. supported at all - just return } trans=beginTransaction(); return !error(); } bool Connection::commitAutoCommitTransaction(const Transaction& trans) { if (m_driver->d->features & Driver::IgnoreTransactions) return true; if (trans.isNull() || !m_driver->transactionsSupported()) return true; return commitTransaction(trans, true); } bool Connection::rollbackAutoCommitTransaction(const Transaction& trans) { if (trans.isNull() || !m_driver->transactionsSupported()) return true; return rollbackTransaction(trans); } #define SET_ERR_TRANS_NOT_SUPP \ { setError(ERR_UNSUPPORTED_DRV_FEATURE, \ i18n("Transactions are not supported for \"%1\" driver.").arg(m_driver->name() )); } #define SET_BEGIN_TR_ERROR \ { if (!error()) \ setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Begin transaction failed")); } Transaction Connection::beginTransaction() { if (!isDatabaseUsed()) return Transaction::null; Transaction trans; if (m_driver->d->features & Driver::IgnoreTransactions) { //we're creating dummy transaction data here, //so it will look like active trans.m_data = new TransactionData(this); d->m_transactions.append(trans); return trans; } if (m_driver->d->features & Driver::SingleTransactions) { if (d->m_default_trans.active()) { setError(ERR_TRANSACTION_ACTIVE, i18n("Transaction already started.") ); return Transaction::null; } if (!(trans.m_data = drv_beginTransaction())) { SET_BEGIN_TR_ERROR; return Transaction::null; } d->m_default_trans = trans; d->m_transactions.append(trans); return d->m_default_trans; } if (m_driver->d->features & Driver::MultipleTransactions) { if (!(trans.m_data = drv_beginTransaction())) { SET_BEGIN_TR_ERROR; return Transaction::null; } d->m_transactions.append(trans); return trans; } SET_ERR_TRANS_NOT_SUPP; return Transaction::null; } bool Connection::commitTransaction(const Transaction trans, bool ignore_inactive) { if (!isDatabaseUsed()) return false; if ( !m_driver->transactionsSupported() && !(m_driver->d->features & Driver::IgnoreTransactions)) { SET_ERR_TRANS_NOT_SUPP; return false; } Transaction t = trans; if (!t.active()) { //try default tr. if (!d->m_default_trans.active()) { if (ignore_inactive) return true; setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") ); return false; } t = d->m_default_trans; d->m_default_trans = Transaction::null; //now: no default tr. } bool ret = true; if (! (m_driver->d->features & Driver::IgnoreTransactions) ) ret = drv_commitTransaction(t.m_data); if (t.m_data) t.m_data->m_active = false; //now this transaction if inactive if (!d->m_dont_remove_transactions) //true=transaction obj will be later removed from list d->m_transactions.remove(t); if (!ret && !error()) setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on commit transaction")); return ret; } bool Connection::rollbackTransaction(const Transaction trans, bool ignore_inactive) { if (!isDatabaseUsed()) return false; if ( !m_driver->transactionsSupported() && !(m_driver->d->features & Driver::IgnoreTransactions)) { SET_ERR_TRANS_NOT_SUPP; return false; } Transaction t = trans; if (!t.active()) { //try default tr. if (!d->m_default_trans.active()) { if (ignore_inactive) return true; setError(ERR_NO_TRANSACTION_ACTIVE, i18n("Transaction not started.") ); return false; } t = d->m_default_trans; d->m_default_trans = Transaction::null; //now: no default tr. } bool ret = true; if (! (m_driver->d->features & Driver::IgnoreTransactions) ) ret = drv_rollbackTransaction(t.m_data); if (t.m_data) t.m_data->m_active = false; //now this transaction if inactive if (!d->m_dont_remove_transactions) //true=transaction obj will be later removed from list d->m_transactions.remove(t); if (!ret && !error()) setError(ERR_ROLLBACK_OR_COMMIT_TRANSACTION, i18n("Error on rollback transaction")); return ret; } #undef SET_ERR_TRANS_NOT_SUPP #undef SET_BEGIN_TR_ERROR /*bool Connection::duringTransaction() { return drv_duringTransaction(); }*/ Transaction& Connection::defaultTransaction() const { return d->m_default_trans; } void Connection::setDefaultTransaction(const Transaction& trans) { if (!checkIsDatabaseUsed()) return; if ( !(m_driver->d->features & Driver::IgnoreTransactions) && (!trans.active() || !m_driver->transactionsSupported()) ) { return; } d->m_default_trans = trans; } const QValueList& Connection::transactions() { return d->m_transactions; } bool Connection::autoCommit() const { return m_autoCommit; } bool Connection::setAutoCommit(bool on) { if (m_autoCommit == on || m_driver->d->features & Driver::IgnoreTransactions) return true; if (!drv_setAutoCommit(on)) return false; m_autoCommit = on; return true; } TransactionData* Connection::drv_beginTransaction() { QString old_sql = m_sql; //don't if (!executeSQL( "BEGIN" )) return 0; return new TransactionData(this); } bool Connection::drv_commitTransaction(TransactionData *) { return executeSQL( "COMMIT" ); } bool Connection::drv_rollbackTransaction(TransactionData *) { return executeSQL( "ROLLBACK" ); } bool Connection::drv_setAutoCommit(bool /*on*/) { return true; } Cursor* Connection::executeQuery( const QString& statement, uint cursor_options ) { if (statement.isEmpty()) return 0; Cursor *c = prepareQuery( statement, cursor_options ); if (!c) return 0; if (!c->open()) {//err - kill that setError(c); delete c; return 0; } return c; } Cursor* Connection::executeQuery( QuerySchema& query, uint cursor_options ) { Cursor *c = prepareQuery( query, cursor_options ); if (!c) return 0; if (!c->open()) {//err - kill that setError(c); delete c; return 0; } return c; } Cursor* Connection::executeQuery( TableSchema& table, uint cursor_options ) { return executeQuery( *table.query(), cursor_options ); } /*Cursor* Connection::prepareQuery( QuerySchema& query, uint cursor_options ) { Cursor *c = prepareQuery( query, cursor_options ); if (!c) return 0; if (!c->open()) {//err - kill that setError(c); delete c; return 0; } return c; }*/ Cursor* Connection::prepareQuery( TableSchema& table, uint cursor_options ) { return prepareQuery( *table.query(), cursor_options ); } bool Connection::deleteCursor(Cursor *cursor) { if (!cursor) return false; if (cursor->connection()!=this) {//illegal call KexiDBDbg << "Connection::deleteCursor(): WARNING! Cannot delete the cursor not owned by the same connection!" << endl; return false; } bool ret = cursor->close(); delete cursor; return ret; } bool Connection::setupObjectSchemaData( const RowData &data, SchemaData &sdata ) { //not found: retrieve schema /* KexiDB::Cursor *cursor; if (!(cursor = executeQuery( QString("select * from kexi__objects where o_id='%1'").arg(objId) ))) return false; if (!cursor->moveFirst()) { deleteCursor(cursor); return false; }*/ //if (!ok) { //deleteCursor(cursor); //return 0; // } bool ok; sdata.m_id = data[0].toInt(&ok); if (!ok) { return false; } sdata.m_name = data[2].toString(); if (!Kexi::isIdentifier( sdata.m_name )) { setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"").arg(sdata.m_name)); return false; } sdata.m_caption = data[3].toString(); sdata.m_desc = data[4].toString(); // KexiDBDbg<<"@@@ Connection::setupObjectSchemaData() == " << sdata.schemaDataDebugString() << endl; return true; } bool Connection::loadObjectSchemaData( int objectID, SchemaData &sdata ) { RowData data; if (!querySingleRecord(QString("select o_id, o_type, o_name, o_caption, o_desc from kexi__objects where o_id=%1").arg(objectID), data)) return false; return setupObjectSchemaData( data, sdata ); } bool Connection::loadObjectSchemaData( int objectType, const QString& objectName, SchemaData &sdata ) { RowData data; if (!querySingleRecord(QString("select o_id, o_type, o_name, o_caption, o_desc from kexi__objects where o_type=%1 and o_name=%2") .arg(objectType).arg(m_driver->valueToSQL(Field::Text, objectName)), data)) return false; return setupObjectSchemaData( data, sdata ); } bool Connection::storeObjectSchemaData( SchemaData &sdata, bool newObject ) { TableSchema *ts = m_tables_byname["kexi__objects"]; if (!ts) return false; if (newObject) { FieldList *fl; bool ok; if (sdata.id()<=0) {//get new ID fl = ts->subList("o_type", "o_name", "o_caption", "o_desc"); ok = fl!=0; if (ok && !insertRecord(*fl, QVariant(sdata.type()), QVariant(sdata.name()), QVariant(sdata.caption()), QVariant(sdata.description()) )) ok = false; delete fl; if (!ok) return false; //fetch newly assigned ID int obj_id = lastInsertedAutoIncValue("o_id",*ts); KexiDBDbg << "######## NEW obj_id == " << obj_id << endl; if (obj_id<=0) return false; sdata.m_id = obj_id; return true; } else { fl = ts->subList("o_id", "o_type", "o_name", "o_caption", "o_desc"); ok = fl!=0; if (ok && !insertRecord(*fl, QVariant(sdata.id()), QVariant(sdata.type()), QVariant(sdata.name()), QVariant(sdata.caption()), QVariant(sdata.description()) )) ok = false; delete fl; return ok; } } //existing object: return executeSQL(QString("update kexi__objects set o_type=%2, o_caption=%3, o_desc=%4 where o_id=%1") .arg(sdata.id()).arg(sdata.type()) .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.caption())) .arg(m_driver->valueToSQL(KexiDB::Field::Text, sdata.description())) ); } //! Query database for a single record, storing entire row bool Connection::querySingleRecord(const QString& sql, RowData &data) { KexiDB::Cursor *cursor; m_sql = sql + " LIMIT 1"; // is this safe? if (!(cursor = executeQuery( m_sql ))) { KexiDBDbg << "Connection::querySingleRecord(): !executeQuery()" << endl; return false; } if (!cursor->moveFirst() || cursor->eof()) { KexiDBDbg << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof()" << endl; deleteCursor(cursor); return false; } cursor->storeCurrentRow(data); return deleteCursor(cursor); } //! Query database for a single record, storing one string field bool Connection::querySingleString(const QString& sql, QString &value) { KexiDB::Cursor *cursor; m_sql = sql + " LIMIT 1"; // is this safe?; if (!(cursor = executeQuery( m_sql ))) { KexiDBDbg << "Connection::querySingleRecord(): !executeQuery()" << endl; return false; } if (!cursor->moveFirst() || cursor->eof()) { KexiDBDbg << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof()" << endl; deleteCursor(cursor); return false; } value = cursor->value(0).toString(); return deleteCursor(cursor); } //! Query database for a single integer bool Connection::querySingleNumber(const QString& sql, int &number) { static QString str; static bool ok; if (!querySingleString(sql, str)) return false; number = str.toInt(&ok); return ok; } bool Connection::resultExists(const QString& sql, bool &success) { KexiDB::Cursor *cursor; m_sql = QString("SELECT 1 FROM (") + sql + ") LIMIT 1"; // is this safe?; if (!(cursor = executeQuery( m_sql ))) { KexiDBDbg << "Connection::querySingleRecord(): !executeQuery()" << endl; success = false; return false; } success = true; if (!cursor->moveFirst() || cursor->eof()) { KexiDBDbg << "Connection::querySingleRecord(): !cursor->moveFirst() || cursor->eof()" << endl; deleteCursor(cursor); return false; } return deleteCursor(cursor); } bool Connection::isEmpty( TableSchema& table, bool &success ) { return !resultExists( selectStatement( *table.query() ), success ); } int Connection::resultCount(const QString& sql) { int count = -1; m_sql = QString("SELECT COUNT() FROM (") + sql + ")"; querySingleNumber(m_sql, count); return count; } KexiDB::TableSchema* Connection::setupTableSchema( const RowData &data ) { TableSchema *t = new TableSchema( this ); if (!setupObjectSchemaData( data, *t )) { delete t; return 0; } /* if (!deleteCursor(table_cur)) { delete t; return 0; }*/ KexiDB::Cursor *cursor; if (!(cursor = executeQuery( QString("select t_id, f_type, f_name, f_length, f_precision, f_constraints, f_options, f_order, f_caption, f_help" " from kexi__fields where t_id=%1 order by f_order").arg(t->m_id) ))) { return 0; } if (!cursor->moveFirst()) { deleteCursor(cursor); return 0; } bool ok; while (!cursor->eof()) { // KexiDBDbg<<"@@@ f_name=="<value(2).asCString()<value(1).toInt(&ok); if (!ok) break; int f_len = cursor->value(3).toInt(&ok); if (!ok) break; int f_prec = cursor->value(4).toInt(&ok); if (!ok) break; int f_constr = cursor->value(5).toInt(&ok); if (!ok) break; int f_opts = cursor->value(6).toInt(&ok); if (!ok) break; if (!Kexi::isIdentifier( cursor->value(2).asString() )) { setError(ERR_INVALID_IDENTIFIER, i18n("Invalid object name \"%1\"") .arg( cursor->value(2).asString() )); ok=false; break; } Field *f = new Field( cursor->value(2).asString(), (Field::Type)f_type, f_constr, f_len, f_prec, f_opts ); f->setDefaultValue( cursor->value(7).toCString() ); f->m_caption = cursor->value(9).asString(); f->m_desc = cursor->value(10).asString(); t->addField(f); cursor->moveNext(); } if (!ok) {//error: deleteCursor(cursor); delete t; return 0; } if (!deleteCursor(cursor)) { delete t; return 0; } //store locally: m_tables.insert(t->m_id, t); m_tables_byname.insert(t->m_name.lower(), t); return t; } TableSchema* Connection::tableSchema( const QString& tableName ) { QString m_tableName = tableName.lower(); TableSchema *t = m_tables_byname[m_tableName]; if (t) return t; //not found: retrieve schema RowData data; if (!querySingleRecord(QString("select o_id, o_type, o_name, o_caption, o_desc from kexi__objects where o_name='%1' and o_type=%2") .arg(m_tableName).arg(KexiDB::TableObjectType), data)) return 0; return setupTableSchema(data); } TableSchema* Connection::tableSchema( int tableId ) { TableSchema *t = m_tables[tableId]; if (t) return t; //not found: retrieve schema RowData data; if (!querySingleRecord(QString("select o_id, o_type, o_name, o_caption, o_desc from kexi__objects where o_id=%1").arg(tableId), data)) return 0; return setupTableSchema(data); } bool Connection::loadDataBlock( int objectID, QString &dataString, const QString& dataID ) { if (objectID<=0) return false; return querySingleString( QString("select o_data from kexi__objectdata where o_id=") + QString::number(objectID) + " and " + KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID), dataString ); } bool Connection::storeDataBlock( int objectID, const QString &dataString, const QString& dataID ) { if (objectID<=0) return false; QString sql = "select kexi__objectdata.o_id from kexi__objectdata where o_id=" + QString::number(objectID); QString sql_sub = KexiDB::sqlWhere(m_driver, KexiDB::Field::Text, "o_sub_id", dataID); bool ok, exists; exists = resultExists(sql + " and " + sql_sub, ok); if (!ok) return false; if (exists) { return executeSQL( "update kexi__objectdata set o_data=" + m_driver->valueToSQL( KexiDB::Field::BLOB, dataString ) + " where o_id=" + QString::number(objectID) + " and " + sql_sub ); } return executeSQL( "insert into kexi__objectdata (o_id, o_data, o_sub_id) values (" + QString::number(objectID) +"," + m_driver->valueToSQL( KexiDB::Field::BLOB, dataString ) + "," + m_driver->valueToSQL( KexiDB::Field::Text, dataID ) + ")" ); } bool Connection::removeDataBlock( int objectID, const QString& dataID) { if (objectID<=0) return false; if (dataID.isEmpty()) return KexiDB::deleteRow(*this, "kexi__objectdata", "o_id", QString::number(objectID)); else return KexiDB::deleteRow(*this, "kexi__objectdata", "o_id", KexiDB::Field::Integer, objectID, "o_sub_id", KexiDB::Field::Text, dataID); } KexiDB::QuerySchema* Connection::setupQuerySchema( const RowData &data ) { bool ok = true; const int objID = data[0].toInt(&ok); if (!ok) return false; QString sqlText; if (!loadDataBlock( objID, sqlText, "sql" )) { return 0; } d->parser()->parse( sqlText ); KexiDB::QuerySchema *query = d->parser()->query(); //error? if (!query) { //todo return 0; } if (!setupObjectSchemaData( data, *query )) { delete query; return 0; } m_queries.insert(query->m_id, query); m_queries_byname.insert(query->m_name.lower(), query); return query; } QuerySchema* Connection::querySchema( const QString& queryName ) { QString m_queryName = queryName.lower(); QuerySchema *q = m_queries_byname[m_queryName]; if (q) return q; //not found: retrieve schema RowData data; if (!querySingleRecord(QString("select o_id, o_type, o_name, o_caption, o_desc from kexi__objects where o_name='%1' and o_type=%2") .arg(m_queryName).arg(KexiDB::QueryObjectType), data)) return 0; return setupQuerySchema(data); } QuerySchema* Connection::querySchema( int queryId ) { QuerySchema *q = m_queries[queryId]; if (q) return q; //not found: retrieve schema RowData data; if (!querySingleRecord(QString("select o_id, o_type, o_name, o_caption, o_desc from kexi__objects where o_id=%1").arg(queryId), data)) return 0; return setupQuerySchema(data); } TableSchema* Connection::newKexiDBSystemTableSchema(const QString& tsname) { TableSchema *ts = new TableSchema(tsname.lower()); ts->setKexiDBSystem(true); m_kexiDBSystemtables.append(ts); m_tables_byname.insert(ts->name(),ts); return ts; } //! Creates kexi__* tables. bool Connection::setupKexiDBSystemSchema() { if (!m_kexiDBSystemtables.isEmpty()) return true; //already set up TableSchema *t_objects = newKexiDBSystemTableSchema("kexi__objects"); t_objects->addField( new Field("o_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) ) .addField( new Field("o_type", Field::Byte, 0, Field::Unsigned) ) .addField( new Field("o_name", Field::Text) ) .addField( new Field("o_caption", Field::Text ) ) .addField( new Field("o_desc", Field::LongText ) ); t_objects->debug(); TableSchema *t_objectdata = newKexiDBSystemTableSchema("kexi__objectdata"); t_objectdata->addField( new Field("o_id", Field::Integer, Field::NotNull, Field::Unsigned) ) .addField( new Field("o_data", Field::BLOB) ) .addField( new Field("o_sub_id", Field::Text) ); TableSchema *t_fields = newKexiDBSystemTableSchema("kexi__fields"); t_fields->addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("f_type", Field::Byte, 0, Field::Unsigned) ) .addField( new Field("f_name", Field::Text ) ) .addField( new Field("f_length", Field::Integer ) ) .addField( new Field("f_precision", Field::Integer ) ) .addField( new Field("f_constraints", Field::Integer ) ) .addField( new Field("f_options", Field::Integer ) ) .addField( new Field("f_default", Field::Text ) ) //these are additional properties: .addField( new Field("f_order", Field::Integer ) ) .addField( new Field("f_caption", Field::Text ) ) .addField( new Field("f_help", Field::LongText ) ); /* TableSchema *t_querydata = newKexiDBSystemTableSchema("kexi__querydata"); t_querydata->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("q_sql", Field::LongText ) ) .addField( new Field("q_valid", Field::Boolean ) ); TableSchema *t_queryfields = newKexiDBSystemTableSchema("kexi__queryfields"); t_queryfields->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("f_order", Field::Integer ) ) .addField( new Field("f_id", Field::Integer ) ) .addField( new Field("f_tab_asterisk", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("f_alltab_asterisk", Field::Boolean) ); TableSchema *t_querytables = newKexiDBSystemTableSchema("kexi__querytables"); t_querytables->addField( new Field("q_id", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("t_id", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("t_order", Field::Integer, 0, Field::Unsigned) );*/ TableSchema *t_db = newKexiDBSystemTableSchema("kexi__db"); t_db->addField( new Field("db_property", Field::Text, Field::NoConstraints, Field::NoOptions, 32 ) ) .addField( new Field("db_value", Field::LongText ) ); TableSchema *t_parts = newKexiDBSystemTableSchema("kexi__parts"); t_parts->addField( new Field("p_id", Field::Integer, Field::PrimaryKey | Field::AutoInc, Field::Unsigned) ) .addField( new Field("p_name", Field::Text) ) .addField( new Field("p_mime", Field::Text ) ) .addField( new Field("p_url", Field::Text ) ); TableSchema *t_final = newKexiDBSystemTableSchema("kexi__final"); t_final->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("property", Field::LongText ) ) .addField( new Field("value", Field::BLOB) ); TableSchema *t_useractions = newKexiDBSystemTableSchema("kexi__useractions"); t_useractions->addField( new Field("p_id", Field::Integer, 0, Field::Unsigned) ) .addField( new Field("scope", Field::Integer ) ) .addField( new Field("name", Field::LongText ) ) .addField( new Field("text", Field::LongText ) ) .addField( new Field("icon", Field::LongText ) ) .addField( new Field("method", Field::Integer ) ) .addField( new Field("arguments", Field::LongText) ); return true; } void Connection::removeMe(TableSchema *ts) { if (ts && !m_destructor_started) { m_tables.take(ts->id()); m_tables.take(ts->id()); m_tables_byname.take(ts->name()); } } QString Connection::anyAvailableDatabaseName() { if (!m_availableDatabaseName.isEmpty()) { return m_availableDatabaseName; } return m_driver->beh->ALWAYS_AVAILABLE_DATABASE_NAME; } void Connection::setAvailableDatabaseName(const QString& dbName) { m_availableDatabaseName = dbName; } bool Connection::updateRow(QuerySchema &query, RowData& data, RowEditBuffer& buf) { // Each SQL identifier needs to be escaped in the generated query. KexiDBDbg << "Connection::updateRow.." << endl; clearError(); //--get PKEY if (buf.dbBuffer().isEmpty()) { KexiDBDbg << " -- NO CHANGES DATA!" << endl; return true; } if (!query.parentTable()) { KexiDBWarn << " -- NO PARENT TABLE!" << endl; return false; } IndexSchema *pkey = query.parentTable()->primaryKey(); if (!pkey || pkey->fields()->isEmpty()) { KexiDBWarn << " -- NO PARENT TABLE's PKEY!" << endl; //js TODO: hmm, perhaps we can try to update without using PKEY? return false; } //update the record: m_sql = "UPDATE " + escapeIdentifier(query.parentTable()->name()) + " SET "; QString sqlset, sqlwhere; sqlset.reserve(1024); sqlwhere.reserve(1024); KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer(); - for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.begin();it!=b.end();++it) { + for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) { if (!sqlset.isEmpty()) sqlset+=","; sqlset += (escapeIdentifier(it.key()->field->name()) + "=" + m_driver->valueToSQL(it.key()->field,it.data())); } QValueVector pkeyFieldsOrder = query.pkeyFieldsOrder(); if (pkey->fieldCount()>0) { uint i=0; for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) { if (!sqlwhere.isEmpty()) sqlwhere+=" AND "; QVariant val = data[ pkeyFieldsOrder[i] ]; if (val.isNull() || !val.isValid()) { setError(ERR_UPDATE_NULL_PKEY_FIELD, i18n("Primary key's field \"%1\" cannot be empty.").arg(it.current()->name())); //js todo: pass the field's name somewhere! return false; } sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" + m_driver->valueToSQL( it.current(), val ) ); } } m_sql += (sqlset + " WHERE " + sqlwhere); KexiDBDbg << " -- SQL == " << m_sql << endl; if (!executeSQL(m_sql)) { setError(ERR_UPDATE_SERVER_ERROR, i18n("Row updating on the server failed.")); return false; } //success: now also assign new value in memory: QMap fieldsOrder = query.fieldsOrder(); - for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.begin();it!=b.end();++it) { + for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) { data[ fieldsOrder[it.key()] ] = it.data(); } return true; } bool Connection::insertRow(QuerySchema &query, RowData& data, RowEditBuffer& buf) { // Each SQL identifier needs to be escaped in the generated query. KexiDBDbg << "Connection::updateRow.." << endl; clearError(); //--get PKEY /*disabled: there may be empty rows (with autoinc) if (buf.dbBuffer().isEmpty()) { KexiDBDbg << " -- NO CHANGES DATA!" << endl; return true; }*/ if (!query.parentTable()) { KexiDBWarn << " -- NO PARENT TABLE!" << endl; return false; } IndexSchema *pkey = query.parentTable()->primaryKey(); if (!pkey || pkey->fields()->isEmpty()) KexiDBWarn << " -- WARNING: NO PARENT TABLE's PKEY" << endl; QString sqlcols, sqlvals; sqlcols.reserve(1024); sqlvals.reserve(1024); //insert the record: m_sql = "INSERT INTO " + escapeIdentifier(query.parentTable()->name()) + " ("; KexiDB::RowEditBuffer::DBMap b = buf.dbBuffer(); if (buf.dbBuffer().isEmpty()) { if (!pkey || pkey->fields()->isEmpty()) { KexiDBWarn << " -- WARNING: PARENT TABLE REQUIRED FOR INSERTING EMPTY ROWS: INSERT CANCELLED" << endl; return false; } //at least one value is needed for VALUES section: find it and set to NULL: Field *anyField = query.parentTable()->anyNonPKField(); if (!anyField) { //try to set NULL in pkey field (could not work for every SQL engine!) anyField = pkey->fields()->first(); } sqlcols += escapeIdentifier(anyField->name()); sqlvals += m_driver->valueToSQL(anyField,QVariant()/*NULL*/); } else { - for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.begin();it!=b.end();++it) { + for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) { if (!sqlcols.isEmpty()) { sqlcols+=","; sqlvals+=","; } sqlcols += escapeIdentifier(it.key()->field->name()); sqlvals += m_driver->valueToSQL(it.key()->field,it.data()); } } m_sql += (sqlcols + ") VALUES (" + sqlvals + ")"); KexiDBDbg << " -- SQL == " << m_sql << endl; bool res = executeSQL(m_sql); if (!res) { setError(ERR_INSERT_SERVER_ERROR, i18n("Row inserting on the server failed.")); return false; } //success: now also assign new value in memory: QMap fieldsOrder = query.fieldsOrder(); - for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.begin();it!=b.end();++it) { + for (KexiDB::RowEditBuffer::DBMap::ConstIterator it=b.constBegin();it!=b.constEnd();++it) { data[ fieldsOrder[it.key()] ] = it.data(); } //fetch autoincremented values QueryColumnInfo::List *aif_list = query.autoIncrementFields(); if (pkey && !aif_list->isEmpty()) { //now only if PKEY is present: //js TODO more... QueryColumnInfo *id_fieldinfo = aif_list->first(); int last_id = lastInsertedAutoIncValue(id_fieldinfo->field->name(), id_fieldinfo->field->table()->name()); if (last_id==-1) { //err... return false; } RowData aif_data; QString getAutoIncForInsertedValue = QString("SELECT ") + query.autoIncrementSQLFieldsList(m_driver) + " FROM " + escapeIdentifier(id_fieldinfo->field->table()->name()) + " WHERE " + escapeIdentifier(id_fieldinfo->field->name()) + "=" + QString::number(last_id); if (!querySingleRecord(getAutoIncForInsertedValue, aif_data)) { //err... return false; } QueryColumnInfo::ListIterator fi_it(*aif_list); QueryColumnInfo *fi; for (uint i=0; (fi = fi_it.current()); ++fi_it, i++) { KexiDBDbg << "Connection::insertRow(): AUTOINCREMENTED FIELD " << fi->field->name() << " == " << aif_data[i].toInt() << endl; data[ fieldsOrder[ fi ] ] = aif_data[i]; } } return true; } bool Connection::deleteRow(QuerySchema &query, RowData& data) { // Each SQL identifier needs to be escaped in the generated query. KexiDBWarn << "Connection::deleteRow.." << endl; clearError(); if (!query.parentTable()) { KexiDBWarn << " -- NO PARENT TABLE!" << endl; return false; } IndexSchema *pkey = query.parentTable()->primaryKey(); if (!pkey || pkey->fields()->isEmpty()) KexiDBWarn << " -- WARNING: NO PARENT TABLE's PKEY" << endl; //update the record: m_sql = "DELETE FROM " + escapeIdentifier(query.parentTable()->name()) + " WHERE "; QString sqlwhere; sqlwhere.reserve(1024); QValueVector pkeyFieldsOrder = query.pkeyFieldsOrder(); if (pkey->fieldCount()>0) { uint i=0; for (Field::ListIterator it = pkey->fieldsIterator(); it.current(); i++, ++it) { if (!sqlwhere.isEmpty()) sqlwhere+=" AND "; QVariant val = data[ pkeyFieldsOrder[i] ]; if (val.isNull() || !val.isValid()) { setError(ERR_DELETE_NULL_PKEY_FIELD, i18n("Primary key's field \"%1\" cannot be empty.").arg(it.current()->name())); //js todo: pass the field's name somewhere! return false; } sqlwhere += ( escapeIdentifier(it.current()->name()) + "=" + m_driver->valueToSQL( it.current(), val ) ); } } m_sql += sqlwhere; KexiDBDbg << " -- SQL == " << m_sql << endl; if (!executeSQL(m_sql)) { setError(ERR_DELETE_SERVER_ERROR, i18n("Row deleting on the server failed.")); return false; } return true; } bool Connection::deleteAllRows(QuerySchema &query) { clearError(); if (!query.parentTable()) { KexiDBWarn << " -- NO PARENT TABLE!" << endl; return false; } IndexSchema *pkey = query.parentTable()->primaryKey(); if (!pkey || pkey->fields()->isEmpty()) KexiDBWarn << "Connection::deleteAllRows -- WARNING: NO PARENT TABLE's PKEY" << endl; m_sql = "DELETE FROM " + escapeIdentifier(query.parentTable()->name()); KexiDBDbg << " -- SQL == " << m_sql << endl; if (!executeSQL(m_sql)) { setError(ERR_DELETE_SERVER_ERROR, i18n("Rows deleting on the server failed.")); return false; } return true; } #include "connection.moc" diff --git a/kexi/kexidb/drivermanager.cpp b/kexi/kexidb/drivermanager.cpp index 3d84158d81..6b8bc57dfb 100644 --- a/kexi/kexidb/drivermanager.cpp +++ b/kexi/kexidb/drivermanager.cpp @@ -1,374 +1,374 @@ /* This file is part of the KDE project Daniel Molkentin Joseph Wenninger Copyright (C) 2003-2004 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include //remove debug #undef KexiDBDbg #define KexiDBDbg if (0) kdDebug() using namespace KexiDB; DriverManagerInternal* DriverManagerInternal::s_self = 0L; DriverManagerInternal::DriverManagerInternal() /* protected */ : QObject( 0, "KexiDB::DriverManager" ) , Object() , m_drivers(17, false) , m_refCount(0) , lookupDriversNeeded(true) { m_drivers.setAutoDelete(true); m_serverResultNum=0; } DriverManagerInternal::~DriverManagerInternal() { KexiDBDbg << "DriverManagerInternal::~DriverManagerInternal()" << endl; m_drivers.clear(); if ( s_self == this ) s_self = 0; KexiDBDbg << "DriverManagerInternal::~DriverManagerInternal() ok" << endl; } void DriverManagerInternal::slotAppQuits() { if (qApp->mainWidget() && qApp->mainWidget()->isVisible()) return; //what a hack! - we give up when app is still there KexiDBDbg << "DriverManagerInternal::slotAppQuits(): let's clear drivers..." << endl; m_drivers.clear(); } DriverManagerInternal *DriverManagerInternal::self() { if (!s_self) s_self = new DriverManagerInternal(); return s_self; } bool DriverManagerInternal::lookupDrivers() { if (!lookupDriversNeeded) return true; if (qApp) { connect(qApp,SIGNAL(aboutToQuit()),this,SLOT(slotAppQuits())); } //TODO: for QT-only version check for KInstance wrapper // KexiDBWarn << "DriverManagerInternal::lookupDrivers(): cannot work without KInstance (KGlobal::instance()==0)!" << endl; // setError("Driver Manager cannot work without KInstance (KGlobal::instance()==0)!"); lookupDriversNeeded = false; clearError(); KTrader::OfferList tlist = KTrader::self()->query("Kexi/DBDriver"); - KTrader::OfferList::ConstIterator it(tlist.begin()); - for(; it != tlist.end(); ++it) + KTrader::OfferList::ConstIterator it(tlist.constBegin()); + for(; it != tlist.constEnd(); ++it) { KService::Ptr ptr = (*it); QString srv_name = ptr->property("X-Kexi-DriverName").toString(); if (srv_name.isEmpty()) { KexiDBWarn << "DriverManagerInternal::lookupDrivers(): X-Kexi-DriverName must be set for KexiDB driver \"" << ptr->property("Name").toString() << "\" service!\n -- skipped!" << endl; delete ptr; continue; } if (m_services_lcase.contains(srv_name.lower())) { delete ptr; continue; } QString mime = ptr->property("X-Kexi-FileDBDriverMime").toString().lower(); QString drvType = ptr->property("X-Kexi-DriverType").toString().lower(); if (drvType=="file") { if (!mime.isEmpty()) { if (!m_services_by_mimetype.contains(mime)) { m_services_by_mimetype.insert(mime, ptr); } else { KexiDBWarn << "DriverManagerInternal::lookupDrivers(): more than one driver for '" << mime << "' mime type!" << endl; } } } else { #ifndef KEXI_SERVER_SUPPORT //no support for this driver continue; #endif } m_services.insert(srv_name, ptr); m_services_lcase.insert(srv_name.lower(), ptr); KexiDBDbg << "KexiDB::DriverManager::lookupDrivers(): registered driver: " << ptr->name() << "(" << ptr->library() << ")" << endl; } if (tlist.isEmpty()) { setError(ERR_DRIVERMANAGER, i18n("Could not find any database drivers.") ); return false; } return true; } KexiDB::Driver::Info DriverManagerInternal::driverInfo(const QString &name) { KexiDB::Driver::Info i = m_driversInfo[name.lower()]; if (!error() && i.name.isEmpty()) setError(ERR_DRIVERMANAGER, i18n("Could not find database driver \"%1\".").arg(name) ); return i; } Driver* DriverManagerInternal::driver(const QString& name) { if (!lookupDrivers()) return 0; clearError(); KexiDBDbg << "DriverManager::driver(): loading " << name << endl; Driver *drv = name.isEmpty() ? 0 : m_drivers.find(name.latin1()); if (drv) return drv; //cached if (!m_services_lcase.contains(name.lower())) { setError(ERR_DRIVERMANAGER, i18n("Could not find database driver \"%1\".").arg(name) ); return 0; } KService::Ptr ptr= *(m_services_lcase.find(name.lower())); QString srv_name = ptr->property("X-Kexi-DriverName").toString(); KexiDBDbg << "KexiDBInterfaceManager::load(): library: "<library()<(ptr, this, srv_name.latin1(), QStringList(),&m_serverResultNum); if (!drv) { setError(ERR_DRIVERMANAGER, i18n("Could not load database driver \"%1\".") .arg(name) ); if (m_componentLoadingErrors.isEmpty()) {//fill errtable on demand m_componentLoadingErrors[KParts::ComponentFactory::ErrNoServiceFound]="ErrNoServiceFound"; m_componentLoadingErrors[KParts::ComponentFactory::ErrServiceProvidesNoLibrary]="ErrServiceProvidesNoLibrary"; m_componentLoadingErrors[KParts::ComponentFactory::ErrNoLibrary]="ErrNoLibrary"; m_componentLoadingErrors[KParts::ComponentFactory::ErrNoFactory]="ErrNoFactory"; m_componentLoadingErrors[KParts::ComponentFactory::ErrNoComponent]="ErrNoComponent"; } m_serverResultName=m_componentLoadingErrors[m_serverResultNum]; return 0; } KexiDBDbg << "KexiDBInterfaceManager::load(): loading succeed: " << name <d->initInternalProperties(); m_drivers.insert(name.latin1(), drv); //cache it return drv; } void DriverManagerInternal::incRefCount() { m_refCount++; KexiDBDbg << "DriverManagerInternal::incRefCount(): " << m_refCount << endl; } void DriverManagerInternal::decRefCount() { m_refCount--; KexiDBDbg << "DriverManagerInternal::decRefCount(): " << m_refCount << endl; // if (m_refCount<1) { // KexiDBDbg<<"KexiDB::DriverManagerInternal::decRefCount(): reached m_refCount<1 -->deletelater()"<name()); } // --------------------------- // --- DriverManager impl. --- // --------------------------- DriverManager::DriverManager() : QObject( 0, "KexiDB::DriverManager" ) , Object() , d_int( DriverManagerInternal::self() ) { d_int->incRefCount(); // if ( !s_self ) // s_self = this; // lookupDrivers(); } DriverManager::~DriverManager() { KexiDBDbg << "DriverManager::~DriverManager()" << endl; /* Connection *conn; for ( conn = m_connections.first(); conn ; conn = m_connections.next() ) { conn->disconnect(); conn->m_driver = 0; //don't let the connection touch our driver now m_connections.remove(); delete conn; }*/ d_int->decRefCount(); if (d_int->m_refCount==0) { //delete internal drv manager! delete d_int; } // if ( s_self == this ) //s_self = 0; KexiDBDbg << "DriverManager::~DriverManager() ok" << endl; } const KexiDB::Driver::InfoMap DriverManager::driversInfo() { if (!d_int->lookupDrivers()) return KexiDB::Driver::InfoMap(); if (!d_int->m_driversInfo.isEmpty()) return d_int->m_driversInfo; ServicesMap::ConstIterator it; for ( it=d_int->m_services.constBegin() ; it != d_int->m_services.constEnd(); ++it ) { Driver::Info info; KService::Ptr ptr = it.data(); info.name = ptr->property("X-Kexi-DriverName").toString(); info.caption = ptr->property("Name").toString(); info.comment = ptr->property("Comment").toString(); if (info.caption.isEmpty()) info.caption = info.name; info.fileBased = (ptr->property("X-Kexi-DriverType").toString().lower()=="file"); if (info.fileBased) info.fileDBMimeType = ptr->property("X-Kexi-FileDBDriverMime").toString().lower(); d_int->m_driversInfo.insert(info.name.lower(), info); } return d_int->m_driversInfo; } const QStringList DriverManager::driverNames() { if (!d_int->lookupDrivers()) return QStringList(); if (d_int->m_services.isEmpty() && d_int->error()) return QStringList(); return d_int->m_services.keys(); } KexiDB::Driver::Info DriverManager::driverInfo(const QString &name) { driversInfo(); KexiDB::Driver::Info i = d_int->driverInfo(name); if (d_int->error()) setError(d_int); return i; } KService::Ptr DriverManager::serviceInfo(const QString &name) { if (!d_int->lookupDrivers()) { setError(d_int); return 0; } clearError(); if (d_int->m_services_lcase.contains(name.lower())) { return *d_int->m_services_lcase.find(name.lower()); } else { setError(ERR_DRIVERMANAGER, i18n("No such driver service: \"%1\".").arg(name) ); return 0; } } const DriverManager::ServicesMap& DriverManager::services() { d_int->lookupDrivers(); return d_int->m_services; } QString DriverManager::lookupByMime(const QString &mimeType) { if (!d_int->lookupDrivers()) { setError(d_int); return 0; } KService::Ptr ptr = d_int->m_services_by_mimetype[mimeType.lower()]; if (!ptr) return QString::null; return ptr->property("X-Kexi-DriverName").toString(); } Driver* DriverManager::driver(const QString& name) { Driver *drv = d_int->driver(name); if (d_int->error()) setError(d_int); return drv; } QString DriverManager::serverErrorMsg() { return d_int->m_serverErrMsg; } int DriverManager::serverResult() { return d_int->m_serverResultNum; } QString DriverManager::serverResultName() { return d_int->m_serverResultName; } void DriverManager::drv_clearServerResult() { d_int->m_serverErrMsg=QString::null; d_int->m_serverResultNum=0; d_int->m_serverResultName=QString::null; } #include "drivermanager_p.moc" diff --git a/kexi/kexidb/drivers/mySQL/mysqlconnection.cpp b/kexi/kexidb/drivers/mySQL/mysqlconnection.cpp index 4e0899d870..3846b71cc8 100644 --- a/kexi/kexidb/drivers/mySQL/mysqlconnection.cpp +++ b/kexi/kexidb/drivers/mySQL/mysqlconnection.cpp @@ -1,968 +1,968 @@ /* This file is part of the KDE project Copyright (C) 2002 Lucijan Busch Daniel Molkentin Copyright (C) 2003 Joseph Wenninger Copyright (C) 2004 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef Q_WS_WIN # include #endif #include #define BOOL bool #include #include #include #include #include #include "mysqldriver.h" #include "mysqlconnection.h" #include "mysqlconnection_p.h" #include "mysqlcursor.h" #include using namespace KexiDB; MySqlConnectionInternal::MySqlConnectionInternal() : mysql(0) , res(0) , temp_st(0x10000) // { } MySqlConnectionInternal::~MySqlConnectionInternal() { if (mysql) { mysql_close(mysql); mysql = 0; } } void MySqlConnectionInternal::storeResult() { res = mysql_errno(mysql); errmsg = mysql_error(mysql); } //-------------------------------------------------------------------------- MySqlConnection::MySqlConnection( Driver *driver, ConnectionData &conn_data ) :Connection(driver,conn_data) ,d(new MySqlConnectionInternal()) { } MySqlConnection::~MySqlConnection() { destroy(); } bool MySqlConnection::drv_connect() { if (!(d->mysql = mysql_init(d->mysql))) return false; KexiDBDrvDbg << "MySqlConnection::connect()" << endl; QString socket; if (m_data->hostName.isEmpty() || (m_data->hostName=="localhost")) { if (m_data->localSocketFileName.isEmpty()) { QStringList sockets; #ifndef Q_WS_WIN sockets.append("/var/lib/mysql/mysql.sock"); sockets.append("/var/run/mysqld/mysqld.sock"); sockets.append("/tmp/mysql.sock"); for(QStringList::ConstIterator it = sockets.constBegin(); it != sockets.constEnd(); it++) { if(QFile(*it).exists()) { socket = (*it); break; } } #endif } else socket=m_data->localSocketFileName; } mysql_real_connect(d->mysql, m_data->hostName.local8Bit(), m_data->userName.local8Bit(), m_data->password.local8Bit(), 0, m_data->port, socket.local8Bit(), 0); if(mysql_errno(d->mysql) == 0) return true; d->storeResult(); //store error msg, if any - can be destroyed after disconenct() drv_disconnect(); // setError(ERR_DB_SPECIFIC,err); return false; } bool MySqlConnection::drv_disconnect() { mysql_close(d->mysql); d->mysql = 0; KexiDBDrvDbg << "MySqlConnection::disconnect()" << endl; return true; } Cursor* MySqlConnection::prepareQuery(const QString& statement, uint cursor_options) { return new MySqlCursor(this,statement,cursor_options); } Cursor* MySqlConnection::prepareQuery( QuerySchema& query, uint cursor_options ) { return new MySqlCursor( this, query, cursor_options ); } QString MySqlConnection::escapeString(const QString& str) const { return QString();//TODO } QCString MySqlConnection::escapeString(const QCString& str) const { return QCString();//TODO } bool MySqlConnection::drv_getDatabasesList( QStringList &list ) { KexiDBDrvDbg << "MySqlConnection::drv_getDatabasesList()" << endl; list.clear(); MYSQL_RES *res; if((res=mysql_list_dbs(d->mysql,0)) != 0) { MYSQL_ROW row; while ( (row = mysql_fetch_row(res))!=0) { list<storeResult(); // setError(ERR_DB_SPECIFIC,mysql_error(d->mysql)); return false; } bool MySqlConnection::drv_createDatabase( const QString &dbName) { KexiDBDrvDbg << "MySqlConnection::drv_createDatabase: " << dbName << endl; // mysql_create_db deprecated, use SQL here. if (drv_executeSQL("CREATE DATABASE " + (dbName))) return true; d->storeResult(); return false; } bool MySqlConnection::drv_useDatabase( const QString &dbName) { //TODO is here escaping needed? return drv_executeSQL("use "+dbName); } bool MySqlConnection::drv_closeDatabase() { //TODO free resources //As far as I know, mysql doesn't support that return true; } bool MySqlConnection::drv_dropDatabase( const QString &dbName) { //TODO is here escaping needed return drv_executeSQL("drop database "+dbName); } bool MySqlConnection::drv_executeSQL( const QString& statement ) { //let kexidb do it! : /* if (statement.isEmpty()) { //add errormsg ? return false; }*/ KexiDBDrvDbg << "MySqlConnection::drv_executeSQL: " << statement << endl; QCString queryStr=statement.utf8(); const char *query=queryStr; if(mysql_real_query(d->mysql, query, strlen(query)) == 0) { return true; } d->storeResult(); // setError(ERR_DB_SPECIFIC,mysql_error(m_mysql)); return false; } Q_ULLONG MySqlConnection::drv_lastInsertRowID() { #ifndef Q_WS_WIN #warning TODO #endif return (Q_ULLONG)mysql_insert_id(d->mysql); } int MySqlConnection::serverResult() { return d->res; } QString MySqlConnection::serverResultName() { return QString::null; } void MySqlConnection::drv_clearServerResult() { if (!d) return; d->res = 0; } QString MySqlConnection::serverErrorMsg() { return d->errmsg; } #if 0 //old code MySqlDB::MySqlDB(QObject *parent, const char *name, const QStringList &) : KexiDB(parent, name) { KexiDBDrvDbg << "MySqlDB::MySqlDB()" << endl; m_driverName = "mySQL" m_isFileDriver=false; m_features=CursorForward; m_typeNames.resize(Field::LastType + 1); m_typeNames.resize(Field::LastType + 1); m_typeNames[Field::Byte]="UNSIGNED TINYINT"; m_typeNames[Field::ShortInteger]="SMALLINT"; m_typeNames[Field::Integer]="INT"; m_typeNames[Field::BigInteger]="BIGINT"; m_typeNames[Field::Boolean]="BOOLEAN"; m_typeNames[Field::Date]="DATE"; m_typeNames[Field::DateTime]="DATETIME"; m_typeNames[Field::Time]="TIME"; m_typeNames[Field::Float]="FLOAT"; m_typeNames[Field::Double]="DOUBLE"; m_typeNames[Field::Text]="TEXT"; m_typeNames[Field::LongText]="LONGTEXT"; m_typeNames[Field::BLOB]="BLOB"; m_mysql = 0; m_mysql = mysql_init(m_mysql); m_connected = false; m_connectedDB = false; } KexiDBRecordSet* MySqlDB::queryRecord(const QString& querystatement, bool buffer) { m_error.setup(0); KexiDBDrvDbg << "MySqlDB::queryRecord()" << endl; if (query(querystatement)) { MYSQL_RES *res; if(!buffer) res = mysql_use_result(m_mysql); else res = mysql_store_result(m_mysql); if(res) { MySqlRecord *rec = new MySqlRecord(res, this, "record", false); return rec; } } else { KexiDBDrvDbg << "MySqlDB::queryRecord(): error..." << endl; KexiDBDrvDbg << "MySqlDB::queryRecord(): cause:"<(this)->query("show databases"); MySqlResult *result = const_cast(this)->storeResult(); if(!result) return s; KexiDBDrvDbg << "field name: " << result->fieldInfo(0)->name() << endl; while(result->next()) { s.append(result->value(0).toString()); } delete result; return s; } bool MySqlDB::isSystemDatabase(QString &dbName) { return dbName=="mysql"; } QStringList MySqlDB::tableNames() { if(!m_connectedDB) return QStringList(); QStringList s; query("show tables"); MySqlResult *result = storeResult(); if(!result) return s; while(result->next()) { s.append(result->value(0).toString()); // KexiDBDrvDbg << "* tableNames():" << result->value(0).toString() << endl; } delete result; m_tableDefs.clear(); - for(QStringList::const_iterator it=s.begin();it!=s.end();++it) { + for(QStringList::ConstIterator it=s.constBegin();it!=s.constEnd();++it) { m_tableDefs.insert((*it),createTableDef(*it)); } return s; } const KexiDBTable * const MySqlDB::table(const QString& name) { return m_tableDefs[name]; } KexiDBTable * MySqlDB::createTableDef(const QString& name) { KexiDBDrvDbg<<"MySQLDB::createTableDef: entered"<fieldInfo(i++)) != 0) { t->addField(*f); //should we support other unique keys here too ? if (f->primary_key()) t->addPrimaryKey(f->name()); KexiDBDrvDbg<<"MySQLDB::createTableDef: addField:"<name()< 0) { char* escaped = (char*) malloc(str.size() * 2 + 2); mysql_real_escape_string(m_mysql, escaped, str.data(), str.size()); rval = escaped; free(escaped); } else { rval = ""; } return rval; } unsigned long MySqlDB::lastAuto() { return (unsigned long)mysql_insert_id(m_mysql); } KexiDBTableStruct MySqlDB::structure(const QString& table) const { KexiDBTableStruct dbStruct; MYSQL_RES* result= mysql_list_fields(m_mysql, table.local8Bit().data(), 0); KexiDBDrvDbg << "MySqlDB::structure: Get fields..." << endl; if(result) { MYSQL_FIELD* field; while((field = mysql_fetch_field(result))) { KexiDBField* f = new KexiDBField(field->table); f->setName(field->name); f->setColumnType(getInternalDataType(field->type)); f->setLength(field->length); f->setPrecision(field->decimals); f->setUnsigned(field->flags & UNSIGNED_FLAG); f->setBinary(field->flags & BINARY_FLAG); f->setDefaultValue(field->def); f->setAutoIncrement(field->flags & AUTO_INCREMENT_FLAG); f->setPrimaryKey(field->flags & PRI_KEY_FLAG); f->setUniqueKey(field->flags & UNIQUE_KEY_FLAG); f->setNotNull(field->flags & NOT_NULL_FLAG); dbStruct.append(f); } mysql_free_result(result); } return dbStruct; } QString MySqlDB::nativeDataType(const KexiDBField::ColumnType& t) const { switch(t) { case KexiDBField::SQLLongVarchar: return "TEXT"; case KexiDBField::SQLVarchar: return "VARCHAR"; case KexiDBField::SQLInteger: return "INTEGER"; case KexiDBField::SQLSmallInt: return "SMALLINT"; case KexiDBField::SQLTinyInt: return "TINYINT"; case KexiDBField::SQLNumeric: return "NUMERIC"; case KexiDBField::SQLDouble: return "DOUBLE"; case KexiDBField::SQLBigInt: return "BIGINT"; case KexiDBField::SQLDecimal: return "DECIMAL"; case KexiDBField::SQLFloat: return "FLOAT"; case KexiDBField::SQLBinary: return "BLOB"; case KexiDBField::SQLLongVarBinary: return "LONGBLOB"; case KexiDBField::SQLVarBinary: return "BLOB"; case KexiDBField::SQLDate: return "DATE"; case KexiDBField::SQLTime: return "TIME"; case KexiDBField::SQLTimeStamp: return "TIMESTAMP"; case KexiDBField::SQLBoolean: return "BOOL"; case KexiDBField::SQLInterval: return "ENUM"; case KexiDBField::SQLInvalid: case KexiDBField::SQLLastType: return QString::null; } return QString::null; } KexiDBField::ColumnType MySqlDB::getInternalDataType(int t) { switch(t) { case FIELD_TYPE_NULL: return KexiDBField::SQLInvalid; case FIELD_TYPE_INT24: case FIELD_TYPE_LONGLONG: return KexiDBField::SQLBigInt; case FIELD_TYPE_NEWDATE: case FIELD_TYPE_DATE: return KexiDBField::SQLDate; case FIELD_TYPE_DECIMAL: return KexiDBField::SQLDecimal; case FIELD_TYPE_DOUBLE: return KexiDBField::SQLDouble; case FIELD_TYPE_FLOAT: return KexiDBField::SQLFloat; case FIELD_TYPE_LONG: case FIELD_TYPE_YEAR: return KexiDBField::SQLInteger; case FIELD_TYPE_SHORT: return KexiDBField::SQLSmallInt; case FIELD_TYPE_TIME: return KexiDBField::SQLTime; case FIELD_TYPE_DATETIME: case FIELD_TYPE_TIMESTAMP: return KexiDBField::SQLTimeStamp; case FIELD_TYPE_TINY: return KexiDBField::SQLTinyInt; case FIELD_TYPE_TINY_BLOB: case FIELD_TYPE_MEDIUM_BLOB: case FIELD_TYPE_LONG_BLOB: case FIELD_TYPE_BLOB: return KexiDBField::SQLVarBinary; case FIELD_TYPE_VAR_STRING: case FIELD_TYPE_STRING: case FIELD_TYPE_SET: case FIELD_TYPE_ENUM: return KexiDBField::SQLVarchar; } return KexiDBField::SQLInvalid; } bool MySqlDB::alterField(const KexiDBField& changedField, unsigned int index, KexiDBTableStruct fields) { KexiDBDrvDbg << "MySqlDB::alterField: Table: " << changedField.table() << " Field: " << fields.at(index)->name() << endl; KexiDBDrvDbg << "MySqlDB::alterField: DataType: " << nativeDataType( changedField.sqlType()) << "ColumnType: " << changedField.sqlType() << endl; QString qstr = "ALTER TABLE `" + changedField.table() + "` CHANGE `" + fields.at(index)->name() + "` `" + changedField.name(); qstr += "` " + createDefinition(changedField, index, fields); KexiDBDrvDbg << "MySqlDB::alterField: Query: " << qstr << endl; bool ok = uhQuery(qstr); if(ok) { ok = changeKeys(changedField, index, fields); } return ok; } bool MySqlDB::createField(const KexiDBField& newField, KexiDBTableStruct fields, bool createTable) { KexiDBDrvDbg << "MySqlDB::createField: Table: " << newField.table() << " Field: " << newField.name() << endl; KexiDBDrvDbg << "MySqlDB::createField: DataType: " << nativeDataType( newField.sqlType()) << "ColumnType: " << newField.sqlType() << endl; QString qstr; if(!createTable) { qstr = "ALTER TABLE `" + newField.table() + "` ADD `" + newField.name(); qstr += "` " + createDefinition(newField, -1, fields); } else { qstr = "CREATE TABLE `" + newField.table() + "` (`" + newField.name(); qstr += "` " + createDefinition(newField, -1, fields); qstr += ")"; } KexiDBDrvDbg << "MySqlDB::createField: Query: " << qstr << endl; bool ok = uhQuery(qstr); if(ok) { ok = changeKeys(newField, -1, fields); } // This is a fresh created table: add its def to our set of tabledefs: //TODO: tableNames() do this on as a side effect -THIS IS BAD IMPL.- fix tableNames() if (ok && createTable) { m_tableDefs.insert(newField.table(),createTableDef(newField.table())); } // return ok; } QString MySqlDB::createDefinition(const KexiDBField& field, int index, KexiDBTableStruct fields) { QString qstr = nativeDataType(field.sqlType()); bool allowUnsigned = false; switch(field.sqlType()) { case KexiDBField::SQLInteger: case KexiDBField::SQLSmallInt: case KexiDBField::SQLTinyInt: case KexiDBField::SQLBigInt: allowUnsigned = true; case KexiDBField::SQLVarchar: qstr += "(" + QString::number((field.length()==0)?255:field.length()) + ")"; break; case KexiDBField::SQLDecimal: case KexiDBField::SQLFloat: case KexiDBField::SQLDouble: case KexiDBField::SQLNumeric: allowUnsigned = true; qstr += "(" + QString::number(field.length()) + "," + QString::number(field.precision()) + ")"; break; case KexiDBField::SQLInvalid: case KexiDBField::SQLBinary: case KexiDBField::SQLBoolean: case KexiDBField::SQLDate: case KexiDBField::SQLLongVarBinary: case KexiDBField::SQLTime: case KexiDBField::SQLTimeStamp: case KexiDBField::SQLVarBinary: case KexiDBField::SQLInterval: case KexiDBField::SQLLongVarchar: case KexiDBField::SQLLastType: break; } if((field.constraints() & KexiDBField::CCNotNull) || field.primary_key()) { qstr += " NOT NULL"; } else { qstr += " NULL"; } if(field.binary() && (field.sqlType() == KexiDBField::SQLVarchar)) { qstr += " BINARY"; } if(field.unsignedType() && allowUnsigned) { qstr += " UNSIGNED"; } if(!field.defaultValue().toString().isEmpty()) { qstr += " DEFAULT " + field.defaultValue().toString(); } if(field.constraints() & KexiDBField::CCAutoInc) { qstr += " AUTO_INCREMENT PRIMARY KEY"; } return qstr; } bool MySqlDB::changeKeys(const KexiDBField& field, int index, KexiDBTableStruct fields) { bool noPrimary = false; QString qstr = "ALTER TABLE `" + field.table() + "`"; if(index >= 0) { if(field.primary_key() == fields.at(index)->primary_key()) { noPrimary = true; } } if(!noPrimary) { qstr += " DROP PRIMARY KEY"; QString fstr; int i = 0, j = 0; for(KexiDBField* f = fields.first(); f; f = fields.next()) { if((index != i) && (f->primary_key())) { if(j > 0) { fstr += ","; } else { j++; } fstr += "`" + f->name() + "`"; } i++; } if(field.primary_key()) { if(j > 0) { fstr += ","; } fstr += "`" + field.name() + "`"; } if(!fstr.isEmpty()) { qstr += ", ADD PRIMARY KEY(" + fstr + ")"; } } if(!noPrimary) { KexiDBDrvDbg << "MySqlDB::changeKeys: Query: " << qstr << endl; return uhQuery(qstr); } return true; } KexiDBError *MySqlDB ::latestError() { return &m_error; } MySqlDB::~MySqlDB() { if(m_connected) { mysql_close(m_mysql); } m_mysql = 0; } QString MySqlDB::escapeName(const QString &tn) { QString en; en = "`" + tn + "`"; return en; } #endif #include "mysqlconnection.moc" diff --git a/kexi/kexidb/expression.cpp b/kexi/kexidb/expression.cpp index b5507990ad..2260207c4c 100644 --- a/kexi/kexidb/expression.cpp +++ b/kexi/kexidb/expression.cpp @@ -1,899 +1,899 @@ /* This file is part of the KDE project Copyright (C) 2003-2004 Jaroslaw Staniek Based on nexp.cpp : Parser module of Python-like language (C) 2001 Jaroslaw Staniek, MIMUW (www.mimuw.edu.pl) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "expression.h" #include "parser/sqlparser.h" #include "parser/parser_p.h" #include #include #include KEXI_DB_EXPORT QString KexiDB::exprClassName(int c) { if (c==KexiDBExpr_Unary) return "Unary"; else if (c==KexiDBExpr_Arithm) return "Arithm"; else if (c==KexiDBExpr_Logical) return "Logical"; else if (c==KexiDBExpr_Relational) return "Relational"; else if (c==KexiDBExpr_SpecialBinary) return "SpecialBinary"; else if (c==KexiDBExpr_Const) return "Const"; else if (c==KexiDBExpr_Variable) return "Variable"; else if (c==KexiDBExpr_Function) return "Function"; else if (c==KexiDBExpr_Aggregation) return "Aggregation"; else if (c==KexiDBExpr_TableList) return "TableList"; return "Unknown"; } using namespace KexiDB; //========================================= BaseExpr::BaseExpr(int token) : m_cl(KexiDBExpr_Unknown) , m_par(0) , m_token(token) { } BaseExpr::~BaseExpr() { } Field::Type BaseExpr::type() { return Field::InvalidType; } QString BaseExpr::debugString() { return QString("BaseExpr(%1,type=%1)").arg(m_token).arg(Driver::defaultSQLTypeName(type())); } bool BaseExpr::validate(ParseInfo& /*parseInfo*/) { return true; } extern const char * const tname(int offset); #define safe_tname(token) ((token>=254 && token<=__LAST_TOKEN) ? tname(token-254) : "") QString BaseExpr::tokenToString() { if (m_token < 254) { if (isprint(m_token)) return QString(QChar(uchar(m_token))); else return QString::number(m_token); } return QString(safe_tname(m_token)); } //========================================= NArgExpr::NArgExpr(int aClass, int token) : BaseExpr(token) { m_cl = aClass; list.setAutoDelete(TRUE); } NArgExpr::~NArgExpr() { } QString NArgExpr::debugString() { QString s = QString("NArgExpr(") + "class=" + exprClassName(m_cl); for ( BaseExpr::ListIterator it(list); it.current(); ++it ) { s+=", "; s+=it.current()->debugString(); } s+=")"; return s; } QString NArgExpr::toString() { QString s; s.reserve(256); for ( BaseExpr::ListIterator it(list); it.current(); ++it ) { if (!s.isEmpty()) s+=", "; s+=it.current()->toString(); } return s; } BaseExpr* NArgExpr::arg(int nr) { return list.at(nr); } void NArgExpr::add(BaseExpr *expr) { list.append(expr); } void NArgExpr::prepend(BaseExpr *expr) { list.prepend(expr); } int NArgExpr::args() { return list.count(); } bool NArgExpr::validate(ParseInfo& parseInfo) { if (!BaseExpr::validate(parseInfo)) return false; for (BaseExpr::ListIterator it(list); it.current(); ++it) { if (!it.current()->validate(parseInfo)) return false; } return true; } //========================================= UnaryExpr::UnaryExpr(int typ, BaseExpr *n) : NArgExpr(KexiDBExpr_Unary, typ) { list.append(n); //ustaw ojca n->setParent(this); } UnaryExpr::~UnaryExpr() { } QString UnaryExpr::debugString() { return "UnaryExpr('" + tokenToString() + "', " + (arg() ? arg()->debugString() : QString("")) + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); } QString UnaryExpr::toString() { if (m_token=='(') //parentheses (special case) return "(" + arg()->toString() + ")"; if (m_token < 255 && isprint(m_token)) return tokenToString() + arg()->toString(); if (m_token==NOT) return "NOT " + arg()->toString(); if (m_token==SQL_IS_NULL) return arg()->toString() + " IS NULL"; if (m_token==SQL_IS_NOT_NULL) return arg()->toString() + " IS NOT NULL"; return QString("{INVALID_OPERATOR#%1} ").arg(m_token) + arg()->toString(); } Field::Type UnaryExpr::type() { //NULL IS NOT NULL : BOOLEAN //NULL IS NULL : BOOLEAN switch (m_token) { case SQL_IS_NULL: case SQL_IS_NOT_NULL: return Field::Boolean; } const Field::Type t = arg()->type(); if (t==Field::Null) return Field::Null; if (m_token==NOT) return Field::Boolean; return t; } bool UnaryExpr::validate(ParseInfo& parseInfo) { if (!NArgExpr::validate(parseInfo)) return false; return true; #if 0 BaseExpr *n = l.at(0); n->check(); /*typ wyniku: const bool dla "NOT " (negacja) int dla "# " (dlugosc stringu) int dla "+/- " */ if (is(NOT) && n->nodeTypeIs(TYP_BOOL)) { node_type=new NConstType(TYP_BOOL); } else if (is('#') && n->nodeTypeIs(TYP_STR)) { node_type=new NConstType(TYP_INT); } else if ((is('+') || is('-')) && n->nodeTypeIs(TYP_INT)) { node_type=new NConstType(TYP_INT); } else { ERR("Niepoprawny argument typu '%s' dla operatora '%s'", n->nodeTypeName(),is(NOT)?QString("not"):QChar(typ())); } #endif } //========================================= BinaryExpr::BinaryExpr(int aClass, BaseExpr *l_n, int typ, BaseExpr *r_n) : NArgExpr(aClass, typ) { list.append(l_n); list.append(r_n); //ustaw ojca l_n->setParent(this); r_n->setParent(this); } BinaryExpr::~BinaryExpr() { } BaseExpr *BinaryExpr::left() { return arg(0); } BaseExpr *BinaryExpr::right() { return arg(1); } bool BinaryExpr::validate(ParseInfo& parseInfo) { if (!NArgExpr::validate(parseInfo)) return false; return true; } Field::Type BinaryExpr::type() { const Field::Type lt = left()->type(), rt = right()->type(); if (lt==Field::Null || rt == Field::Null) { if (m_token!=OR) //note that NULL OR something != NULL return Field::Null; } switch (m_token) { case AND: case OR: case XOR: case SIMILAR_TO: return Field::Boolean; } if (Field::isFPNumericType(lt) && Field::isIntegerType(rt)) return lt; //case BITWISE_SHIFT_LEFT: //case BITWISE_SHIFT_RIGHT: //TODO return left()->type(); } QString BinaryExpr::debugString() { return QString("BinaryExpr(") + "class=" + exprClassName(m_cl) + "," + (left() ? left()->debugString() : QString("")) + ",'" + tokenToString() + "'," + (right() ? right()->debugString() : QString("")) + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); } QString BinaryExpr::toString() { #define INFIX(a) \ left()->toString() + " " + a + " " + right()->toString() if (m_token < 255 && isprint(m_token)) return INFIX(tokenToString()); // other arithmetic operations: << >> if (m_token==BITWISE_SHIFT_RIGHT) return INFIX(">>"); if (m_token==BITWISE_SHIFT_LEFT) return INFIX("<<"); // other relational operations: <= >= <> (or !=) LIKE IN if (m_token==NOT_EQUAL) return INFIX("<>"); if (m_token==NOT_EQUAL2) return INFIX("!="); if (m_token==LESS_OR_EQUAL) return INFIX("<="); if (m_token==GREATER_OR_EQUAL) return INFIX(">="); if (m_token==LIKE) return INFIX("LIKE"); if (m_token==SQL_IN) return INFIX("IN"); // other logical operations: OR (or ||) AND (or &&) XOR if (m_token==SIMILAR_TO) return INFIX("SIMILAR TO"); if (m_token==NOT_SIMILAR_TO) return INFIX("NOT SIMILAR TO"); if (m_token==OR) return INFIX("OR"); if (m_token==AND) return INFIX("AND"); if (m_token==XOR) return INFIX("XOR"); // other string operations: || (as CONCATENATION) if (m_token==CONCATENATION) return INFIX("||"); // SpecialBinary "pseudo operators": /* not handled here */ return INFIX( QString("{INVALID_BINARY_OPERATOR#%1} ").arg(m_token)); } //========================================= ConstExpr::ConstExpr( int token, const QVariant& val) : BaseExpr( token ) , value(val) { m_cl = KexiDBExpr_Const; } ConstExpr::~ConstExpr() { } Field::Type ConstExpr::type() { if (m_token==SQL_NULL) return Field::Null; if (m_token==INTEGER_CONST) { //TODO ok? //TODO: add sign info? if (value.type() == QVariant::Int || value.type() == QVariant::UInt) { Q_LLONG v = value.toInt(); if (v <= 0xff && v > -0x80) return Field::Byte; if (v <= 0xffff && v > -0x8000) return Field::ShortInteger; return Field::Integer; } return Field::BigInteger; } if (m_token==REAL_CONST) return Field::Double; if (m_token==CHARACTER_STRING_LITERAL) { //TODO: Field::defaultTextLength() is hardcoded now! if (value.toString().length() > Field::defaultTextLength()) return Field::LongText; else return Field::Text; } return Field::InvalidType; } QString ConstExpr::debugString() { return QString("ConstExpr('") + tokenToString() +"'," + toString() + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); } QString ConstExpr::toString() { if (m_token==SQL_NULL) return "NULL"; if (m_token==REAL_CONST) return QString::number(value.toPoint().x())+"."+QString::number(value.toPoint().y()); return value.toString(); } bool ConstExpr::validate(ParseInfo& parseInfo) { if (!BaseExpr::validate(parseInfo)) return false; return type()!=Field::InvalidType; } //========================================= VariableExpr::VariableExpr( const QString& _name) : BaseExpr( 0/*undefined*/ ) , name(_name) , field(0) , tablePositionForField(-1) , tableForQueryAsterisk(0) { m_cl = KexiDBExpr_Variable; } VariableExpr::~VariableExpr() { } QString VariableExpr::debugString() { return QString("VariableExpr(") + name + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); } QString VariableExpr::toString() { return name; } //! We're assuming it's called after VariableExpr::validate() Field::Type VariableExpr::type() { if (field) return field->type(); //BTW, asterisks are not stored in VariableExpr outside of parser, so ok. return Field::InvalidType; } #define IMPL_ERROR(errmsg) parseInfo.errMsg = "Implementation error"; parseInfo.errDescr = errmsg bool VariableExpr::validate(ParseInfo& parseInfo) { if (!BaseExpr::validate(parseInfo)) return false; field = 0; tablePositionForField = -1; tableForQueryAsterisk = 0; /* taken from parser's addColumn(): */ kdDebug() << "checking variable name: " << name << endl; int dotPos = name.find('.'); QString tableName, fieldName; //TODO: shall we also support db name? if (dotPos>0) { tableName = name.left(dotPos); fieldName = name.mid(dotPos+1); } if (tableName.isEmpty()) {//fieldname only fieldName = name; if (fieldName=="*") { // querySchema->addAsterisk( new QueryAsterisk(querySchema) ); return true; } //find first table that has this field Field *firstField = 0; for (TableSchema::ListIterator it(*parseInfo.querySchema->tables()); it.current(); ++it) { Field *f = it.current()->field(fieldName); if (f) { if (!firstField) { firstField = f; } else if (f->table()!=firstField->table()) { //ambiguous field name parseInfo.errMsg = i18n("Ambiguous field name"); parseInfo.errDescr = i18n("Both table \"%1\" and \"%2\" have defined \"%3\" field. " "Use \".%4\" notation to specify table name.") .arg(firstField->table()->name()).arg(f->table()->name()) .arg(fieldName).arg(fieldName); return false; } } } if (!firstField) { parseInfo.errMsg = i18n("Field not found"); parseInfo.errDescr = i18n("Table containing \"%1\" field not found").arg(fieldName); return false; } //ok field = firstField; //store // querySchema->addField(firstField); return true; } //table.fieldname or tableAlias.fieldname tableName = tableName.lower(); TableSchema *ts = parseInfo.querySchema->table( tableName ); if (ts) {//table.fieldname //check if "table" is covered by an alias const QValueList tPositions = parseInfo.querySchema->tablePositions(tableName); - QValueList::ConstIterator it = tPositions.begin(); + QValueList::ConstIterator it = tPositions.constBegin(); QCString tableAlias; bool covered = true; - for (; it!=tPositions.end() && covered; ++it) { + for (; it!=tPositions.constEnd() && covered; ++it) { tableAlias = parseInfo.querySchema->tableAlias(*it); if (tableAlias.isEmpty() || tableAlias.lower()==tableName.latin1()) covered = false; //uncovered kdDebug() << " --" << "covered by " << tableAlias << " alias" << endl; } if (covered) { parseInfo.errMsg = i18n("Could not access the table directly using its name"); parseInfo.errDescr = i18n("Table \"%1\" is covered by aliases. Instead of \"%2\", " "you can write \"%3\"").arg(tableName) .arg(tableName+"."+fieldName).arg(tableAlias+"."+fieldName.latin1()); return false; } } int tablePosition = -1; if (!ts) {//try to find tableAlias tablePosition = parseInfo.querySchema->tablePositionForAlias( tableName.latin1() ); if (tablePosition>=0) { ts = parseInfo.querySchema->tables()->at(tablePosition); if (ts) kdDebug() << " --it's a tableAlias.name" << endl; } } if (!ts) { parseInfo.errMsg = i18n("Table not found"); parseInfo.errDescr = i18n("Unknown table \"%1\"").arg(tableName); return false; } QValueList *positionsList = parseInfo.repeatedTablesAndAliases[ tableName ]; if (!positionsList) { //for sanity IMPL_ERROR(tableName + "." + fieldName + ", !positionsList "); return false; } //it's a table.* if (fieldName=="*") { if (positionsList->count()>1) { parseInfo.errMsg = i18n("Ambiguous \"%1.*\" expression").arg(tableName); parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined").arg(tableName); return false; } tableForQueryAsterisk = ts; // querySchema->addAsterisk( new QueryAsterisk(querySchema, ts) ); return true; } kdDebug() << " --it's a table.name" << endl; Field *realField = ts->field(fieldName); if (!realField) { parseInfo.errMsg = i18n("Field not found"); parseInfo.errDescr = i18n("Table \"%1\" has no \"%2\" field") .arg(tableName).arg(fieldName); return false; } // check if table or alias is used twice and both have the same column // (so the column is ambiguous) int numberOfTheSameFields = 0; for (QValueList::iterator it = positionsList->begin(); it!=positionsList->end();++it) { TableSchema *otherTS = parseInfo.querySchema->tables()->at(*it); if (otherTS->field(fieldName)) numberOfTheSameFields++; if (numberOfTheSameFields>1) { parseInfo.errMsg = i18n("Ambiguous \"%1.%2\" expression") .arg(tableName).arg(fieldName); parseInfo.errDescr = i18n("More than one \"%1\" table or alias defined containing \"%2\" field") .arg(tableName).arg(fieldName); return false; } } field = realField; //store tablePositionForField = tablePosition; // querySchema->addField(realField, tablePosition); return true; } //========================================= static QValueList FunctionExpr_builtIns; static const char* FunctionExpr_builtIns_[] = {"SUM", "MIN", "MAX", "AVG", "COUNT", "STD", "STDDEV", "VARIANCE", 0 }; QValueList FunctionExpr::builtInAggregates() { if (FunctionExpr_builtIns.isEmpty()) { for (const char **p = FunctionExpr_builtIns_; *p; p++) FunctionExpr_builtIns << *p; } return FunctionExpr_builtIns; } FunctionExpr::FunctionExpr( const QString& _name, NArgExpr* args_ ) : BaseExpr( 0/*undefined*/ ) , name(_name) , args(args_) { if (isBuiltInAggregate(name.latin1())) m_cl = KexiDBExpr_Aggregation; else m_cl = KexiDBExpr_Function; args->setParent( this ); } FunctionExpr::~FunctionExpr() { delete args; } QString FunctionExpr::debugString() { return QString("FunctionExpr(") + name + "," + args->debugString() + QString(",type=%1)").arg(Driver::defaultSQLTypeName(type())); } QString FunctionExpr::toString() { return name + "(" + args->toString() + ")"; } Field::Type FunctionExpr::type() { //TODO return Field::InvalidType; } bool FunctionExpr::validate(ParseInfo& parseInfo) { if (!BaseExpr::validate(parseInfo)) return false; return args->validate(parseInfo); } bool FunctionExpr::isBuiltInAggregate(const QCString& fname) { return builtInAggregates().find(fname.upper())!=FunctionExpr_builtIns.end(); } #if 0 //========================================= ArithmeticExpr::ArithmeticExpr(BaseExpr *l_n, int typ, BaseExpr *r_n) : BinaryExpr(l_n,typ,r_n) { } void ArithmeticExpr::check() { BinaryExpr::check(); BaseExpr *l_n = l.at(0); BaseExpr *r_n = l.at(1); ASSERT(l_n!=NULL); ASSERT(r_n!=NULL); l_n->check(); r_n->check(); /*typ wyniku: oba argumenty musza miec rwartosc typu string lub int (stala albo zmienna) */ if (l_n->nodeTypeIs(TYP_INT) && r_n->nodeTypeIs(TYP_INT)) { node_type=new NConstType(TYP_INT); } else if (l_n->nodeTypeIs(TYP_STR) && r_n->nodeTypeIs(TYP_STR) && is('+')) { node_type=new NConstType(TYP_STR); } else { ERR("Niepoprawne argumenty typu '%s' i '%s' dla operatora '%s'", l_n->nodeTypeName(), r_n->nodeTypeName(), QChar(typ())); } debug("ArithmeticExpr::check() OK"); } //========================================= RelationalExpr::RelationalExpr(BaseExpr *l_n, int typ, BaseExpr *r_n) : BinaryExpr(l_n,typ,r_n) { } void RelationalExpr::check() { BinaryExpr::check(); BaseExpr *l_n = l.at(0); BaseExpr *r_n = l.at(1); ASSERT(l_n!=NULL); ASSERT(r_n!=NULL); l_n->check(); r_n->check(); char errop=0; //==1 gdy bledna oper. /*typ wyniku: const bool dla: " =,<,>,<>,<=,>= " " =,<,>,<>,<=,>= " " =,<> " " =,<> " " =,<> " */ if ((l_n->nodeTypeIs(TYP_INT) && r_n->nodeTypeIs(TYP_INT)) ||(l_n->nodeTypeIs(TYP_STR) && r_n->nodeTypeIs(TYP_STR))) { switch (typ()) { case '=': case '<': case '>': case REL_ROZNE: case REL_MN_ROWNE: case REL_WIEK_ROWNE: break;//ok default: errop=1;//blad } } // else if ((l_n->nodeTypeIs(TYP_BOOL) && r_n->nodeTypeIs(TYP_BOOL)) // ||(l_n->nodeTypeIs(TYP_CLASS) && r_n->nodeTypeIs(TYP_CLASS)) // ||(l_n->nodeTypeIs(TYP_DICT) && r_n->nodeTypeIs(TYP_DICT))) { else if ((l_n->nodeTypeIs(TYP_BOOL) || l_n->nodeTypeIs(TYP_DICT) || l_n->nodeTypeIs(TYP_CLASS) || l_n->nodeTypeIs(TYP_NIL)) && r_n->nodeType()->like(l_n->nodeType())) { switch (typ()) { case '=': case REL_ROZNE: break;//ok default: errop=1;//blad } } else errop=1; if (errop) { ERR("Niepoprawne argumenty typu '%s' i '%s' dla operatora relacyjnego '%s'", l_n->nodeTypeName(), r_n->nodeTypeName(), tname()); } else {//ok: node_type=new NConstType(TYP_BOOL); } } //========================================= LogicalExpr::LogicalExpr(BaseExpr *l_n, int typ, BaseExpr *r_n) : BinaryExpr(l_n,typ,r_n) { } void LogicalExpr::check() { BinaryExpr::check(); BaseExpr *l_n = l.at(0); BaseExpr *r_n = l.at(1); ASSERT(l_n!=NULL); ASSERT(r_n!=NULL); l_n->check(); r_n->check(); /*typ wyniku: const bool dla " OR/AND " */ if (l_n->nodeTypeIs(TYP_BOOL) && r_n->nodeTypeIs(TYP_BOOL)) { node_type = l_n->nodeType(); } else { ERR("Niepoprawne argumenty typu '%s' i '%s' dla operacji logicznej '%s'", l_n->nodeTypeName(), r_n->nodeTypeName(), QString(is(AND)?"and":"or")); } } #endif #if 0 NConstInt::NConstInt(int v) : BaseExpr(CONST_INT), val(v) { node_type = new NConstType(TYP_INT); } //----------------------------------------- const QString NConstInt::dump() { return QString(name()) + "(" + QString::number(val)+ ")"; } //----------------------------------------- const QString NConstInt::name() { return "ConstInt"; } //----------------------------------------- int NConstInt::value() { return val; } //----------------------------------------- void NConstInt::check() { BaseExpr::check(); } //========================================= //stala booleowska NConstBool::NConstBool(const char v) : BaseExpr(CONST_BOOL), val(v) { node_type = new NConstType(TYP_BOOL); } //----------------------------------------- const QString NConstBool::dump() { return QString(name()) + "(" + QString::number(val) + ")"; } //----------------------------------------- const QString NConstBool::name() { return "ConstBool"; } //----------------------------------------- const char NConstBool::value() { return val; } //----------------------------------------- void NConstBool::check() { BaseExpr::check(); } //========================================= //stala znakowa NConstStr::NConstStr(const char *v) : BaseExpr(CONST_STR), val(v) { node_type = new NConstType(TYP_STR); } //----------------------------------------- const QString NConstStr::dump() { return QString(name()) + "(" + val + ")"; } //----------------------------------------- const QString NConstStr::name() { return "ConstStr"; } //----------------------------------------- const QString NConstStr::value() { return val; } //----------------------------------------- void NConstStr::check() { BaseExpr::check(); //dodaj etykiete i zapisz string lab=generateUniqueLabel("const"); gen.mlabel(lab); gen.mcode(MN_TEXT,val); } #endif /* OLD Expression::Expression() :d(0)//unused ,m_field(0) { } Expression::~Expression() { } int Expression::type() { if (!m_field) return Field::InvalidType; return m_field->type(); } */ diff --git a/kexi/kexidb/parser/parser_p.cpp b/kexi/kexidb/parser/parser_p.cpp index 0278747789..49a67ddeb0 100644 --- a/kexi/kexidb/parser/parser_p.cpp +++ b/kexi/kexidb/parser/parser_p.cpp @@ -1,593 +1,593 @@ /* This file is part of the KDE project Copyright (C) 2004 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "parser_p.h" #include "sqlparser.h" #include #include #include #include using namespace KexiDB; Parser *parser; Field *field; //bool requiresTable; QPtrList fieldList; int current = 0; QString ctoken = ""; //------------------------------------- ParserPrivate::ParserPrivate() : reservedKeywords(997, 997, false) , initialized(false) { clear(); table = 0; select = 0; db = 0; } ParserPrivate::~ParserPrivate() { delete select; delete table; } void ParserPrivate::clear() { operation = Parser::OP_None; error = ParserError(); } //------------------------------------- ParseInfo::ParseInfo(KexiDB::QuerySchema *query) : repeatedTablesAndAliases(997, false) , querySchema(query) { repeatedTablesAndAliases.setAutoDelete(true); } ParseInfo::~ParseInfo() { } //------------------------------------- extern int yyparse(); extern void tokenize(const char *data); void yyerror(const char *str) { kdDebug() << "error: " << str << endl; kdDebug() << "at character " << current << " near tooken " << ctoken << endl; parser->setOperation(Parser::OP_Error); const bool otherError = (qstrnicmp(str, "other error", 11)==0); if (parser->error().type().isEmpty() && (str==0 || strlen(str)==0 || qstrnicmp(str, "syntax error", 12)==0 || qstrnicmp(str, "parse error", 11)==0) || otherError) { kdDebug() << parser->statement() << endl; QString ptrline = ""; for(int i=0; i < current; i++) ptrline += " "; ptrline += "^"; kdDebug() << ptrline << endl; //lexer may add error messages QString lexerErr = parser->error().error(); QString errtypestr(str); if (lexerErr.isEmpty()) { #if 0 if (errtypestr.startsWith("parse error, unexpected ")) { //something like "parse error, unexpected IDENTIFIER, expecting ',' or ')'" QString e = errtypestr.mid(24); kdDebug() << e <=2) { kdDebug() << "**" << captured[1] << endl; kdDebug() << "**" << captured[2] << endl; } } // IDENTIFIER, expecting '")) { e = errtypestr.mid(47); kdDebug() << e <isReservedKeyword(ctoken.latin1())) parser->setError( ParserError(i18n("Syntax Error"), i18n("\"%1\" is a reserved keyword").arg(ctoken)+lexerErr, ctoken, current) ); else parser->setError( ParserError(i18n("Syntax Error"), i18n("Syntax Error near \"%1\"").arg(ctoken)+lexerErr, ctoken, current) ); } } } void setError(const QString& errName, const QString& errDesc) { parser->setError( ParserError(errName, errDesc, ctoken, current) ); yyerror(errName.latin1()); } void setError(const QString& errDesc) { setError("other error", errDesc); } /* this is better than assert() */ #define IMPL_ERROR(errmsg) setError("Implementation error", errmsg) bool parseData(Parser *p, const char *data) { /* todo: make this REENTRANT */ parser = p; parser->clear(); field = 0; fieldList.clear(); // requiresTable = false; if (!data) { ParserError err(i18n("Error"), i18n("No query specified"), ctoken, current); parser->setError(err); yyerror(""); parser = 0; return false; } tokenize(data); if (!parser->error().type().isEmpty()) { parser = 0; return false; } yyparse(); bool ok = true; if(parser->operation() == Parser::OP_Select) { kdDebug() << "parseData(): ok" << endl; // kdDebug() << "parseData(): " << tableDict.count() << " loaded tables" << endl; /* TableSchema *ts; for(QDictIterator it(tableDict); TableSchema *s = tableList.first(); s; s = tableList.next()) { kdDebug() << " " << s->name() << endl; }*/ /*removed Field::ListIterator it = parser->select()->fieldsIterator(); for(Field *item; (item = it.current()); ++it) { if(tableList.findRef(item->table()) == -1) { ParserError err(i18n("Field List Error"), i18n("Unknown table '%1' in field list").arg(item->table()->name()), ctoken, current); parser->setError(err); yyerror("fieldlisterror"); ok = false; } }*/ //take the dummy table out of the query // parser->select()->removeTable(dummy); } else { ok = false; } // tableDict.clear(); parser = 0; return ok; } /* Adds \a column to \a querySchema. \a column can be in a form of table.field, tableAlias.field or field */ bool addColumn( ParseInfo& parseInfo, BaseExpr* columnExpr ) { if (!columnExpr->validate(parseInfo)) { setError(parseInfo.errMsg, parseInfo.errDescr); return false; } VariableExpr *v_e = dynamic_cast(columnExpr); if (columnExpr->exprClass() == KexiDBExpr_Variable && v_e) { //it's a variable: if (v_e->name=="*") {//all tables asterisk if (parseInfo.querySchema->tables()->isEmpty()) { setError(i18n("\"*\" could not be used if no tables are specified")); return false; } parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema) ); } else if (v_e->tableForQueryAsterisk) {//one-table asterisk parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema, v_e->tableForQueryAsterisk) ); } else if (v_e->field) {//"table.field" or "field" (bound to a table or not) parseInfo.querySchema->addField(v_e->field, v_e->tablePositionForField); } else { IMPL_ERROR("addColumn(): unknown case!"); return false; } return true; } //it's complex expression Field *field = new Field(parseInfo.querySchema, columnExpr); parseInfo.querySchema->addField(field); #if 0 kdDebug() << "found variable name: " << varName << endl; int dotPos = varName.find('.'); QString tableName, fieldName; //TODO: shall we also support db name? if (dotPos>0) { tableName = varName.left(dotPos); fieldName = varName.mid(dotPos+1); } if (tableName.isEmpty()) {//fieldname only fieldName = varName; if (fieldName=="*") { parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema) ); } else { //find first table that has this field Field *firstField = 0; for (TableSchema::ListIterator it(*parseInfo.querySchema->tables()); it.current(); ++it) { Field *f = it.current()->field(fieldName); if (f) { if (!firstField) { firstField = f; } else if (f->table()!=firstField->table()) { //ambiguous field name setError(i18n("Ambiguous field name"), i18n("Both table \"%1\" and \"%2\" have defined \"%3\" field. " "Use \".%4\" notation to specify table name.") .arg(firstField->table()->name()).arg(f->table()->name()) .arg(fieldName).arg(fieldName)); return false; } } } if (!firstField) { setError(i18n("Field not found"), i18n("Table containing \"%1\" field not found").arg(fieldName)); return false; } //ok parseInfo.querySchema->addField(firstField); } } else {//table.fieldname or tableAlias.fieldname tableName = tableName.lower(); TableSchema *ts = parseInfo.querySchema->table( tableName ); if (ts) {//table.fieldname //check if "table" is covered by an alias const QValueList tPositions = parseInfo.querySchema->tablePositions(tableName); - QValueList::ConstIterator it = tPositions.begin(); + QValueList::ConstIterator it = tPositions.constBegin(); QCString tableAlias; bool covered = true; - for (; it!=tPositions.end() && covered; ++it) { + for (; it!=tPositions.constEnd() && covered; ++it) { tableAlias = parseInfo.querySchema->tableAlias(*it); if (tableAlias.isEmpty() || tableAlias.lower()==tableName.latin1()) covered = false; //uncovered kdDebug() << " --" << "covered by " << tableAlias << " alias" << endl; } if (covered) { setError(i18n("Could not access the table directly using its name"), i18n("Table \"%1\" is covered by aliases. Instead of \"%2\", " "you can write \"%3\"").arg(tableName) .arg(tableName+"."+fieldName).arg(tableAlias+"."+fieldName.latin1())); return false; } } int tablePosition = -1; if (!ts) {//try to find tableAlias tablePosition = parseInfo.querySchema->tablePositionForAlias( tableName.latin1() ); if (tablePosition>=0) { ts = parseInfo.querySchema->tables()->at(tablePosition); if (ts) kdDebug() << " --it's a tableAlias.name" << endl; } } if (ts) { QValueList *positionsList = repeatedTablesAndAliases[ tableName ]; if (!positionsList) { IMPL_ERROR(tableName + "." + fieldName + ", !positionsList "); return false; } if (fieldName=="*") { if (positionsList->count()>1) { setError(i18n("Ambiguous \"%1.*\" expression").arg(tableName), i18n("More than one \"%1\" table or alias defined").arg(tableName)); return false; } parseInfo.querySchema->addAsterisk( new QueryAsterisk(parseInfo.querySchema, ts) ); } else { kdDebug() << " --it's a table.name" << endl; Field *realField = ts->field(fieldName); if (realField) { // check if table or alias is used twice and both have the same column // (so the column is ambiguous) int numberOfTheSameFields = 0; for (QValueList::iterator it = positionsList->begin(); it!=positionsList->end();++it) { TableSchema *otherTS = parseInfo.querySchema->tables()->at(*it); if (otherTS->field(fieldName)) numberOfTheSameFields++; if (numberOfTheSameFields>1) { setError(i18n("Ambiguous \"%1.%2\" expression").arg(tableName).arg(fieldName), i18n("More than one \"%1\" table or alias defined containing \"%2\" field") .arg(tableName).arg(fieldName)); return false; } } parseInfo.querySchema->addField(realField, tablePosition); } else { setError(i18n("Field not found"), i18n("Table \"%1\" has no \"%2\" field") .arg(tableName).arg(fieldName)); return false; } } } else { tableNotFoundError(tableName); return false; } } #endif return true; } //clean up no longer needed objects #define CLEANUP \ delete colViews; \ delete tablesList QuerySchema* parseSelect( QuerySchema* querySchema, NArgExpr* colViews, NArgExpr* tablesList, BaseExpr* whereExpr ) { ParseInfo parseInfo(querySchema); //-------tables list // assert( tablesList ); //&& tablesList->exprClass() == KexiDBExpr_TableList ); uint columnNum = 0; /*TODO: use this later if there are columns that use database fields, e.g. "SELECT 1 from table1 t, table2 t") is ok however. */ //used to collect information about first repeated table name or alias: // QDict tableNamesAndTableAliases(997, false); // QString repeatedTableNameOrTableAlias; if (tablesList) { for (int i=0; iargs(); i++, columnNum++) { BaseExpr *e = tablesList->arg(i); VariableExpr* t_e = 0; QCString aliasString; if (e->exprClass() == KexiDBExpr_SpecialBinary) { BinaryExpr* t_with_alias = dynamic_cast(e); assert(t_with_alias); assert(t_with_alias->left()->exprClass() == KexiDBExpr_Variable); assert(t_with_alias->right()->exprClass() == KexiDBExpr_Variable && (t_with_alias->token()==AS || t_with_alias->token()==0)); t_e = dynamic_cast(t_with_alias->left()); aliasString = dynamic_cast(t_with_alias->right())->name.latin1(); } else { t_e = dynamic_cast(e); } assert(t_e); QCString tname = t_e->name.latin1(); TableSchema *s = parser->db()->tableSchema(tname); if(!s) { setError(//i18n("Field List Error"), i18n("Table \"%1\" does not exist").arg(tname)); // yyerror("fieldlisterror"); CLEANUP; return 0; } QCString tableOrAliasName; if (!aliasString.isEmpty()) { tableOrAliasName = aliasString; kdDebug() << "- add alias for table: " << aliasString << endl; } else { tableOrAliasName = tname; } // 1. collect information about first repeated table name or alias // (potential ambiguity) QValueList *list = parseInfo.repeatedTablesAndAliases[tableOrAliasName]; if (list) { //another table/alias with the same name list->append( i ); kdDebug() << "- another table/alias with name: " << tableOrAliasName << endl; } else { list = new QValueList(); list->append( i ); parseInfo.repeatedTablesAndAliases.insert( tableOrAliasName, list ); kdDebug() << "- first table/alias with name: " << tableOrAliasName << endl; } /* if (repeatedTableNameOrTableAlias.isEmpty()) { if (tableNamesAndTableAliases[tname]) repeatedTableNameOrTableAlias=tname; else tableNamesAndTableAliases.insert(tname, (const char*)1); } if (!aliasString.isEmpty()) { kdDebug() << "- add alias for table: " << aliasString << endl; // querySchema->setTableAlias(columnNum, aliasString); //2. collect information about first repeated table name or alias // (potential ambiguity) if (repeatedTableNameOrTableAlias.isEmpty()) { if (tableNamesAndTableAliases[aliasString]) repeatedTableNameOrTableAlias=aliasString; else tableNamesAndTableAliases.insert(aliasString, (const char*)1); } }*/ kdDebug() << "addTable: " << tname << endl; querySchema->addTable( s, aliasString ); } } /* set parent table if there's only one */ // if (parser->select()->tables()->count()==1) if (querySchema->tables()->count()==1) querySchema->setParentTable(querySchema->tables()->first()); //-------add fields if (colViews) { BaseExpr *e; columnNum = 0; for (BaseExpr::ListIterator it(colViews->list);(e = it.current()); columnNum++) { bool moveNext = true; //used to avoid ++it when an item is taken from the list BaseExpr *columnExpr = e; VariableExpr* aliasVariable = 0; if (e->exprClass() == KexiDBExpr_SpecialBinary && dynamic_cast(e) && (e->token()==AS || e->token()==0)) { //KexiDBExpr_SpecialBinary: with alias columnExpr = dynamic_cast(e)->left(); // isFieldWithAlias = true; aliasVariable = dynamic_cast(dynamic_cast(e)->right()); if (!aliasVariable) { setError(i18n("Invalid alias definition for column \"%1\"") .arg(columnExpr->toString())); //ok? CLEANUP; return 0; } } const int c = columnExpr->exprClass(); const bool isExpressionField = c == KexiDBExpr_Const || c == KexiDBExpr_Unary || c == KexiDBExpr_Arithm || c == KexiDBExpr_Logical || c == KexiDBExpr_Relational || c == KexiDBExpr_Const || c == KexiDBExpr_Function || c == KexiDBExpr_Aggregation; if (c == KexiDBExpr_Variable) { //just a variable, do nothing, addColumn() will handle this } else if (isExpressionField) { //expression object will be reused, take, will be owned, do not destroy KexiDBDbg << colViews->list.count() << " " << it.current()->debugString() << endl; KexiDBDbg << it.atFirst () << endl; colViews->list.take(0); //take() doesn't work moveNext = false; } else if (aliasVariable) { //take first (left) argument of the special binary expr, will be owned, do not destroy dynamic_cast(e)->list.take(0); } else { setError(i18n("Invalid \"%1\" column definition").arg(e->toString())); //ok? CLEANUP; return 0; } if (!addColumn( parseInfo, columnExpr )) { CLEANUP; return 0; } if (aliasVariable) { kdDebug() << "ALIAS \"" << aliasVariable->name << "\" set for column " << columnNum << endl; querySchema->setColumnAlias(columnNum, aliasVariable->name.latin1()); } /* if (e->exprClass() == KexiDBExpr_SpecialBinary && dynamic_cast(e) && (e->type()==AS || e->type()==0)) { //also add alias VariableExpr* aliasVariable = dynamic_cast(dynamic_cast(e)->right()); if (!aliasVariable) { setError(i18n("Invalid column alias definition")); //ok? return 0; } kdDebug() << "ALIAS \"" << aliasVariable->name << "\" set for column " << columnNum << endl; querySchema->setColumnAlias(columnNum, aliasVariable->name.latin1()); }*/ if (moveNext) ++it; } } //----- WHERE expr. if (whereExpr) { if (!whereExpr->validate(parseInfo)) { setError(parseInfo.errMsg, parseInfo.errDescr); CLEANUP; return false; } querySchema->setWhereExpression(whereExpr); } kdDebug() << "Select ColViews=" << (colViews ? colViews->debugString() : QString::null) << " Tables=" << (tablesList ? tablesList->debugString() : QString::null) << endl; CLEANUP; return querySchema; } #undef CLEANUP diff --git a/kexi/main/keximainwindowimpl.cpp b/kexi/main/keximainwindowimpl.cpp index 505d977a37..5f32c07057 100644 --- a/kexi/main/keximainwindowimpl.cpp +++ b/kexi/main/keximainwindowimpl.cpp @@ -1,2872 +1,2872 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2004 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "keximainwindowimpl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "projectsettingsui.h" #include "kexibrowser.h" #include "kexipropertyeditorview.h" #include "kexipropertybuffer.h" #include "kexiactionproxy.h" #include "kexidialogbase.h" #include "kexipartmanager.h" #include "kexipart.h" #include "kexipartinfo.h" #include "kexipartguiclient.h" #include "kexiproject.h" #include "kexiprojectdata.h" #include "kexiprojectset.h" #include "kexi.h" #include "kexi_utils.h" #include "kexistatusbar.h" #include "kexiinternalpart.h" #include "kexiuseraction.h" #include "startup/KexiStartup.h" #include "startup/KexiNewProjectWizard.h" #include "startup/KexiStartupDialog.h" /* #include "startup/KexiConnSelector.h" #include "startup/KexiProjectSelectorBase.h" #include "startup/KexiProjectSelector.h" */ #include "kexicontexthelp.h" #include "kexinamedialog.h" //Extreme verbose debug #if defined(Q_WS_WIN) # define KexiVDebug kdDebug() #endif #if !defined(KexiVDebug) # define KexiVDebug if (0) kdDebug() #endif //first fix the geometry #define KEXI_NO_CTXT_HELP 1 //show property editor #define KEXI_PROP_EDITOR 1 //! @todo REENABLE when blinking and dock //! width changes will be removed in KMDI //#define PROPEDITOR_VISIBILITY_CHANGES //#undef KDOCKWIDGET_P #if defined(KDOCKWIDGET_P) #include #endif typedef QIntDict KexiDialogDict; class KexiMainWindowImpl::Private { public: KexiProject *prj; KConfig *config; #ifndef KEXI_NO_CTXT_HELP KexiContextHelp *ctxHelp; #endif KexiBrowser *nav; QGuardedPtr propEditor; QGuardedPtr propBuffer; KexiDialogDict dialogs; KXMLGUIClient *curDialogGUIClient, *curDialogViewGUIClient, *closedDialogGUIClient, *closedDialogViewGUIClient; QGuardedPtr curDialog; KexiNameDialog *nameDialog; QTimer timer; //helper timer // QSignalMapper *actionMapper; QAsciiDict popups; //list of menu popups QPopupMenu *createMenu; QString origAppCaption; // actions_for_view_modes; // KRadioAction *last_checked_mode; #ifndef KEXI_NO_CTXT_HELP KToggleAction *action_show_helper; #endif //data menu KAction *action_data_save_row; KAction *action_data_cancel_row_changes; //settings menu KAction *action_configure; //for dock windows KMdiToolViewAccessor* navToolWindow; KMdiToolViewAccessor* propEditorToolWindow; QGuardedPtr focus_before_popup; // KexiRelationPart *relationPart; int privateIDCounter; //!< counter: ID for private "document" like Relations window bool block_KMdiMainFrm_eventFilter : 1; //! Set to true only in destructor, used by closeDialog() to know if //! user can cancel dialog closing. If true user even doesn't see any messages //! before closing a dialog. This is for extremely sanity... and shouldn't be even needed. bool forceDialogClosing : 1; //! Indicates that we're inside closeDialog() method - to avoid inf. recursion //! on dialog removing bool insideCloseDialog : 1; //! Used in several places to show info dialog at startup (only once per session) //! before displaying other stuff bool showImportantInfoOnStartup : 1; // //! Used sometimes to block showErrorMessage() // bool disableErrorMessages : 1; //! Indicates if project is started in --final mode bool final : 1; Private(KexiMainWindowImpl* w) : wnd(w) , dialogs(401) { propEditorToolWindow=0; final = false; nav=0; navToolWindow=0; prj = 0; curDialogGUIClient=0; curDialogViewGUIClient=0; closedDialogGUIClient=0; closedDialogViewGUIClient=0; nameDialog=0; curDialog=0; block_KMdiMainFrm_eventFilter=false; focus_before_popup=0; // relationPart=0; privateIDCounter=0; action_view_nav=0; action_view_propeditor=0; forceDialogClosing=false; insideCloseDialog=false; createMenu=0; showImportantInfoOnStartup=true; // disableErrorMessages=false; // last_checked_mode=0; propEditorDockSeparatorPos=-1; #ifndef KEXI_SHOW_UNIMPLEMENTED dummy_action = new KActionMenu("", wnd); #endif } ~Private() { } KexiMainWindowImpl *wnd; /*! Toggles last checked view mode radio action, if available. */ void toggleLastCheckedMode() { if (curDialog.isNull()) return; KRadioAction *ra = actions_for_view_modes[ curDialog->currentViewMode() ]; if (ra) ra->setChecked(true); // if (!last_checked_mode) // return; // last_checked_mode->setChecked(true); } int propEditorDockSeparatorPos; void updatePropEditorDockWidthInfo() { if (propEditor) { KDockWidget *dw = (KDockWidget *)propEditor->parentWidget(); #if defined(KDOCKWIDGET_P) KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); if (ds) propEditorDockSeparatorPos = ds->separatorPos(); #endif } } void showStartProcessMsg(const QStringList& args) { wnd->showErrorMessage(i18n("Could not start %1 application.").arg(KEXI_APP_NAME), i18n("Command \"%1\" failed.").arg(args.join(" "))); } void hideMenuItem(const QString& menuName, const QString& itemText, bool alsoSeparator) { QPopupMenu *pm = popups[menuName.ascii()]; if (!pm) return; uint i=0; const uint c = pm->count(); for (;itext( pm->idAt(i) ) <text( pm->idAt(i) ).lower().stripWhiteSpace()==itemText.lower().stripWhiteSpace()) break; } if (isetItemVisible( pm->idAt(i), false ); if (alsoSeparator) pm->setItemVisible( pm->idAt(i+1), false ); //also separator } } void updatePropEditorVisibility(int viewMode) { if (propEditorToolWindow) { if (viewMode==0 || viewMode==Kexi::DataViewMode) { #ifdef PROPEDITOR_VISIBILITY_CHANGES propEditorToolWindow->hide(); #endif } else { propEditorToolWindow->show(); } } } }; //------------------------------------------------- KexiMainWindowImpl::KexiMainWindowImpl() : KexiMainWindow() , KexiGUIMessageHandler(this) , d(new KexiMainWindowImpl::Private(this) ) { KexiProjectData *pdata = Kexi::startupHandler().projectData(); d->final = Kexi::startupHandler().forcedFinalMode() /* <-- simply forced final mode */ /* project has 'final mode' set as default and not 'design mode' override is found: */ || (pdata && pdata->finalMode() && !Kexi::startupHandler().forcedDesignMode()); if(d->final) kdDebug() << "KexiMainWindowImpl::KexiMainWindowImpl(): starting up in final mode" << endl; d->config = kapp->config(); if ( !initialGeometrySet() ) { int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->screenGeometry(scnum); d->config->setGroup("MainWindow"); QSize s ( d->config->readNumEntry( QString::fromLatin1("Width %1").arg(desk.width()), 700 ), d->config->readNumEntry( QString::fromLatin1("Height %1").arg(desk.height()), 480 ) ); resize (kMin (s.width(), desk.width()), kMin(s.height(), desk.height())); } setManagedDockPositionModeEnabled(true);//TODO(js): remove this if will be default in kmdi :) setStandardMDIMenuEnabled(false); setAsDefaultHost(); //this is default host now. KGlobal::iconLoader()->addAppDir("kexi"); //get informed connect(&Kexi::partManager(),SIGNAL(partLoaded(KexiPart::Part*)),this,SLOT(slotPartLoaded(KexiPart::Part*))); connect( m_pMdi, SIGNAL(nowMaximized(bool)), this, SLOT(slotCaptionForCurrentMDIChild(bool)) ); connect( m_pMdi, SIGNAL(noMaximizedChildFrmLeft(KMdiChildFrm*)), this, SLOT(slotNoMaximizedChildFrmLeft(KMdiChildFrm*))); connect( this, SIGNAL(lastChildFrmClosed()), this, SLOT(slotLastChildFrmClosed())); //no such signal connect( m_pMdi, SIGNAL(lastChildViewClosed()), this, SLOT(slotLastChildViewClosed())); connect( this, SIGNAL(childViewIsDetachedNow(QWidget*)), this, SLOT(slotChildViewIsDetachedNow(QWidget*))); connect( this, SIGNAL(mdiModeHasBeenChangedTo(KMdi::MdiMode)), this, SLOT(slotMdiModeHasBeenChangedTo(KMdi::MdiMode))); if(!d->final) { setXMLFile("kexiui.rc"); setAcceptDrops(true); initActions(); createShellGUI(true); } (void) new KexiStatusBar(this, "status_bar"); d->origAppCaption = caption(); restoreSettings(); if(!d->final) { initContextHelp(); initPropertyEditor(); } {//store menu popups list QObjectList *l = queryList( "QPopupMenu" ); for (QObjectListIt it( *l ); it.current(); ++it ) { kdDebug() << "name=" <name() << " cname="<className()<installEventFilter(this); d->popups.insert(it.current()->name(), static_cast(it.current())); } delete l; d->createMenu = d->popups["create"]; } //fix menus a bit more: #ifndef KEXI_SHOW_UNIMPLEMENTED d->hideMenuItem("file", i18n("&Import"), true); d->hideMenuItem("help", i18n( "&Report Bug..." ), true); #endif if (!isFakingSDIApplication() && !d->final) { // QPopupMenu *menu = (QPopupMenu*) child( "window", "KPopupMenu" ); QPopupMenu *menu = d->popups["window"]; unsigned int count = menuBar()->count(); if (menu) setWindowMenu(menu); else menuBar()->insertItem( i18n("&Window"), windowMenu(), -1, count-2); // standard position is left to the last ('Help') } m_pTaskBar->setCaption(i18n("Task Bar")); //js TODO: move this to KMDIlib if (!d->final) { invalidateActions(); d->timer.singleShot(0,this,SLOT(slotLastActions())); } } KexiMainWindowImpl::~KexiMainWindowImpl() { d->forceDialogClosing=true; closeProject(); delete d; } KexiProject *KexiMainWindowImpl::project() { return d->prj; } void KexiMainWindowImpl::setWindowMenu(QPopupMenu *menu) { delete m_pWindowMenu; m_pWindowMenu = menu; int count = menuBar()->count(); //try to move "window" menu just before "Settings" menu (count-3) const QString txt = i18n("&Window"); int i; for (i=0; itext( menuBar()->idAt(i) ) << endl; if (txt==menuBar()->text( menuBar()->idAt(i) )) break; } if (iidAt(i); menuBar()->removeItemAt(i); menuBar()->insertItem(txt, m_pWindowMenu, id, count-3); } m_pWindowMenu->setCheckable(TRUE); QObject::connect( m_pWindowMenu, SIGNAL(aboutToShow()), this, SLOT(fillWindowMenu()) ); } QPopupMenu* KexiMainWindowImpl::findPopupMenu(const char *popupName) { return d->popups[popupName]; } void KexiMainWindowImpl::initActions() { // setupGUI(KMainWindow::Keys|KMainWindow::StatusBar|KMainWindow::Save|KMainWindow::Create); // d->actionMapper = new QSignalMapper(this, "act_map"); // connect(d->actionMapper, SIGNAL(mapped(const QString &)), this, SLOT(slotAction(const QString &))); // PROJECT MENU KAction *action = new KAction(i18n("&New..."), "filenew", KStdAccel::shortcut(KStdAccel::New), this, SLOT(slotProjectNew()), actionCollection(), "project_new"); action->setToolTip(i18n("Create a new project")); action->setWhatsThis(i18n("Creates a new project. Currently opened project is not affected.")); action = KStdAction::open( this, SLOT( slotProjectOpen() ), actionCollection(), "project_open" ); action->setToolTip(i18n("Open an existing project")); action->setWhatsThis(i18n("Opens an existing project. Currently opened project is not affected.")); #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_open_recent = new KActionMenu(i18n("Open Recent"), actionCollection(), "project_open_recent"); connect(d->action_open_recent->popupMenu(),SIGNAL(activated(int)),this,SLOT(slotProjectOpenRecent(int))); connect(d->action_open_recent->popupMenu(), SIGNAL(aboutToShow()),this,SLOT(slotProjectOpenRecentAboutToShow())); d->action_open_recent->popupMenu()->insertSeparator(); d->action_open_recent_more_id = d->action_open_recent->popupMenu() ->insertItem(i18n("&More Projects..."), this, SLOT(slotProjectOpenRecentMore()), 0, 1000); #else d->action_open_recent = d->dummy_action; #endif d->action_save = KStdAction::save( this, SLOT( slotProjectSave() ), actionCollection(), "project_save" ); // d->action_save = new KAction(i18n("&Save"), "filesave", KStdAccel::shortcut(KStdAccel::Save), // this, SLOT(slotProjectSave()), actionCollection(), "project_save"); d->action_save->setToolTip(i18n("Save object changes")); d->action_save->setWhatsThis(i18n("Saves object changes from currently selected window.")); #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_save_as = new KAction(i18n("Save &As..."), "filesaveas", 0, this, SLOT(slotProjectSaveAs()), actionCollection(), "project_saveas"); d->action_save_as->setToolTip(i18n("Save object as")); d->action_save_as->setWhatsThis(i18n("Saves object changes from currently selected window under a new name (within the same project).")); d->action_project_properties = new KAction(i18n("Project Properties"), "info", 0, this, SLOT(slotProjectProperties()), actionCollection(), "project_properties"); #else d->action_save_as = d->dummy_action; d->action_project_properties = d->dummy_action; #endif d->action_close = new KAction(i18n("&Close Project"), "fileclose", KStdAccel::shortcut(KStdAccel::Close), this, SLOT(slotProjectClose()), actionCollection(), "project_close" ); d->action_close->setToolTip(i18n("Close the current project")); d->action_close->setWhatsThis(i18n("Closes the current project.")); KStdAction::quit( this, SLOT(slotQuit()), actionCollection(), "quit"); #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_project_relations = new KAction(i18n("&Relationships..."), "relation", CTRL + Key_R, this, SLOT(slotProjectRelations()), actionCollection(), "project_relations"); d->action_project_relations->setToolTip(i18n("Project relationships")); d->action_project_relations->setWhatsThis(i18n("Shows project relationships.")); new KAction(i18n("From File..."), "fileopen", 0, this, SLOT(slotImportFile()), actionCollection(), "import_file"); new KAction(i18n("From Server..."), "server", 0, this, SLOT(slotImportServer()), actionCollection(), "import_server"); #else d->action_project_relations = d->dummy_action; #endif //EDIT MENU d->action_edit_cut = createSharedAction( KStdAction::Cut, "edit_cut"); d->action_edit_copy = createSharedAction( KStdAction::Copy, "edit_copy"); d->action_edit_paste = createSharedAction( KStdAction::Paste, "edit_paste"); d->action_edit_undo = createSharedAction( KStdAction::Undo, "edit_undo"); d->action_edit_redo = createSharedAction( KStdAction::Redo, "edit_redo"); d->action_edit_delete = createSharedAction(i18n("&Delete"), "button_cancel", 0/*Key_Delete*/, "edit_delete"); d->action_edit_delete->setToolTip(i18n("Delete object")); d->action_edit_delete->setWhatsThis(i18n("Deletes currently selected object.")); d->action_edit_delete_row = createSharedAction(i18n("Delete Row"), "delete_table_row", CTRL+Key_Delete, "edit_delete_row"); d->action_edit_delete_row->setToolTip(i18n("Delete currently selected row from a table")); d->action_edit_delete_row->setWhatsThis(i18n("Deletes currently selected row from a table.")); d->action_edit_clear_table = createSharedAction(i18n("Clear Table Contents"), "clear_table_contents", 0, "edit_clear_table"); d->action_edit_clear_table->setToolTip(i18n("Clear table contents")); d->action_edit_clear_table->setWhatsThis(i18n("Clears table contents.")); setActionVolatile( d->action_edit_clear_table, true ); d->action_edit_edititem = createSharedAction(i18n("Edit Item"), 0, Key_F2, "edit_edititem"); d->action_edit_edititem->setToolTip(i18n("Edit currently selected item")); d->action_edit_edititem->setWhatsThis(i18n("Edits currently selected item.")); d->action_edit_insert_empty_row = createSharedAction(i18n("&Insert Empty Row"), "insert_table_row", SHIFT | CTRL | Key_Insert, "edit_insert_empty_row"); setActionVolatile( d->action_edit_insert_empty_row, true ); d->action_edit_insert_empty_row->setToolTip(i18n("Insert one empty row above")); d->action_edit_insert_empty_row->setWhatsThis(i18n("Inserts one empty row above currently selected table row.")); //VIEW MENU d->action_view_data_mode = new KRadioAction(i18n("&Data View"), "table", KShortcut(), this, SLOT(slotViewDataMode()), actionCollection(), "view_data_mode"); d->actions_for_view_modes.insert( Kexi::DataViewMode, d->action_view_data_mode ); d->action_view_data_mode->setExclusiveGroup("view_mode"); d->action_view_data_mode->setToolTip(i18n("Switch to Data View mode")); d->action_view_data_mode->setWhatsThis(i18n("Switches to Data View mode.")); d->action_view_design_mode = new KRadioAction(i18n("D&esign View"), "state_edit", KShortcut(), this, SLOT(slotViewDesignMode()), actionCollection(), "view_design_mode"); d->actions_for_view_modes.insert( Kexi::DesignViewMode, d->action_view_design_mode ); d->action_view_design_mode->setExclusiveGroup("view_mode"); d->action_view_design_mode->setToolTip(i18n("Switch to Design View mode")); d->action_view_design_mode->setWhatsThis(i18n("Switches to Design View mode.")); d->action_view_text_mode = new KRadioAction(i18n("&Text View"), "state_sql", KShortcut(), this, SLOT(slotViewTextMode()), actionCollection(), "view_text_mode"); d->actions_for_view_modes.insert( Kexi::TextViewMode, d->action_view_text_mode ); d->action_view_text_mode->setExclusiveGroup("view_mode"); d->action_view_text_mode->setToolTip(i18n("Switch to Text View mode")); d->action_view_text_mode->setWhatsThis(i18n("Switches to Text View mode.")); d->action_view_nav = new KAction(i18n("Project Navigator"), "", ALT + Key_1, this, SLOT(slotViewNavigator()), actionCollection(), "view_navigator"); d->action_view_nav->setToolTip(i18n("Go to Project navigator panel")); d->action_view_nav->setWhatsThis(i18n("Goes to Project navigator panel.")); #ifdef KEXI_PROP_EDITOR d->action_view_propeditor = new KAction(i18n("Property Editor"), "", ALT + Key_2, this, SLOT(slotViewPropertyEditor()), actionCollection(), "view_propeditor"); d->action_view_propeditor->setToolTip(i18n("Go to Property editor panel")); d->action_view_propeditor->setWhatsThis(i18n("Goes to Property editor panel.")); #endif //DATA MENU d->action_data_save_row = createSharedAction(i18n("&Save Row"), "button_ok", SHIFT | Key_Return, "data_save_row"); d->action_data_save_row->setToolTip(i18n("Save currently selected table row's data")); d->action_data_save_row->setWhatsThis(i18n("Saves currently selected table row's data.")); setActionVolatile( d->action_data_save_row, true ); d->action_data_cancel_row_changes = createSharedAction(i18n("&Cancel Row Changes"), "button_cancel", 0 , "data_cancel_row_changes"); d->action_data_cancel_row_changes->setToolTip(i18n("Cancel changes made to currently selected table row")); d->action_data_cancel_row_changes->setWhatsThis(i18n("Cancels changes made to currently selected table row.")); setActionVolatile( d->action_data_cancel_row_changes, true ); action = createSharedAction(i18n("&Filter"), "filter", 0, "data_filter"); setActionVolatile( action, true ); // action->setToolTip(i18n("")); //todo // action->setWhatsThis(i18n("")); //todo // setSharedMenu("data_sort"); action = createSharedAction(i18n("&Ascending"), "sort_az", 0, "data_sort_az"); setActionVolatile( action, true ); action->setToolTip(i18n("Sort data in ascending order")); action->setWhatsThis(i18n("Sorts data in ascending order (from A to Z and from 0 to 9). Data from selected column is used for sorting.")); action = createSharedAction(i18n("&Descending"), "sort_za", 0, "data_sort_za"); setActionVolatile( action, true ); action->setToolTip(i18n("Sort data in descending order")); action->setWhatsThis(i18n("Sorts data in descending (from Z to A and from 9 to 0). Data from selected column is used for sorting.")); //SETTINGS MENU setStandardToolBarMenuEnabled( true ); action = KStdAction::keyBindings(this, SLOT( slotConfigureKeys() ), actionCollection() ); action->setWhatsThis(i18n("Lets you configure shortcut keys.")); #ifdef KEXI_SHOW_UNIMPLEMENTED action = KStdAction::configureToolbars( this, SLOT( slotConfigureToolbars() ), actionCollection() ); action->setWhatsThis(i18n("Lets you configure toolbars.")); #endif d->action_show_other = new KActionMenu(i18n("Other"), actionCollection(), "options_show_other"); #ifndef KEXI_NO_CTXT_HELP d->action_show_helper = new KToggleAction(i18n("Show Context Help"), "", CTRL + Key_H, actionCollection(), "options_show_contexthelp"); #if KDE_IS_VERSION(3,2,90) d->action_show_helper->setCheckedState(i18n("Hide Context Help")); #endif #endif #ifdef KEXI_FORMS_SUPPORT slotOptionsEnableForms(true, true); #else slotOptionsEnableForms(false, true); # if 0 KToggleAction *toggleaction = new KToggleAction(i18n("Enable Forms"), "", 0, actionCollection(), "options_enable_forms"); # if KDE_IS_VERSION(3,2,90) toggleaction->setCheckedState(i18n("Disable Forms")); # endif d->config->setGroup("Unfinished"); if (d->config->readBoolEntry("EnableForms", false)) { slotOptionsEnableForms( true, true ); toggleaction->setChecked(true); } connect(toggleaction, SIGNAL(toggled(bool)), this, SLOT(slotOptionsEnableForms(bool))); # endif //0 #endif #ifdef KEXI_SHOW_UNIMPLEMENTED d->action_configure = KStdAction::preferences(this, SLOT(slotShowSettings()), actionCollection()); action->setWhatsThis(i18n("Lets you configure Kexi.")); #endif //HELP MENU #if 0//js: todo reenable later KStdAction::tipOfDay( this, SLOT( slotTipOfTheDayAction() ), actionCollection() ) ->setWhatsThis(i18n("This shows useful tips on the use of this application.")); #endif #if 0 //we don't have a time for updating info text for each new version new KAction(i18n("Important Information"), "messagebox_info", 0, this, SLOT(slotImportantInfo()), actionCollection(), "help_show_important_info"); #endif //TODO: UNCOMMENT TO REMOVE MDI MODES SETTING m_pMdiModeMenu->hide(); // KAction *actionSettings = new KAction(i18n("Configure Kexi..."), "configure", 0, // actionCollection(), "kexi_settings"); // actionSettings->setWhatsThis(i18n("Lets you configure Kexi.")); // connect(actionSettings, SIGNAL(activated()), this, SLOT(slotShowSettings())); } void KexiMainWindowImpl::invalidateActions() { invalidateProjectWideActions(); invalidateSharedActions(); } void KexiMainWindowImpl::invalidateSharedActions(QWidget *w) { //TODO: enabling is more complex... /* d->action_edit_cut->setEnabled(true); d->action_edit_copy->setEnabled(true); d->action_edit_paste->setEnabled(true);*/ if (!w) w = focusWindow(); KexiSharedActionHost::invalidateSharedActions(w); } void KexiMainWindowImpl::invalidateSharedActions() { invalidateSharedActions(0); } // unused, I think void KexiMainWindowImpl::invalidateSharedActionsLater() { QTimer::singleShot(1, this, SLOT(invalidateSharedActions())); } void KexiMainWindowImpl::invalidateProjectWideActions() { // stateChanged("project_opened",d->prj ? StateNoReverse : StateReverse); if(d->final) return; const bool have_dialog = d->curDialog; const bool dialog_dirty = d->curDialog && d->curDialog->dirty(); //PROJECT MENU d->action_save->setEnabled(have_dialog && dialog_dirty); d->action_save_as->setEnabled(have_dialog); d->action_project_properties->setEnabled(d->prj); d->action_close->setEnabled(d->prj); d->action_project_relations->setEnabled(d->prj); //VIEW MENU d->action_view_nav->setEnabled(d->prj); d->action_view_propeditor->setEnabled(d->prj); d->action_view_data_mode->setEnabled( have_dialog && d->curDialog->supportsViewMode(Kexi::DataViewMode) ); if (!d->action_view_data_mode->isEnabled()) d->action_view_data_mode->setChecked(false); d->action_view_design_mode->setEnabled( have_dialog && d->curDialog->supportsViewMode(Kexi::DesignViewMode) ); if (!d->action_view_design_mode->isEnabled()) d->action_view_design_mode->setChecked(false); d->action_view_text_mode->setEnabled( have_dialog && d->curDialog->supportsViewMode(Kexi::TextViewMode) ); if (!d->action_view_text_mode->isEnabled()) d->action_view_text_mode->setChecked(false); #ifndef KEXI_NO_CTXT_HELP d->action_show_helper->setEnabled(d->prj); #endif //CREATE MENU if (d->createMenu) d->createMenu->setEnabled(d->prj); //DOCKS if (d->nav) d->nav->setEnabled(d->prj); if (d->propEditor) d->propEditor->setEnabled(d->prj); } void KexiMainWindowImpl::invalidateViewModeActions() { if (d->curDialog) { //update toggle action if (d->curDialog->currentViewMode()==Kexi::DataViewMode) d->action_view_data_mode->setChecked( true ); else if (d->curDialog->currentViewMode()==Kexi::DesignViewMode) d->action_view_design_mode->setChecked( true ); else if (d->curDialog->currentViewMode()==Kexi::TextViewMode) d->action_view_text_mode->setChecked( true ); } } tristate KexiMainWindowImpl::startup() { switch (Kexi::startupHandler().action()) { case KexiStartupHandler::CreateBlankProject: return createBlankProject(); case KexiStartupHandler::UseTemplate: return cancelled; //TODO break; case KexiStartupHandler::OpenProject: if (!openProject(Kexi::startupHandler().projectData())) { if (d->final) return false; } break; default:; } return true; } static QString internalReason(KexiDB::Object *obj) { const QString &s = obj->errorMsg(); if (s.isEmpty()) return s; return QString("
(%1) ").arg(i18n("reason:")+" "+s+""); } bool KexiMainWindowImpl::openProject(KexiProjectData *projectData) { if (!projectData) return false; if (d->final) { //TODO: maybe also auto allow to open objects... return initFinalMode(projectData); } createKexiProject( projectData ); // d->prj = new KexiProject( projectData ); // connect(d->prj, SIGNAL(error(const QString&,KexiDB::Object*)), this, SLOT(showErrorMessage(const QString&,KexiDB::Object*))); if (!d->prj->open()) { delete d->prj; d->prj = 0; return false; } initNavigator(); Kexi::recentProjects().addProjectData( projectData ); invalidateActions(); // d->disableErrorMessages = true; enableMessages( false ); QString not_found_msg; //ok, now open "autoopen: objects - for (QValueList::Iterator it = projectData->autoopenObjects.begin(); - it != projectData->autoopenObjects.end(); ++it ) + for (QValueList::ConstIterator it = projectData->autoopenObjects.constBegin(); + it != projectData->autoopenObjects.constEnd(); ++it ) { KexiProjectData::ObjectInfo info = *it; KexiPart::Info *i = Kexi::partManager().info( QCString("kexi/")+info["type"].lower().latin1() ); if (!i) { not_found_msg += "
  • "; if (!info["name"].isEmpty()) not_found_msg += (QString("\"") + info["name"] + "\" - "); if (info["action"]=="new") not_found_msg += i18n("cannot create object - "); not_found_msg += (i18n("unknown object type \"%1\"").arg(info["type"])+ internalReason(&Kexi::partManager())+"
  • "); continue; } if (info["action"]=="new") { if (!newObject( i )) { not_found_msg += "
  • "; not_found_msg += (i18n("cannot create object of type \"%1\"").arg(info["type"])+ internalReason(d->prj)+"
  • "); } continue; } KexiPart::Item *item = d->prj->item(i, info["name"]); if (!item) { not_found_msg += "
  • "; not_found_msg += ( QString("
  • \"")+ info["name"] + "\" - " + i18n("object not found")+ internalReason(d->prj)+"
  • " ); continue; } int viewMode; if (info["action"]=="design") viewMode = Kexi::DesignViewMode; else if (info["action"]=="edittext") viewMode = Kexi::TextViewMode; else viewMode = Kexi::DataViewMode; if (!openObject(item, viewMode)) { not_found_msg += "
  • "; not_found_msg += ( QString("
  • \"")+ info["name"] + "\" - " + i18n("cannot open object")+ internalReason(d->prj)+"
  • " ); continue; } } enableMessages( true ); // d->disableErrorMessages = false; if (!not_found_msg.isEmpty()) showErrorMessage(i18n("You have requested selected objects to be opened automatically on startup. Several objects cannot be opened."), QString("
      %1
    ").arg(not_found_msg) ); d->updatePropEditorVisibility(d->curDialog ? d->curDialog->currentViewMode() : 0); #ifndef PROPEDITOR_VISIBILITY_CHANGES d->propEditorToolWindow->hide(); #endif updateAppCaption(); // d->navToolWindow->wrapperWidget()->setFixedWidth(200); //js TODO: make visible FOR OTHER MODES if needed if (mdiMode()==KMdi::ChildframeMode) { //make docks visible again if (!d->navToolWindow->wrapperWidget()->isVisible()) static_cast(d->navToolWindow->wrapperWidget())->makeDockVisible(); // if (!d->propEditorToolWindow->wrapperWidget()->isVisible()) // static_cast(d->propEditorToolWindow->wrapperWidget())->makeDockVisible(); } return true; } tristate KexiMainWindowImpl::closeProject() { if (!d->prj) return true; //close each window, optionally asking if user wants to close (if data changed) while (!d->curDialog.isNull()) { tristate res = closeDialog( d->curDialog ); if (!res || ~res) return res; } if(d->nav) { d->nav->clear(); d->navToolWindow->hide(); } if(d->propEditorToolWindow) d->propEditorToolWindow->hide(); d->dialogs.clear(); //sanity! delete d->prj; d->prj=0; // Kexi::partManager().unloadAllParts(); invalidateActions(); if(!d->final) updateAppCaption(); return true; } void KexiMainWindowImpl::initContextHelp() { #ifndef KEXI_NO_CTXT_HELP d->ctxHelp=new KexiContextHelp(this,this); d->ctxHelp->setContextHelp(i18n("Welcome"),i18n("The KEXI team wishes you a lot of productive work, " "with this product.


    If you have found a bug or have a feature request, please don't " "hesitate to report it at our issue " "tracking system .


    If you would like to join our effort, the development documentation " "at www.kexi-project.org is a good starting point."),0); addToolWindow(d->ctxHelp,KDockWidget::DockBottom | KDockWidget::DockLeft,getMainDockWidget(),20); #endif } void KexiMainWindowImpl::initNavigator() { kdDebug() << "KexiMainWindowImpl::initNavigator()" << endl; if(!d->nav) { d->nav = new KexiBrowser(this); d->nav->installEventFilter(this); d->navToolWindow = addToolWindow(d->nav, KDockWidget::DockLeft, getMainDockWidget(), 20/*, lv, 35, "2"*/); connect(d->nav,SIGNAL(openItem(KexiPart::Item*,int)),this,SLOT(openObject(KexiPart::Item*,int))); connect(d->nav,SIGNAL(openOrActivateItem(KexiPart::Item*,int)), this,SLOT(openObjectFromNavigator(KexiPart::Item*,int))); connect(d->nav,SIGNAL(newItem( KexiPart::Info* )), this,SLOT(newObject(KexiPart::Info*))); connect(d->nav,SIGNAL(removeItem(KexiPart::Item*)), this,SLOT(removeObject(KexiPart::Item*))); connect(d->nav,SIGNAL(renameItem(KexiPart::Item*,const QString&, bool&)), this,SLOT(renameObject(KexiPart::Item*,const QString&, bool&))); if (d->prj) {//connect to the project connect(d->prj, SIGNAL(itemRemoved(const KexiPart::Item&)), d->nav, SLOT(slotRemoveItem(const KexiPart::Item&))); } } if(d->prj->isConnected()) { d->nav->clear(); KexiPart::PartInfoList *pl = Kexi::partManager().partInfoList(); for(KexiPart::Info *it = pl->first(); it; it = pl->next()) { kdDebug() << "KexiMainWindowImpl::initNavigator(): adding " << it->groupName() << endl; d->nav->addGroup(it); /* KexiPart::Part *p=Kexi::partManager().part(it); if (!p) { //TODO: js - OPTIONALLY: show error continue; } p->createGUIClient(this);*/ //load part - we need this to have GUI merged with part's actions //js: FUTURE TODO - don't do that when DESIGN MODE is OFF KexiPart::Part *p=Kexi::partManager().part(it); if (!p) { //TODO: js - OPTIONALLY: show error } //lookup project's objects (part items) //js: FUTURE TODO - don't do that when DESIGN MODE is OFF KexiPart::ItemDict *item_dict = d->prj->items(it); if (!item_dict) continue; for (KexiPart::ItemDictIterator item_it( *item_dict ); item_it.current(); ++item_it) { d->nav->addItem(item_it); } } } d->nav->setFocus(); invalidateActions(); } void KexiMainWindowImpl::slotLastActions() { #if defined(KEXI_PROP_EDITOR) && defined(KDOCKWIDGET_P) if (mdiMode()==KMdi::ChildframeMode) { KDockWidget *dw = (KDockWidget *)d->propEditor->parentWidget(); KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); //1 ds->resize(ds->width()*3, ds->height()); //1 ds->setSeparatorPos(30, true); //1 ds->setForcedFixedWidth( dw, 200 ); } #endif #ifdef Q_WS_WIN showMaximized();//js: workaround for not yet completed layout settings storage on win32 #endif } void KexiMainWindowImpl::initPropertyEditor() { #ifdef KEXI_PROP_EDITOR //TODO: FIX LAYOUT PROBLEMS d->propEditor = new KexiPropertyEditorView(this); d->propEditor->installEventFilter(this); d->propEditorToolWindow = addToolWindow(d->propEditor, KDockWidget::DockRight, getMainDockWidget(), 20); d->config->setGroup("PropertyEditor"); int size = d->config->readNumEntry("FontSize", -1); QFont f(d->propEditor->font()); if (size<0) { //this gives: // -2/3 of base font size (6 point minimum) // if the current screen width is > 1100, +1 point is added to every 100 points greater than 1300 // for resolutions below 1100 in width, 7 is the minimum // maximum size is the base size const int wdth = KGlobalSettings::desktopGeometry(this).width(); size = QMAX( 6 + QMAX(0, wdth - 1100) / 100 , f.pointSize()*2/3 ); if (wdth<1100) size = QMAX( size, 7 ); size = QMIN( size, f.pointSize() ); } f.setPointSize( size ); d->propEditor->setFont(f); if (mdiMode()==KMdi::ChildframeMode) { KDockWidget *dw = (KDockWidget *)d->propEditor->parentWidget(); #if defined(KDOCKWIDGET_P) KDockSplitter *ds = (KDockSplitter *)dw->parentWidget(); ds->show(); // ds->resize(400, ds->height()); // ds->setSeparatorPos(400, true); // ds->setForcedFixedWidth( dw, 400 ); // ds->setSeparatorPos(600, true); d->config->setGroup("MainWindow"); ds->setSeparatorPos(d->config->readNumEntry("RightDockPosition", 80/* % */), true); //1 ds->setForcedFixedWidth( dw, 600 ); // ds->resize(400, ds->height()); // dw->resize(400, dw->height()); #endif //1 dw->setMinimumWidth(200); // ds->setSeparatorPos(d->propEditor->sizeHint().width(), true); //heh, this is for IDEAl only, I suppose? if (m_rightContainer) { m_rightContainer->setForcedFixedWidth( 400 ); } } #endif int w = d->propEditor->width(); /* KMdiToolViewAccessor *tmp=createToolWindow(); tmp->setWidgetToWrap(d->propEditor); d->propEditor->show(); // I'm not sure, if this is a bug in kdockwidget, which I would better fix there tmp->show(KDockWidget::DockRight,getMainDockWidget(),20); */ } void KexiMainWindowImpl::slotPartLoaded(KexiPart::Part* p) { if (!p) return; connect(p, SIGNAL(newObjectRequest(KexiPart::Info*)), this, SLOT(newObject(KexiPart::Info*))); p->createGUIClients(this); } //! internal void KexiMainWindowImpl::slotCaptionForCurrentMDIChild(bool childrenMaximized) { //js todo: allow to set custom "static" app caption KMdiChildView *view = 0L; if (!d->curDialog) view = 0; else if (d->curDialog->isAttached()) { view = d->curDialog; } else { //current dialog isn't attached! - find top level child if (m_pMdi->topChild()) { view = m_pMdi->topChild()->m_pClient; childrenMaximized = view->mdiParent()->state()==KMdiChildFrm::Maximized; } else view = 0; } if (childrenMaximized && view) { setCaption( d->curDialog->caption() + (d->appCaptionPrefix.isEmpty() ? "" : " - " + d->appCaptionPrefix) ); } else { setCaption( (d->appCaptionPrefix.isEmpty() ? "" : d->appCaptionPrefix + " - ") + d->origAppCaption ); } } void KexiMainWindowImpl::updateAppCaption() { //js todo: allow to set custom "static" app caption d->appCaptionPrefix = ""; if (d->prj && d->prj->data()) {//add project name d->appCaptionPrefix = d->prj->data()->caption(); if (d->appCaptionPrefix.isEmpty()) d->appCaptionPrefix = d->prj->data()->databaseName(); } if (!d->appCaptionPrefix.isEmpty()) d->appCaptionPrefix = d->appCaptionPrefix; bool max = false; if (d->curDialog && d->curDialog->mdiParent()) max = d->curDialog->mdiParent()->state()==KMdiChildFrm::Maximized; slotCaptionForCurrentMDIChild(max); /* KMdiChildView *view; if (!d->curDialog) view = 0; else if (d->curDialog->isAttached()) { view = d->curDialog; } else { //current dialog isn't attached! - find top level child if (m_pMdi->topChild()) { view = m_pMdi->topChild()->m_pClient; } else view = 0; } kApp->setCaption( d->appCaption ); if (view && view->mdiParent()->state()==KMdiChildFrm::Maximized) { setCaption( view->caption() ); } else { setCaption( d->appCaption ); }*/ } void KexiMainWindowImpl::slotNoMaximizedChildFrmLeft(KMdiChildFrm*) { slotCaptionForCurrentMDIChild(false); } void KexiMainWindowImpl::slotLastChildFrmClosed() { slotCaptionForCurrentMDIChild(false); activeWindowChanged(0); } void KexiMainWindowImpl::slotChildViewIsDetachedNow(QWidget*) { slotCaptionForCurrentMDIChild(false); } /*void KexiMainWindowImpl::closeEvent(QCloseEvent *ev) { storeSettings(); bool cancelled = false; if (!closeProject(cancelled)) { //todo: error message return; } if (cancelled) { ev->ignore(); return; } ev->accept(); }*/ bool KexiMainWindowImpl::queryClose() { // storeSettings(); const tristate res = closeProject(); if (~res) return false; if (res) storeSettings(); return ! ~res; } bool KexiMainWindowImpl::queryExit() { // storeSettings(); return true; } void KexiMainWindowImpl::restoreSettings() { d->config->setGroup("MainWindow"); // Saved settings applyMainWindowSettings( d->config, "MainWindow" );//, instance()->instanceName() ); //small hack - set the default -- bottom // d->config->setGroup(QString(name()) + " KMdiTaskBar Toolbar style"); d->config->setGroup("MainWindow Toolbar KMdiTaskBar"); const bool tbe = d->config->readEntry("Position").isEmpty(); if (tbe || d->config->readEntry("Position")=="Bottom") { if (tbe) d->config->writeEntry("Position","Bottom"); moveDockWindow(m_pTaskBar, DockBottom); } d->config->setGroup("MainWindow"); int mdimode = d->config->readNumEntry("MDIMode", -1);//KMdi::TabPageMode); switch(mdimode) { case KMdi::ToplevelMode: switchToToplevelMode(); m_pTaskBar->switchOn(true); break; case KMdi::ChildframeMode: switchToChildframeMode(); m_pTaskBar->switchOn(true); break; case KMdi::IDEAlMode: switchToIDEAlMode(); break; case KMdi::TabPageMode: switchToTabPageMode(); break; default:;//-1 } // restore a possible maximized Childframe mode bool maxChildFrmMode = d->config->readBoolEntry("maximized childframes", true); setEnableMaximizedChildFrmMode(maxChildFrmMode); #if 0 if ( !initialGeometrySet() ) { // Default size // int restoredWidth, restoredHeight; int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->screenGeometry(scnum); //#if KDE_IS_VERSION(3,1,90) // restoredWidth = KGlobalSettings::screenGeometry(scnum).width(); // restoredHeight = KGlobalSettings::screenGeometry(scnum).height(); //#else // restoredWidth = QApplication::desktop()->width(); // restoredHeight = QApplication::desktop()->height(); //#endif /* if (restoredWidth > 1100) {// very big desktop ? restoredWidth = 1000; restoredHeight = 800; } if (restoredWidth > 850) {// big desktop ? restoredWidth = 800; restoredHeight = 600; } else {// small (800x600, 640x480) desktop restoredWidth = QMIN( restoredWidth, 600 ); restoredHeight = QMIN( restoredHeight, 400 ); }*/ config->setGroup("MainWindow"); QSize s ( config->readNumEntry( QString::fromLatin1("Width %1").arg(desk.width()), 700 ), config->readNumEntry( QString::fromLatin1("Height %1").arg(desk.height()), 480 ) ); resize (kMin (s.width(), desk.width()), kMin(s.height(), desk.height())); } #endif } void KexiMainWindowImpl::storeSettings() { kdDebug() << "KexiMainWindowImpl::storeSettings()" << endl; // saveWindowSize( d->config ); //instance()->config() ); saveMainWindowSettings( d->config, "MainWindow" ); d->config->writeEntry("MDIMode", mdiMode()); // config->sync(); d->config->writeEntry("maximized childframes", isInMaximizedChildFrmMode()); if (mdiMode()==KMdi::ChildframeMode) { if (d->propEditorDockSeparatorPos > 0 && d->propEditorDockSeparatorPos <= 100) { d->config->setGroup("MainWindow"); d->config->writeEntry("RightDockPosition", d->propEditorDockSeparatorPos); } } d->config->setGroup("PropertyEditor"); d->config->writeEntry("FontSize", d->propEditor->font().pointSize()); } void KexiMainWindowImpl::restoreWindowConfiguration(KConfig *config) { kdDebug()<<"preparing session restoring"<setGroup("MainWindow"); QString dockGrp; if (kapp->isRestored()) dockGrp=config->group()+"-Docking"; else dockGrp="MainWindow0-Docking"; if (config->hasGroup(dockGrp)) readDockConfig(config,dockGrp); } void KexiMainWindowImpl::storeWindowConfiguration(KConfig *config) { kdDebug()<<"preparing session saving"<setGroup("MainWindow"); QString dockGrp; #if KDE_IS_VERSION(3,1,9) && !defined(Q_WS_WIN) if (kapp->sessionSaving()) dockGrp=config->group()+"-Docking"; else #endif dockGrp="MainWindow0-Docking"; kdDebug()<<"Before write dock config"<saveDocumentList (config); // m_projectManager->saveProjectList (config); } void KexiMainWindowImpl::saveGlobalProperties( KConfig* sessionConfig ) { storeWindowConfiguration(sessionConfig); } void KexiMainWindowImpl::registerChild(KexiDialogBase *dlg) { kdDebug() << "KexiMainWindowImpl::registerChild()" << endl; connect(dlg, SIGNAL(activated(KMdiChildView *)), this, SLOT(activeWindowChanged(KMdiChildView *))); connect(dlg, SIGNAL(dirtyChanged(KexiDialogBase*)), this, SLOT(slotDirtyFlagChanged(KexiDialogBase*))); // connect(dlg, SIGNAL(childWindowCloseRequest(KMdiChildView *)), this, SLOT(childClosed(KMdiChildView *))); if(dlg->id() != -1) d->dialogs.insert(dlg->id(), dlg); kdDebug() << "KexiMainWindowImpl::registerChild() ID = " << dlg->id() << endl; if (m_mdiMode==KMdi::ToplevelMode || m_mdiMode==KMdi::ChildframeMode) {//kmdi fix //js TODO: check if taskbar is switched in menu if (m_pTaskBar && !m_pTaskBar->isSwitchedOn()) m_pTaskBar->switchOn(true); } //KMdiChildFrm *frm = dlg->mdiParent(); //if (frm) { // dlg->setMargin(20); //dlg->setLineWidth(20); //} } void KexiMainWindowImpl::updateDialogViewGUIClient(KXMLGUIClient *viewClient) { if (viewClient!=d->curDialogViewGUIClient) { //view clients differ kdDebug()<<"KexiMainWindowImpl::activeWindowChanged(): old view gui client:" <<(d->curDialogViewGUIClient ? d->curDialogViewGUIClient->xmlFile() : "") <<" new view gui client: "<<( viewClient ? viewClient->xmlFile() : "") <curDialogViewGUIClient) { guiFactory()->removeClient(d->curDialogViewGUIClient); } if (viewClient) { if (d->closedDialogViewGUIClient) { //ooh, there is a client which dialog is already closed -- BUT it is the same client as our //so: give up } else { guiFactory()->addClient(viewClient); } } } } void KexiMainWindowImpl::activeWindowChanged(KMdiChildView *v) { KexiDialogBase *dlg = static_cast(v); kdDebug() << "KexiMainWindowImpl::activeWindowChanged() to = " << (dlg ? dlg->caption() : "") << endl; KXMLGUIClient *client=0; //common for all views KXMLGUIClient *viewClient=0; //specific for current dialog's view if (!dlg) client=0; else if ( dlg->isRegistered()) { // client=dlg->guiClient(); client=dlg->commonGUIClient(); viewClient=dlg->guiClient(); if (d->closedDialogGUIClient) { if (client!=d->closedDialogGUIClient) { //ooh, there is a client which dialog is already closed -- and we don't want it guiFactory()->removeClient(d->closedDialogGUIClient); d->closedDialogGUIClient=0; } } if (d->closedDialogViewGUIClient) { if (viewClient!=d->closedDialogViewGUIClient) { //ooh, there is a client which dialog is already closed -- and we don't want it guiFactory()->removeClient(d->closedDialogViewGUIClient); d->closedDialogViewGUIClient=0; } } if (client!=d->curDialogGUIClient) { //clients differ kdDebug()<<"KexiMainWindowImpl::activeWindowChanged(): old gui client:" <<(d->curDialogGUIClient ? d->curDialogGUIClient->xmlFile() : "") <<" new gui client: "<<( client ? client->xmlFile() : "") <curDialogGUIClient) { guiFactory()->removeClient(d->curDialogGUIClient); d->curDialog->detachFromGUIClient(); } if (client) { if (d->closedDialogGUIClient) { //ooh, there is a client which dialog is already closed -- BUT it is the same client as our //so: give up } else { guiFactory()->addClient(client); } dlg->attachToGUIClient(); } } else { //clients are the same if ((KexiDialogBase*)d->curDialog!=dlg) { if (d->curDialog) d->curDialog->detachFromGUIClient(); if (dlg) dlg->attachToGUIClient(); } } updateDialogViewGUIClient(viewClient); /* if (viewClient!=d->curDialogViewGUIClient) { //view clients differ kdDebug()<<"KexiMainWindowImpl::activeWindowChanged(): old view gui client:" <curDialogViewGUIClient<<" new view gui client: "<curDialogViewGUIClient) { guiFactory()->removeClient(d->curDialogViewGUIClient); } if (viewClient) { if (d->closedDialogViewGUIClient) { //ooh, there is a client which dialog is already closed -- BUT it is the same client as our //so: give up } else { guiFactory()->addClient(viewClient); } } }*/ } bool update_dlg_caption = dlg && dlg!=(KexiDialogBase*)d->curDialog && dlg->mdiParent(); if (d->curDialogGUIClient && !client) guiFactory()->removeClient(d->curDialogGUIClient); d->curDialogGUIClient=client; if (d->curDialogViewGUIClient && !viewClient) guiFactory()->removeClient(d->curDialogViewGUIClient); d->curDialogViewGUIClient=viewClient; bool dialogChanged = ((KexiDialogBase*)d->curDialog)!=dlg; if (dialogChanged) { if (d->curDialog) { //inform previously activated dialog about deactivation d->curDialog->deactivate(); } } d->curDialog=dlg; propertyBufferSwitched(d->curDialog); if (dialogChanged) { // invalidateSharedActions(); //update property editor's contents... // if ((KexiPropertyBuffer*)d->propBuffer!=d->curDialog->propertyBuffer()) { // propertyBufferSwitched();//d->curDialog); // d->propBuffer = d->curDialog->propertyBuffer(); // d->propEditor->editor()->setBuffer( d->propBuffer ); // } if (d->curDialog->currentViewMode()!=0) //on opening new dialog it can be 0; we don't want this d->updatePropEditorVisibility(d->curDialog->currentViewMode()); } //update caption... if (update_dlg_caption) { slotCaptionForCurrentMDIChild(d->curDialog->mdiParent()->state()==KMdiChildFrm::Maximized); } // if (!d->curDialog.isNull()) // d->last_checked_mode = d->actions_for_view_modes[ d->curDialog->currentViewMode() ]; invalidateViewModeActions(); invalidateActions(); if (dlg) dlg->setFocus(); } bool KexiMainWindowImpl::activateWindow(int id) { kdDebug() << "KexiMainWindowImpl::activateWindow()" << endl; return activateWindow( d->dialogs[id] ); } bool KexiMainWindowImpl::activateWindow(KexiDialogBase *dlg) { kdDebug() << "KexiMainWindowImpl::activateWindow(KexiDialogBase *)" << endl; if(!dlg) return false; d->focus_before_popup = dlg; dlg->activate(); return true; } void KexiMainWindowImpl::childClosed(KMdiChildView *v) { KexiDialogBase *dlg = static_cast(v); d->dialogs.remove(dlg->id()); //focus navigator if nothing else available if (d->dialogs.isEmpty()) d->nav->setFocus(); } void KexiMainWindowImpl::slotShowSettings() { KEXI_UNFINISHED(d->action_configure->text()); //TODO KexiSettings s(this); // s.exec(); } void KexiMainWindowImpl::slotConfigureKeys() { /* KKeyDialog dlg; dlg.insert( actionCollection() ); dlg.configure();*/ KKeyDialog::configure( actionCollection() ); } void KexiMainWindowImpl::slotConfigureToolbars() { KEditToolbar edit(factory()); // connect(&edit,SIGNAL(newToolbarConfig()),this,SLOT(slotNewToolbarConfig())); (void) edit.exec(); } void KexiMainWindowImpl::slotProjectNew() { if (d->prj) { //TODO use KexiStartupDialog(KexiStartupDialog::Templates...) bool cancel; KexiProjectData *new_data = createBlankProjectData( cancel, false /* do not confirm prj overwrites: user will be asked on process startup */ ); if (!new_data) return; //start new instance //TODO use KProcess? QStringList args; QProcess *proc = 0; if (!new_data->connectionData()->fileName().isEmpty()) { //file based args << qApp->applicationFilePath() << "-create-opendb" << new_data->connectionData()->fileName(); proc = new QProcess(args, this, "process"); proc->setWorkingDirectory( QFileInfo(new_data->connectionData()->fileName()).dir(true) ); } else { //server based //TODO return; } if (!proc->start()) { d->showStartProcessMsg(args); } delete proc; delete new_data; // KEXI_UNFINISHED(i18n("Create another project")); return; } //create within this instance createBlankProject(); } void KexiMainWindowImpl::createKexiProject(KexiProjectData* new_data) { d->prj = new KexiProject( new_data, this ); // d->prj = ::createKexiProject(new_data); //provided by KexiMessageHandler connect(d->prj, SIGNAL(error(const QString&,KexiDB::Object*)), this, SLOT(showErrorMessage(const QString&,KexiDB::Object*))); //provided by KexiMessageHandler connect(d->prj, SIGNAL(error(const QString&,const QString&)), this, SLOT(showErrorMessage(const QString&,const QString&))); connect(d->prj, SIGNAL(itemRenamed(const KexiPart::Item&)), this, SLOT(slotObjectRenamed(const KexiPart::Item&))); if (d->nav) connect(d->prj, SIGNAL(itemRemoved(const KexiPart::Item&)), d->nav, SLOT(slotRemoveItem(const KexiPart::Item&))); } KexiProjectData* KexiMainWindowImpl::createBlankProjectData(bool &cancelled, bool confirmOverwrites) { cancelled = false; KexiNewProjectWizard wiz(Kexi::connset(), 0, "KexiNewProjectWizard", true); wiz.setConfirmOverwrites(confirmOverwrites); if (wiz.exec() != QDialog::Accepted) { cancelled=true; return 0; } KexiProjectData *new_data; if (wiz.projectConnectionData()) { //server-based project KexiDB::ConnectionData *cdata = wiz.projectConnectionData(); kdDebug() << "DBNAME: " << wiz.projectDBName() << " SERVER: " << cdata->serverInfoString() << endl; new_data = new KexiProjectData( *cdata, wiz.projectDBName(), wiz.projectCaption() ); } else if (!wiz.projectDBName().isEmpty()) { //file-based project KexiDB::ConnectionData cdata; cdata.connName = wiz.projectCaption(); cdata.driverName = KexiDB::Driver::defaultFileBasedDriverName(); cdata.setFileName( wiz.projectDBName() ); new_data = new KexiProjectData( cdata, wiz.projectDBName(), wiz.projectCaption() ); } else { cancelled = true; return 0; } return new_data; } tristate KexiMainWindowImpl::createBlankProject() { bool cancel; KexiProjectData *new_data = createBlankProjectData(cancel); if (cancel) return cancelled; if (!new_data) return false; createKexiProject( new_data ); bool ok = d->prj->create(true /*overwrite*/ ); if (!ok) { delete d->prj; d->prj = 0; return false; } kdDebug() << "KexiMainWindowImpl::slotProjectNew(): new project created --- " << endl; initNavigator(); Kexi::recentProjects().addProjectData( new_data ); invalidateActions(); updateAppCaption(); return true; } /* moved to kexiproject tristate KexiMainWindowImpl::createBlankProject() { KexiProjectData *new_data = Kexi::startupHandler().projectData(); // true, if project data was provided from command line const bool dataAlreadyProvided = new_data; if (!new_data) {// project not provided, ask using the wizard KexiNewProjectWizard wiz(Kexi::connset(), 0, "KexiNewProjectWizard", true); if (wiz.exec() != QDialog::Accepted) return cancelled; if (wiz.projectConnectionData()) { //server-based project KexiDB::ConnectionData *cdata = wiz.projectConnectionData(); kdDebug() << "DBNAME: " << wiz.projectDBName() << " SERVER: " << cdata->serverInfoString() << endl; new_data = new KexiProjectData( *cdata, wiz.projectDBName(), wiz.projectCaption() ); } else if (!wiz.projectDBName().isEmpty()) { //file-based project KexiDB::ConnectionData cdata; cdata.connName = wiz.projectCaption(); cdata.driverName = "sqlite"; cdata.setFileName( wiz.projectDBName() ); new_data = new KexiProjectData( cdata, wiz.projectDBName(), wiz.projectCaption() ); } else return cancelled; } createKexiProject( new_data ); //todo: move this method outside keximainwindowimpl, so the window wont be visible if not needed bool ok = true; if (dataAlreadyProvided) { tristate res = d->prj->create(false); if (~res) { if (KMessageBox::Yes != KMessageBox::warningYesNo(qApp->desktop(), i18n( "The project \"%1\" already exists.\n" "Do you want to replace it with a new, blank one?") .arg(new_data->name()))) ///connectionData().dbFileName()))) //todo add serverInfoString() for server-based prj { return cancelled; } } ok = res; } if (ok) { tristate res = d->prj->create(true); ok = res; } if (!ok) { delete d->prj; d->prj = 0; return false; } kdDebug() << "KexiMainWindowImpl::slotProjectNew(): new project created --- " << endl; initNavigator(); Kexi::recentProjects().addProjectData( new_data ); invalidateActions(); updateAppCaption(); return true; }*/ void KexiMainWindowImpl::slotProjectOpen() { KexiStartupDialog dlg( KexiStartupDialog::OpenExisting, 0, Kexi::connset(), Kexi::recentProjects(), this, "KexiOpenDialog"); if (dlg.exec()!=QDialog::Accepted) return; if (d->prj) {//js: TODO: start new instance! QProcess *proc; QStringList args; if (!dlg.selectedExistingFile().isEmpty()) { //TODO use KRun args << qApp->applicationFilePath() << dlg.selectedExistingFile(); proc = new QProcess(args, this, "process"); proc->setWorkingDirectory( QFileInfo(dlg.selectedExistingFile()).dir(true) ); } //TODO: server-based if (!proc->start()) { d->showStartProcessMsg(args); } delete proc; return; } KexiProjectData* projectData = 0; KexiDB::ConnectionData *cdata = dlg.selectedExistingConnection(); if (cdata) { projectData = Kexi::startupHandler().selectProject( cdata, this ); if (!projectData && Kexi::startupHandler().error()) { showErrorMessage(&Kexi::startupHandler()); } } else { QString selFile = dlg.selectedExistingFile(); if (!selFile.isEmpty()) { //file-based project kdDebug() << "Project File: " << selFile << endl; KexiDB::ConnectionData cdata; cdata.setFileName( selFile ); cdata.driverName = KexiStartupHandler::detectDriverForFile( cdata.driverName, selFile, this ); if (cdata.driverName.isEmpty()) return; projectData = new KexiProjectData(cdata, selFile); } } if (!projectData) return; openProject(projectData); } void KexiMainWindowImpl::slotProjectOpenRecentAboutToShow() { //setup KPopupMenu *popup = d->action_open_recent->popupMenu(); const int cnt = popup->count(); //remove older for (int i = 0; iidAt(0); if (id==d->action_open_recent_more_id) break; if (id>=0) { popup->removeItem(id); } } //insert current items int cur_id = 0, cur_idx = 0; //TODO: cur_id = popup->insertItem("My example project 1", ++cur_id, cur_idx++); cur_id = popup->insertItem("My example project 2", ++cur_id, cur_idx++); cur_id = popup->insertItem("My example project 3", ++cur_id, cur_idx++); } void KexiMainWindowImpl::slotProjectOpenRecent(int id) { if (id<0 || id==d->action_open_recent_more_id) return; kdDebug() << "KexiMainWindowImpl::slotProjectOpenRecent("<curDialog) return; saveObject( d->curDialog ); updateAppCaption(); invalidateActions(); } void KexiMainWindowImpl::slotProjectSaveAs() { KEXI_UNFINISHED(i18n("Save object as")); } void KexiMainWindowImpl::slotProjectProperties() { //TODO: load the implementation not the ui :) ProjectSettingsUI u(this); u.exec(); } void KexiMainWindowImpl::slotProjectClose() { closeProject(); } void KexiMainWindowImpl::slotProjectRelations() { if (!d->prj) return; KexiDialogBase *d = KexiInternalPart::createDialogInstance("relation", this); activateWindow(d); /* KexiRelationPart *p = relationPart(); if(!p) return; p->createWindow(this);*/ } void KexiMainWindowImpl::slotImportFile() { KEXI_UNFINISHED("Import: " + i18n("From File...")); } void KexiMainWindowImpl::slotImportServer() { KEXI_UNFINISHED("Import: " + i18n("From Server...")); } void KexiMainWindowImpl::slotQuit() { if (~ closeProject()) return; close(); } void KexiMainWindowImpl::slotViewNavigator() { if (!d->nav || !d->navToolWindow) return; if (!d->nav->isVisible()) makeWidgetDockVisible(d->nav); // makeDockVisible(dynamic_cast(d->navToolWindow->wrapperWidget())); // d->navToolWindow->wrapperWidget()->show(); // d->navToolWindow->show(KDockWidget::DockLeft, getMainDockWidget()); d->navToolWindow->wrapperWidget()->raise(); // d->block_KMdiMainFrm_eventFilter=true; d->nav->setFocus(); d->block_KMdiMainFrm_eventFilter=false; } void KexiMainWindowImpl::slotViewPropertyEditor() { if (!d->propEditor || !d->propEditorToolWindow) return; if (!d->propEditor->isVisible()) makeWidgetDockVisible(d->propEditor); d->propEditorToolWindow->wrapperWidget()->raise(); d->block_KMdiMainFrm_eventFilter=true; d->propEditor->setFocus(); d->block_KMdiMainFrm_eventFilter=false; } bool KexiMainWindowImpl::switchToViewMode(int viewMode) { if (!d->curDialog) { d->toggleLastCheckedMode(); return false; } if (!d->curDialog->supportsViewMode( viewMode )) { showErrorMessage(i18n("Selected view mode is not supported for \"%1\" object.") .arg(d->curDialog->partItem()->name()), i18n("Selected view mode (%1) is not supported by this object type (%2)") .arg(Kexi::nameForViewMode(viewMode)) .arg(d->curDialog->part()->instanceName()) ); d->toggleLastCheckedMode(); return false; } // bool cancelled; tristate res = d->curDialog->switchToViewMode( viewMode ); if (!res) { showErrorMessage(i18n("Switching to other view failed (%1).").arg(Kexi::nameForViewMode(viewMode)), d->curDialog); d->toggleLastCheckedMode(); return false; } if (~res) { d->toggleLastCheckedMode(); return false; } //view changed: switch to this view's gui client KXMLGUIClient *viewClient=d->curDialog->guiClient(); updateDialogViewGUIClient(viewClient); if (d->curDialogViewGUIClient && !viewClient) guiFactory()->removeClient(d->curDialogViewGUIClient); d->curDialogViewGUIClient=viewClient; //remember d->updatePropEditorVisibility(viewMode); invalidateSharedActions(); return true; } void KexiMainWindowImpl::slotViewDataMode() { switchToViewMode(Kexi::DataViewMode); } void KexiMainWindowImpl::slotViewDesignMode() { switchToViewMode(Kexi::DesignViewMode); } void KexiMainWindowImpl::slotViewTextMode() { switchToViewMode(Kexi::TextViewMode); } /* void KexiMainWindowImpl::showSorryMessage(const QString &title, const QString &details) { showMessage(KMessageBox::Sorry, title, details); } void KexiMainWindowImpl::showErrorMessage(const QString &title, const QString &details) { showMessage(KMessageBox::Error, title, details); } void KexiMainWindowImpl::showMessage(KMessageBox::DialogType dlgType, const QString &title, const QString &details) { if (d->disableErrorMessages) return; QString msg = title; if (title.isEmpty()) msg = i18n("Unknown error"); msg = "

    "+msg+"

    "; if (!details.isEmpty()) { switch (dlgType) { case KMessageBox::Error: KMessageBox::detailedError(this, msg, details); break; default: KMessageBox::detailedSorry(this, msg, details); } } else { KMessageBox::messageBox(this, dlgType, msg); } } void KexiMainWindowImpl::showErrorMessage(const QString &msg, KexiDB::Object *obj) { QString _msg = msg; if (!obj) { showErrorMessage(_msg); return; } QString details; KexiDB::getHTMLErrorMesage(obj, _msg, details); showErrorMessage(_msg, details); } void KexiMainWindowImpl::showErrorMessage(const QString &msg, const QString &details, KexiDB::Object *obj) { QString _msg = msg; if (!obj) { showErrorMessage(_msg, details); return; } QString _details; KexiDB::getHTMLErrorMesage(obj, _msg, _details); showErrorMessage(_msg, _details); } void KexiMainWindowImpl::showErrorMessage(Kexi::ObjectStatus *status) { showErrorMessage("", status); } void KexiMainWindowImpl::showErrorMessage(const QString &message, Kexi::ObjectStatus *status) { if (status && status->error()) { QString msg = message; if (msg.isEmpty()) { msg = status->message; status->message = status->description; status->description = ""; } QString desc; if (!status->message.isEmpty()) { if (status->description.isEmpty()) { desc = status->message; } else { msg += (QString("

    ") + status->message); desc = status->description; } } showErrorMessage(message, desc, status->dbObject()); } else { showErrorMessage(message); } status->clearStatus(); } */ void KexiMainWindowImpl::closeWindow(KMdiChildView *pWnd, bool layoutTaskBar) { closeDialog(static_cast(pWnd), layoutTaskBar); } tristate KexiMainWindowImpl::saveObject( KexiDialogBase *dlg, const QString& messageWhenAskingForName ) { if (!dlg->neverSaved()) { //data was saved in the past -just save again const tristate res = dlg->storeData(); if (!res) showErrorMessage(i18n("Saving \"%1\" object failed.").arg(dlg->partItem()->name()), d->curDialog); return res; } //data was never saved in the past -we need to create a new object at the backend if (!d->nameDialog) { d->nameDialog = new KexiNameDialog( messageWhenAskingForName, this, "nameDialog"); //check if that name is allowed d->nameDialog->widget()->addNameSubvalidator( new Kexi::KexiDBObjectNameValidator(project()->dbConnection()->driver(), 0, "sub")); } else { d->nameDialog->widget()->setMessageText( messageWhenAskingForName ); } d->nameDialog->widget()->setCaptionText(dlg->partItem()->caption()); d->nameDialog->widget()->setNameText(dlg->partItem()->name()); d->nameDialog->setCaption(i18n("Save Object As")); d->nameDialog->setDialogIcon( DesktopIcon( dlg->itemIcon(), KIcon::SizeMedium ) ); bool found; do { if (d->nameDialog->exec()!=QDialog::Accepted) return cancelled; //check if that name already exists KexiDB::SchemaData tmp_sdata; found = project()->dbConnection()->loadObjectSchemaData( dlg->part()->info()->projectPartID(), d->nameDialog->widget()->nameText(), tmp_sdata ); if (found) { KMessageBox::information(this, i18n("%1 \"%2\" already exists.\nPlease choose other name.") .arg(dlg->part()->instanceName()).arg(d->nameDialog->widget()->nameText())); continue; } } while (found); const int oldItemID = dlg->partItem()->identifier(); //update name and caption dlg->partItem()->setName( d->nameDialog->widget()->nameText() ); dlg->partItem()->setCaption( d->nameDialog->widget()->captionText() ); const tristate res = dlg->storeNewData(); if (~res) return cancelled; if (!res) { showErrorMessage(i18n("Saving new \"%1\" object failed.").arg(dlg->partItem()->name()), d->curDialog); return false; } //update navigator d->nav->addItem(dlg->partItem()); //item id changed to final one: update association in dialogs' dictionary d->dialogs.take(oldItemID); d->dialogs.insert(dlg->partItem()->identifier(), dlg); return true; } tristate KexiMainWindowImpl::closeDialog(KexiDialogBase *dlg, bool layoutTaskBar) { if (!dlg) return true; if (d->insideCloseDialog) return true; d->insideCloseDialog = true; /*this crashes but is nice: QWidget *www = guiFactory()->container("query", dlg->commonGUIClient()); delete www;*/ bool remove_on_closing = dlg->partItem() ? dlg->partItem()->neverSaved() : false; if (dlg->dirty() && !d->forceDialogClosing) { //dialog's data is dirty: const int quertionRes = KMessageBox::questionYesNoCancel( this, i18n( "

    The object has been modified: %1 \"%2\".

    Do you want to save it?

    " ) .arg(dlg->part()->instanceName()).arg(dlg->partItem()->name()), QString::null, KStdGuiItem::save(), KStdGuiItem::discard()); if (quertionRes==KMessageBox::Cancel) { d->insideCloseDialog = false; return cancelled; } if (quertionRes==KMessageBox::Yes) { //save it // if (!dlg->storeData()) tristate res = saveObject( dlg ); if (!res || ~res) { //js:TODO show error info; (retry/ignore/cancel) d->insideCloseDialog = false; return res; } remove_on_closing = false; } } const int dlg_id = dlg->id(); //remember now, because removeObject() can destruct partitem object if (remove_on_closing) { //we won't save this object, and it was never saved -remove it if (!removeObject( dlg->partItem(), true )) { //msg? //TODO: ask if we'd continue and return true/false d->insideCloseDialog = false; return false; } } else { //not dirty now if(d->nav) d->nav->updateItemName( dlg->partItem(), false ); } d->dialogs.take(dlg_id); //don't remove -KMDI will do that KXMLGUIClient *client = dlg->commonGUIClient(); KXMLGUIClient *viewClient = dlg->guiClient(); if (d->curDialogGUIClient==client) { d->curDialogGUIClient=0; } if (d->curDialogViewGUIClient==viewClient) { d->curDialogViewGUIClient=0; } if (client) { //sanity: ouch, it is not removed yet? - do it now if (d->closedDialogGUIClient && d->closedDialogGUIClient!=client) guiFactory()->removeClient(d->closedDialogGUIClient); if (d->dialogs.isEmpty()) {//now there is no dialogs - remove client RIGHT NOW! d->closedDialogGUIClient=0; guiFactory()->removeClient(client); } else { //remember this - and MAYBE remove later, if needed d->closedDialogGUIClient=client; } } if (viewClient) { //sanity: ouch, it is not removed yet? - do it now if (d->closedDialogViewGUIClient && d->closedDialogViewGUIClient!=viewClient) guiFactory()->removeClient(d->closedDialogViewGUIClient); if (d->dialogs.isEmpty()) {//now there is no dialogs - remove client RIGHT NOW! d->closedDialogViewGUIClient=0; guiFactory()->removeClient(viewClient); } else { //remember this - and MAYBE remove later, if needed d->closedDialogViewGUIClient=viewClient; } } KMdiMainFrm::closeWindow(dlg, layoutTaskBar); //focus navigator if nothing else available if (d->dialogs.isEmpty()) { if (d->nav) d->nav->setFocus(); d->updatePropEditorVisibility(0); } invalidateActions(); d->insideCloseDialog = false; return true; } /* KexiRelationPart * KexiMainWindowImpl::relationPart() { if(d->relationPart) return d->relationPart; d->relationPart = KParts::ComponentFactory::createInstanceFromLibrary("kexihandler_relation", this, "prel"); return d->relationPart; }*/ void KexiMainWindowImpl::detachWindow(KMdiChildView *pWnd,bool bShow) { KMdiMainFrm::detachWindow(pWnd,bShow); // update icon - from small to large pWnd->setIcon( DesktopIcon( static_cast(pWnd)->itemIcon() ) ); // pWnd->setIcon( DesktopIcon( static_cast(pWnd)->part()->info()->itemIcon() ) ); } void KexiMainWindowImpl::attachWindow(KMdiChildView *pWnd, bool /*bShow*/, bool bAutomaticResize) { // if (bAutomaticResize || w->size().isEmpty() || (w->size() == QSize(1,1))) { KMdiMainFrm::attachWindow(pWnd,true,bAutomaticResize); //for dialogs in normal state: decrease dialog's height if it exceeds area contents if (pWnd->mdiParent()->state() == KMdiChildFrm::Normal && pWnd->geometry().bottom() > pWnd->mdiParent()->mdiAreaContentsRect().bottom()) { QRect r = pWnd->geometry(); r.setBottom( pWnd->mdiParent()->mdiAreaContentsRect().bottom() - 5 ); pWnd->setGeometry( r ); } // update icon - from large to small pWnd->mdiParent()->setIcon( SmallIcon( static_cast(pWnd)->itemIcon() ) ); } QWidget* KexiMainWindowImpl::findWindow(QWidget *w) { while (w && !acceptsSharedActions(w)) w = w->parentWidget(); return w; } bool KexiMainWindowImpl::acceptsSharedActions(QObject *w) { return w->inherits("KexiDialogBase") || w->inherits("KexiViewBase"); } bool KexiMainWindowImpl::eventFilter( QObject *obj, QEvent * e ) { //KexiVDebug << "eventFilter: " <type() << " " <name()<type()==QEvent::KeyPress) { KexiVDebug << "KEY EVENT " << QString::number(static_cast(e)->key(), 16) << endl; KexiVDebug << endl; } if (e->type()==QEvent::AccelOverride) { KexiVDebug << "AccelOverride EVENT" << endl; } if (e->type()==QEvent::Close) { KexiVDebug << "Close EVENT" << endl; } if (e->type()==QEvent::Resize) { KexiVDebug << "Resize EVENT" << endl; } if (e->type()==QEvent::ShowMaximized) { KexiVDebug << "ShowMaximized EVENT" << endl; } if (obj==d->propEditor) { if (e->type()==QEvent::Resize) { d->updatePropEditorDockWidthInfo(); } } QWidget *focus_w = 0; if (obj->inherits("QPopupMenu")) { /* Fixes for popup menus behaviour: For hiding/showing: focus previously (d->focus_before_popup) focused window, if known, otherwise focus currently focused one. And: just invalidate actions. */ if (e->type()==QEvent::Hide || e->type()==QEvent::Show) { KexiVDebug << e->type() << endl; focus_w = focusWindow(); if (!d->focus_before_popup.isNull()) { d->focus_before_popup->setFocus(); d->focus_before_popup=0; invalidateSharedActions(); } else { if (focus_w) { focus_w->setFocus(); invalidateSharedActions(); } } } return false; } /*! On mouse click on the findow, make sure it's focused and actions are invalidated */ if (e->type()==QEvent::MouseButtonPress) { QWidget *w = findWindow(static_cast(obj)); KexiVDebug << "MouseButtonPress EVENT " << (w ? w->name() : 0) << endl; if (w) { w->setFocus(); invalidateSharedActions(d->curDialog); } } QWidget *w = findWindow(static_cast(obj)); if (e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut) { focus_w = focusWindow(); KexiVDebug << "Focus EVENT" << endl; KexiVDebug << (focus_w ? focus_w->name() : "" ) << endl; KexiVDebug << "eventFilter: " <type() << " " <name() <type()==QEvent::WindowActivate) { KexiVDebug << "WindowActivate EVENT" << endl; KexiVDebug << "eventFilter: " <type() << " " <name()<type()==QEvent::FocusIn) { if (focus_w) { // if (d->actionProxies[ w ]) // if (d->actionProxies[ focus_w ]) { if (actionProxyFor( focus_w )) { // invalidateSharedActions(); } else { /* QObject* o = focusWidget(); while (o && !o->inherits("KexiDialogBase") && !o->inherits("KexiDockBase")) o = o->parent();*/ //js invalidateSharedActions(focus_w); } } // /*|| e->type()==QEvent::FocusOut*/) && /*(!obj->inherits("KexiDialogBase")) &&*/ d->actionProxies[ obj ]) { // invalidateSharedActions(); } if (e->type()==QEvent::FocusOut && focus_w && focus_w==d->curDialog && actionProxyFor( obj )) { invalidateSharedActions(d->curDialog); } #endif if (!d->focus_before_popup.isNull() && e->type()==QEvent::FocusOut && obj->inherits("KMenuBar")) { //d->nav->setFocus(); d->focus_before_popup->setFocus(); d->focus_before_popup=0; invalidateSharedActions(d->curDialog); return true; } //remember currently focued window invalidate act. if (e->type()==QEvent::FocusOut) { if (static_cast(e)->reason()==QFocusEvent::Popup) { if (Kexi::hasParent(d->curDialog, focus_w)) { invalidateSharedActions(d->curDialog); d->focus_before_popup=d->curDialog; } else { //not needed??? invalidateSharedActions(focus_w); d->focus_before_popup=focus_w; } } } //keep focus in main window: if (w && w==d->nav) { // kdDebug() << "NAV" << endl; if (e->type()==QEvent::FocusIn) { return true; } else if (e->type()==QEvent::WindowActivate && w==d->focus_before_popup) { // d->nav->setFocus(); d->focus_before_popup=0; return true; } else if (e->type()==QEvent::FocusOut) { if (static_cast(e)->reason()==QFocusEvent::Tab) { //activate current child: if (d->curDialog) { d->curDialog->activate(); return true; } } else if (static_cast(e)->reason()==QFocusEvent::Popup) { d->focus_before_popup=w; } //invalidateSharedActions(); } else if (e->type()==QEvent::Hide) { setFocus(); return false; } } if (d->block_KMdiMainFrm_eventFilter)//we don't want KMDI to eat our event! return false; return KMdiMainFrm::eventFilter(obj,e);//let KMDI do its work } KexiDialogBase * KexiMainWindowImpl::openObject(const QCString& mime, const QString& name, int viewMode) { KexiPart::Item *item = d->prj->item(mime,name); if (!item) return 0; return openObject(item, viewMode); } KexiDialogBase * KexiMainWindowImpl::openObject(KexiPart::Item* item, int viewMode) { if (!d->prj || !item) return 0; KexiDialogBase *dlg = d->dialogs[ item->identifier() ]; bool needsUpdateViewGUIClient = true; if (dlg) { dlg->activate(); if (viewMode!=dlg->currentViewMode()) { if (!switchToViewMode(viewMode)) return 0; } /* if (dlg->currentViewMode()!=viewMode) { //try to switch bool cancelled; if (!dlg->switchToViewMode(viewMode, cancelled)) { //js TODO: add error msg... return 0; } if (cancelled) return 0; needsUpdateViewGUIClient = false; }*/ needsUpdateViewGUIClient = false; } else { dlg = d->prj->openObject(this, *item, viewMode); if (dlg) d->updatePropEditorVisibility(dlg->currentViewMode()); } if (!dlg || !activateWindow(dlg)) { //js TODO: add error msg... return 0; } if (needsUpdateViewGUIClient && !d->final) { //view changed: switch to this view's gui client KXMLGUIClient *viewClient=dlg->guiClient(); updateDialogViewGUIClient(viewClient); if (d->curDialogViewGUIClient && !viewClient) guiFactory()->removeClient(d->curDialogViewGUIClient); d->curDialogViewGUIClient=viewClient; //remember } invalidateViewModeActions(); if (viewMode!=dlg->currentViewMode()) invalidateSharedActions(); return dlg; } KexiDialogBase * KexiMainWindowImpl::openObjectFromNavigator(KexiPart::Item* item, int viewMode) { if (!d->prj || !item) return false; KexiDialogBase *dlg = d->dialogs[ item->identifier() ]; if (dlg) { if (activateWindow(dlg)) {//item->identifier())) {//just activate invalidateViewModeActions(); return dlg; } } //do the same as in openObject() return openObject(item, viewMode); } bool KexiMainWindowImpl::newObject( KexiPart::Info *info ) { if (!d->prj || !info) return false; KexiPart::Part *part = Kexi::partManager().part(info->mime()); if(!part) return false; //js TODO: move this code if(info->projectPartID() == -1) { KexiDB::TableSchema *ts = project()->dbConnection()->tableSchema("kexi__parts"); kdDebug() << "KexiMainWindowImpl::newObject(): schema: " << ts << endl; if (!ts) return false; KexiDB::FieldList *fl = ts->subList("p_name", "p_mime", "p_url"); kdDebug() << "KexiMainWindowImpl::newObject(): fieldlist: " << fl << endl; if (!fl) return false; if (!project()->dbConnection()->insertRecord(*fl, QVariant(info->groupName()), QVariant(info->mime()), QVariant("http://"))) return false; kdDebug() << "KexiMainWindowImpl::newObject(): insert success!" << endl; info->setProjectPartID(project()->dbConnection()->lastInsertedAutoIncValue("p_id", "kexi__parts")); kdDebug() << "KexiMainWindowImpl::newObject(): new id is: " << info->projectPartID() << endl; } KexiPart::Item *it = d->prj->createPartItem(info); //this, *item, viewMode); if (!it) { //js: todo: err return false; } if (!it->neverSaved()) //only add stored objects to the browser d->nav->addItem(it); return openObject(it, Kexi::DesignViewMode); } tristate KexiMainWindowImpl::removeObject( KexiPart::Item *item, bool dontAsk ) { if (!d->prj || !item) return false; KexiPart::Part *part = Kexi::partManager().part(item->mime()); if (!part) return false; if (!dontAsk) { if (KMessageBox::No == KMessageBox::warningYesNo(this, "

    "+i18n("Do you want to remove:") +"

    "+part->instanceName()+" \""+ item->name() + "\"?

    ", 0, KStdGuiItem::yes(), KStdGuiItem::no(), "askBeforeDeletePartItem"/*config entry*/)) return cancelled; } KexiDialogBase *dlg = d->dialogs[item->identifier()]; if (dlg) {//close existing window // if (!dlg->tryClose(true)) const bool tmp = d->forceDialogClosing; /*const bool remove_on_closing = */dlg->partItem()->neverSaved(); d->forceDialogClosing = true; const tristate res = closeDialog(dlg); d->forceDialogClosing = tmp; //restore if (!res || ~res) { return res; } // if (remove_on_closing) //already removed // return true; // if (!dlg->close(true)) // return true; //ok - close cancelled } if (!d->prj->removeObject(this, *item)) { //TODO(js) better msg showSorryMessage( i18n("Could not remove object.") ); return false; } return true; } void KexiMainWindowImpl::renameObject( KexiPart::Item *item, const QString& _newName, bool &success ) { QString newName = _newName.stripWhiteSpace(); if (newName.isEmpty()) { showSorryMessage( i18n("Could not set empty name for this object.") ); success = false; return; } if (!d->prj->renameObject(this, *item, newName)) { success = false; return; } } void KexiMainWindowImpl::slotObjectRenamed(const KexiPart::Item &item) { KexiDialogBase *dlg = d->dialogs[item.identifier()]; if (dlg) {//change item dlg->updateCaption(); if (static_cast(d->curDialog)==dlg)//optionally, update app. caption updateAppCaption(); } } int KexiMainWindowImpl::generatePrivateID() { return --d->privateIDCounter; } void KexiMainWindowImpl::propertyBufferSwitched(KexiDialogBase *dlg, bool force, bool preservePrevSelection) { kdDebug() << "KexiMainWindowImpl::propertyBufferSwitched()" << endl; if ((KexiDialogBase*)d->curDialog!=dlg) return; if (d->propEditor) { KexiPropertyBuffer *newBuf = d->curDialog ? d->curDialog->propertyBuffer() : 0; if (!newBuf || (force || static_cast(d->propBuffer) != newBuf)) { d->propBuffer = newBuf; d->propEditor->editor()->setBuffer( d->propBuffer, preservePrevSelection ); } } } void KexiMainWindowImpl::slotDirtyFlagChanged(KexiDialogBase* dlg) { KexiPart::Item *item = dlg->partItem(); //update text in navigator and app. caption if(!d->final) d->nav->updateItemName( item, dlg->dirty() ); invalidateActions(); updateAppCaption(); } void KexiMainWindowImpl::slotMdiModeHasBeenChangedTo(KMdi::MdiMode) { //after switching to other MDI mode, pointer to current dialog needs to be updated activateFirstWin(); activeWindowChanged(activeWindow()); } void KexiMainWindowImpl::slotTipOfTheDay() { //todo } void KexiMainWindowImpl::slotImportantInfo() { importantInfo(false); } void KexiMainWindowImpl::importantInfo(bool /*onStartup*/) { #if 0 if (onStartup && !d->showImportantInfoOnStartup) return; QString key = QString("showImportantInfo %1").arg(KEXI_VERSION_STRING); d->config->setGroup("Startup"); bool show = d->config->readBoolEntry(key,true); if (show || !onStartup) { //if !onStartup - dialog is always shown d->config->setGroup("TipOfDay"); if (!d->config->hasKey("RunOnStart")) d->config->writeEntry("RunOnStart",true); QString lang = KGlobal::locale()->language(); QString fname = locate("data", QString("kexi/readme_")+lang); if (fname.isEmpty())//back to default fname = locate("data", "kexi/readme_en"); KTipDialog tipDialog(new KTipDatabase(QString::null), 0); tipDialog.setCaption(i18n("Important Information")); QObjectList *l = tipDialog.queryList( "KPushButton" );//hack: hide <- -> buttons int i=0; for (QObjectListIt it( *l ); it.current() && i<2; ++it, i++ ) static_cast(it.current())->hide(); QFile f(fname); if ( f.open( IO_ReadOnly ) ) { QTextStream ts(&f); ts.setCodec( KGlobal::locale()->codecForEncoding() ); QTextBrowser *tb = Kexi::findFirstChild(&tipDialog,"KTextBrowser"); if (tb) { tb->setText( QString("%1").arg(ts.read()) ); } f.close(); } tipDialog.adjustSize(); QRect desk = QApplication::desktop()->screenGeometry( QApplication::desktop()->screenNumber(this) ); tipDialog.resize( QMAX(tipDialog.width(),desk.width()*3/5), QMAX(tipDialog.height(),desk.height()*3/5) ); KDialog::centerOnScreen(&tipDialog); tipDialog.setModal ( true ); tipDialog.exec(); //a hack: get user's settings d->config->setGroup("TipOfDay"); show = d->config->readBoolEntry("RunOnStart", show); } //write our settings back d->config->setGroup("Startup"); d->config->writeEntry(key,show); d->showImportantInfoOnStartup = false; #endif } void KexiMainWindowImpl::slotOptionsEnableForms(bool show, bool noMessage) { Kexi::tempShowForms() = show; d->config->setGroup("Unfinished"); d->config->writeEntry("EnableForms", Kexi::tempShowForms()); if (noMessage) return; QString note = i18n("Please note that forms are currently unstable functionality, provided only for your preview."); if (show) { KMessageBox::information(this, "

    "+i18n("Forms will be available after restarting Kexi application.")+"

    "+note+"

    "); } else { KMessageBox::information(this, "

    "+i18n("Forms will be hidden after restarting Kexi application.")+"

    "+note+"

    "); } } bool KexiMainWindowImpl::inFinalMode() const { return d->final; } bool KexiMainWindowImpl::initFinalMode(KexiProjectData *projectData) { //TODO Kexi::tempShowForms() = true; if(!projectData) return false; createKexiProject(projectData); //initialize project d->prj->m_final = true; //announce that we are in fianl mode if(!d->prj->open()) //try to open database return false; KexiDB::TableSchema *sch = d->prj->dbConnection()->tableSchema("kexi__final"); QString err_msg = i18n("Could not start project \"%1\" in Final Mode.") .arg(static_cast(projectData)->name()); if(!sch) { hide(); showErrorMessage( err_msg, i18n("No Final Mode data found.") ); return false; } KexiDB::Cursor *c = d->prj->dbConnection()->executeQuery(*sch); if(!c) { hide(); showErrorMessage( err_msg, i18n("Error reading Final Mode data.") ); return false; } QString startupPart; QString startupItem; while(c->moveNext()) { kdDebug() << "KexiMainWinImpl::initFinalMode(): property: [" << c->value(1).toString() << "] " << c->value(2).toString() << endl; if(c->value(1).toString() == "startup-part") startupPart = c->value(2).toString(); else if(c->value(1).toString() == "startup-item") startupItem = c->value(2).toString(); else if(c->value(1).toString() == "mainxmlui") setXML(c->value(2).toString()); } d->prj->dbConnection()->deleteCursor(c); kdDebug() << "KexiMainWinImpl::initFinalMode(): part: " << startupPart << endl; kdDebug() << "KexiMainWinImpl::initFinalMode(): item: " << startupItem << endl; initActions(); initUserActions(); guiFactory()->addClient(this); setStandardToolBarMenuEnabled(false); setHelpMenuEnabled(false); KexiPart::Info *i = Kexi::partManager().info(startupPart.latin1()); if (!i) { hide(); showErrorMessage( err_msg, i18n("Specified plugin does not exist.") ); return false; } Kexi::partManager().part(i); KexiPart::Item *item = d->prj->item(i, startupItem); if(!openObject(item, Kexi::DataViewMode)) { hide(); showErrorMessage( err_msg, i18n("Specified object could not be opened.") ); return false; } QWidget::setCaption("MyApp");//TODO return true; } void KexiMainWindowImpl::initUserActions() { KexiDB::Cursor *c = d->prj->dbConnection()->executeQuery("SELECT p_id, name, text, icon, method, arguments FROM kexi__useractions WHERE scope = 0"); if(!c) return; while(c->moveNext()) { KexiUserAction::fromCurrentRecord(this, actionCollection(), c); } d->prj->dbConnection()->deleteCursor(c); /* KexiUserAction *a1 = new KexiUserAction(this, actionCollection(), "user_dataview", "Change to dataview", "table"); Arguments args; args.append(QVariant("kexi/table")); args.append(QVariant("persons")); a1->setMethod(KexiUserAction::OpenObject, args); */ } #include "keximainwindowimpl.moc" diff --git a/kexi/main/startup/KexiStartup.cpp b/kexi/main/startup/KexiStartup.cpp index 9767bcb473..243f70b710 100644 --- a/kexi/main/startup/KexiStartup.cpp +++ b/kexi/main/startup/KexiStartup.cpp @@ -1,579 +1,579 @@ /* This file is part of the KDE project Copyright (C) 2003-2004 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "KexiStartup.h" #ifdef Q_WS_WIN # include "KexiStartup_p_win.h" #else # include "KexiStartup_p.h" #endif #include "kexiproject.h" #include "kexiprojectdata.h" #include "kexiprojectset.h" #include "kexiguimsghandler.h" #include #include #include "KexiStartupDialog.h" #include "KexiConnSelector.h" #include "KexiProjectSelectorBase.h" #include "KexiProjectSelector.h" #include "KexiNewProjectWizard.h" #include #include #include #include #include #include #include #include #include #if KDE_IS_VERSION(3,1,9) # include #endif #include #include namespace Kexi { static KexiStartupHandler _startupHandler; KexiStartupHandler& startupHandler() { return _startupHandler; } } //--------------------------------- class KexiStartupHandlerPrivate { public: KexiStartupHandlerPrivate() { } int dummy; }; //--------------------------------- static bool stripQuotes(const QString &item, QString &name) { if (item.left(1)=="\"" && item.right(1)=="\"") { name = item.mid(1, item.length()-2); return true; } name = item; return false; } void updateProgressBar(KProgressDialog *pd, char *buffer, int buflen) { char *p = buffer; QCString line(80); for (int i=0; i='0' && *p<='9'; j++, i++, p++) line+=QChar(*p); --i; --p; int percent = line.toInt(&ok); if (ok && percent>=0 && percent<=100 && pd->progressBar()->progress()progressBar()->setProgress(percent); qApp->processEvents(100); } } } } //--------------------------------- KexiStartupHandler::KexiStartupHandler() : QObject(0,"KexiStartupHandler") , KexiStartupData() , d( new KexiStartupHandlerPrivate() ) { } KexiStartupHandler::~KexiStartupHandler() { delete d; } bool KexiStartupHandler::getAutoopenObjects(KCmdLineArgs *args, const QCString &action_name) { QCStringList list = args->getOptionList(action_name); - QCStringList::const_iterator it; + QCStringList::ConstIterator it; bool atLeastOneFound = false; - for ( it = list.begin(); it!=list.end(); ++it) { + for ( it = list.constBegin(); it!=list.constEnd(); ++it) { QString type_name, obj_name, item=*it; int idx; bool name_required = true; if (action_name=="new") { obj_name = ""; stripQuotes(item, type_name); name_required = false; } else {//open, design, text //option with " " (default type == "table") if (stripQuotes(item, obj_name)) { type_name = "table"; } else if ((idx = item.find(':'))!=-1) { //option with type name specified: type_name = item.left(idx).lower(); obj_name = item.mid(idx+1); //optional: remove "" if (obj_name.left(1)=="\"" && obj_name.right(1)=="\"") obj_name = obj_name.mid(1, obj_name.length()-2); } else { //just obj. name: type name is "table" by default obj_name = item; type_name = "table"; } } if (type_name.isEmpty()) continue; if (name_required && obj_name.isEmpty()) continue; KexiProjectData::ObjectInfo info; info["name"]=obj_name; info["type"]=type_name; info["action"]=action_name; //ok, now add info for this object projectData()->autoopenObjects.append( info ); // projectData->autoopenObjects.append( QPair(type_name, obj_name) ); atLeastOneFound = true; } return atLeastOneFound; } bool KexiStartupHandler::init(int argc, char **argv) { m_action = DoNothing; KCmdLineArgs *args = KCmdLineArgs::parsedArgs(0); if (!args) return true; KexiDB::ConnectionData cdata; cdata.driverName = args->getOption("dbdriver"); // if (cdata.driverName.isEmpty()) // cdata.driverName = KexiDB::Driver::defaultFileBasedDriverName(); #ifdef KEXI_SERVER_SUPPORT cdata.hostName = args->getOption("host"); cdata.localSocketFileName = args->getOption("local-socket"); cdata.userName = args->getOption("user"); #endif // cdata.password = args->getOption("password"); bool fileDriverSelected; if (cdata.driverName.isEmpty()) fileDriverSelected = true; else { KexiDB::DriverManager dm; KexiDB::Driver::Info dinfo = dm.driverInfo(cdata.driverName); if (dinfo.name.isEmpty()) { //driver name provided explicity, but not found KMessageBox::sorry(0, dm.errorMsg()); return false; } fileDriverSelected = dinfo.fileBased; } bool projectFileExists = false; //obfuscate the password, if present //removed /* for (int i=1; i<(argc-1); i++) { if (qstrcmp("--password",argv[i])==0 || qstrcmp("-password",argv[i])==0) { QCString pwd(argv[i+1]); if (!pwd.isEmpty()) { pwd.fill(' '); pwd[0]='x'; qstrcpy(argv[i+1], (const char*)pwd); } break; } } */ #ifdef KEXI_SERVER_SUPPORT const QString portStr = args->getOption("port"); if (!portStr.isEmpty()) { bool ok; const int p = portStr.toInt(&ok); if (ok && p > 0) cdata.port = p; else { KMessageBox::sorry( 0, i18n("You have specified invalid port number \"%1\".")); return false; } } #endif #ifdef KEXI_SHOW_UNIMPLEMENTED m_forcedFinalMode = args->isSet("final-mode"); m_forcedDesignMode = args->isSet("design-mode"); #else m_forcedFinalMode = false; m_forcedDesignMode = false; #endif m_createDB = args->isSet("createdb"); m_alsoOpenDB = args->isSet("create-opendb"); if (m_alsoOpenDB) m_createDB = true; m_dropDB = args->isSet("dropdb"); const bool openExisting = !m_createDB && !m_dropDB; const QString couldnotMsg = QString::fromLatin1("\n") +i18n("Could not start Kexi application this way."); if (m_createDB && m_dropDB) { KMessageBox::sorry( 0, i18n( "You have used both \"createdb\" and \"dropdb\" startup options.")+couldnotMsg); return false; }; if (m_createDB || m_dropDB) { if (args->count()<1) { KMessageBox::sorry( 0, i18n("No project name specified.") ); return false; } m_action = Exit; } //TODO: add option for non-gui; integrate with KWallet; // move to static KexiProject method if (!fileDriverSelected && !cdata.driverName.isEmpty() && cdata.password.isEmpty()) { QString msg = cdata.userName.isEmpty() ? "

    "+i18n("Please enter the password.") : "

    "+i18n("Please enter the password for user %1.").arg(cdata.userName); QString srv = cdata.serverInfoString(false); if (srv.isEmpty() || srv.lower()=="localhost") srv = i18n("local database server"); msg += ("

    "+i18n("Database server: %1").arg(srv)+"

    "); QString usr; if (cdata.userName.isEmpty()) usr = i18n("unspecified user", "(unspecified)"); else usr = cdata.userName; msg += ("

    "+i18n("Username: %1").arg(usr)+"

    "); QCString pwd; if (QDialog::Accepted == KPasswordDialog::getPassword(pwd, msg)) { cdata.password = QString(pwd); } else { m_action = Exit; return true; } } kdDebug() << "ARGC==" << args->count() << endl; for (int i=0;icount();i++) { kdDebug() << "ARG" <count()>=1) { QString prjName = args->arg(0); if (fileDriverSelected) { QFileInfo finfo(prjName); cdata.setFileName( finfo.absFilePath() ); projectFileExists = finfo.exists(); if (m_dropDB && !projectFileExists) { KMessageBox::sorry(0, i18n("Could not remove project.\nThe file \"%1\" does not exist.") .arg(cdata.dbFileName())); return 0; } } if (m_createDB) { if (cdata.driverName.isEmpty()) cdata.driverName = KexiDB::Driver::defaultFileBasedDriverName(); m_projectData = new KexiProjectData(cdata, prjName); //dummy } else { if (fileDriverSelected) { int detectOptions = 0; if (m_dropDB) detectOptions |= DontConvert; cdata.driverName = KexiStartupHandler::detectDriverForFile( cdata.driverName, cdata.fileName(), 0, detectOptions ); if (cdata.driverName.isEmpty()) return false; } m_projectData = new KexiProjectData(cdata, prjName); } // if (!m_projectData) // return false; } if (args->count()>1) { //TODO: KRun another Kexi instances } //---autoopen objects: const bool atLeastOneAOOFound = getAutoopenObjects(args, "open") || getAutoopenObjects(args, "design") || getAutoopenObjects(args, "edittext") || getAutoopenObjects(args, "new"); if (atLeastOneAOOFound && !openExisting) { KMessageBox::information( 0, i18n("You have specified a few database objects to be opened automatically, " "using startup options.\n" "These options will be ignored because it is not available while creating " "or dropping projects.")); } if (m_createDB) { bool cancelled; KexiGUIMessageHandler gui; KexiProject *prj = KexiProject::createBlankProject(cancelled, projectData(), &gui); bool ok = prj!=0; delete prj; if (cancelled) return true; if (!m_alsoOpenDB) { if (ok) { KMessageBox::information( 0, i18n("Project \"%1\" created successfully.") .arg( projectData()->databaseName() )); } return ok; } } else if (m_dropDB) { KexiGUIMessageHandler gui; tristate res = KexiProject::dropProject(projectData(), &gui, false/*ask*/); if (res) KMessageBox::information( 0, i18n("Project \"%1\" dropped successfully.") .arg( projectData()->databaseName() )); return res!=false; } //------ /* if (m_forcedFinalMode || (m_projectData && projectData->finalMode())) { //TODO: maybe also auto allow to open objects... KexiMainWindowImpl::initFinal(m_projectData); return; }*/ if (!m_projectData) { cdata = KexiDB::ConnectionData(); //clear // importantInfo(true); // //some connection data KexiDB::ConnectionData *conndata; conndata = new KexiDB::ConnectionData(); conndata->connName = "My connection"; conndata->driverName = "mysql"; conndata->hostName = "myhost.org"; conndata->userName = "otheruser"; conndata->port = 53121; Kexi::connset().addConnectionData(conndata); conndata = new KexiDB::ConnectionData(); conndata->connName = "Local pgsql connection"; conndata->driverName = "postgresql"; conndata->hostName = "localhost"; // -- default //"host.net"; #if !KDE_IS_VERSION(3,1,9) conndata->userName = getlogin(); //-- temporary #else conndata->userName = KUser().loginName(); //-- temporary #endif Kexi::connset().addConnectionData(conndata); //some recent projects data KexiProjectData *projectData = new KexiProjectData( *conndata, "bigdb", "Big DB" ); projectData->setCaption("My Big Project"); projectData->setDescription("This is my first biger project started yesterday. Have fun!"); Kexi::recentProjects().addProjectData(projectData); // if (!KexiStartupDialog::shouldBeShown()) return true; KexiStartupDialog dlg(KexiStartupDialog::Everything, KexiStartupDialog::CheckBoxDoNotShowAgain, Kexi::connset(), Kexi::recentProjects(), 0, "dlg"); if (dlg.exec()!=QDialog::Accepted) return true; int r = dlg.result(); if (r==KexiStartupDialog::TemplateResult) { kdDebug() << "Template key == " << dlg.selectedTemplateKey() << endl; if (dlg.selectedTemplateKey()=="blank") { m_action = CreateBlankProject; //createBlankDatabase(); return true; } return true;//todo - templates: m_action = UseTemplate; } else if (r==KexiStartupDialog::OpenExistingResult) { kdDebug() << "Existing project --------" << endl; QString selFile = dlg.selectedExistingFile(); if (!selFile.isEmpty()) { //file-based project kdDebug() << "Project File: " << selFile << endl; cdata.setFileName( selFile ); cdata.driverName = KexiStartupHandler::detectDriverForFile( cdata.driverName, selFile ); if (cdata.driverName.isEmpty()) return false; m_projectData = new KexiProjectData(cdata, selFile); } else if (dlg.selectedExistingConnection()) { kdDebug() << "Existing connection: " << dlg.selectedExistingConnection()->serverInfoString() << endl; KexiDB::ConnectionData *cdata = dlg.selectedExistingConnection(); //ok, now we will try to show projects for this connection to the user m_projectData = selectProject( cdata ); } } else if (r==KexiStartupDialog::OpenRecentResult) { kdDebug() << "Recent project --------" << endl; const KexiProjectData *data = dlg.selectedProjectData(); if (data) { kdDebug() << "Selected project: database=" << data->databaseName() << " connection=" << data->constConnectionData()->serverInfoString() << endl; } //js: TODO return true; } if (!m_projectData) return true; } if (m_projectData && (openExisting || (m_createDB && m_alsoOpenDB))) { m_action = OpenProject; } //show if wasn't show yet // importantInfo(true); return true; } QString KexiStartupHandler::detectDriverForFile( const QString& driverName, const QString &dbFileName, QWidget *parent, int options ) { QString ret; QFileInfo finfo(dbFileName); if (dbFileName.isEmpty() || !finfo.isReadable()) { KMessageBox::sorry(parent, i18n( "Could not load project.\nThe file \"%1\" does not exist.").arg(dbFileName)); return QString::null; } if (!finfo.isWritable()) { //TODO: if file is ro: change project mode } KMimeType::Ptr ptr = KMimeType::findByFileContent(dbFileName); QString mimename = ptr.data()->name(); kdDebug() << "KexiStartupHandler::detectProjectData(): found mime is: " << ptr.data()->name() << endl; if (mimename=="application/x-kexiproject-shortcut") { return QString::null;//TODO: get information for xml shortcut file } // "application/x-kexiproject-sqlite", etc QString detectedDriverName = Kexi::driverManager().lookupByMime(mimename).latin1(); if (/*cdata.driverName.isEmpty() ||*/ (!driverName.isEmpty() && driverName.lower()!=detectedDriverName.lower() && KMessageBox::Yes == KMessageBox::warningYesNo(parent, i18n( "The project file \"%1\" is recognized as compatible with \"%2\" database driver, " "while you have asked for \"%3\" database driver to be used.\n" "Do you want to use \"%4\" database driver?") .arg(dbFileName).arg(detectedDriverName).arg(driverName).arg(detectedDriverName)) )) { ret = detectedDriverName; } kdDebug() << "KexiStartupHandler::detectProjectData(): driver name: " << ret << endl; //hardcoded for convenience: const QString newFileFormat = "SQLite3"; if (!(options & DontConvert) && detectedDriverName.lower()=="sqlite2" && detectedDriverName.lower()!=driverName.lower() && KMessageBox::Yes == KMessageBox::questionYesNo(parent, i18n( "Previous version of database file format (\"%1\") is detected in the \"%2\" project file.\n" "Do you want to convert the project to a new \"%3\" format (recommended)?") .arg(detectedDriverName).arg(dbFileName).arg(newFileFormat)) ) { // SQLite2ToSQLite3Migration *migr = new SQLite2ToSQLite3Migration migr( finfo.absFilePath() ); tristate res = migr.run(); kdDebug() << "--- migr.run() END ---" <error()) { setStatus(i18n("Could not load list of available projects for connection \"%1\"") .arg(cdata->serverInfoString()), prjdlg.projectSet()->errorMsg()); return 0; } if (prjdlg.exec()!=QDialog::Accepted) return 0; if (prjdlg.selectedProjectData()) { //deep copy projectData = new KexiProjectData(*prjdlg.selectedProjectData()); } return projectData; } diff --git a/kexi/main/startup/KexiStartupFileDialog_win.cpp b/kexi/main/startup/KexiStartupFileDialog_win.cpp index d03b5335e8..f5f02abea8 100644 --- a/kexi/main/startup/KexiStartupFileDialog_win.cpp +++ b/kexi/main/startup/KexiStartupFileDialog_win.cpp @@ -1,485 +1,485 @@ /* This file is part of the KDE project Copyright (C) 2003-2004 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*! Temporary moved from QKW KFileDialog implementation. TODO: move to KDElibs/win32 KFileDialog wrapper */ #include "KexiStartupFileDialog.h" #include #include #include class KexiStartupFileDialogBasePrivate { public: KexiStartupFileDialogBasePrivate() {} KFile::Mode mode; QString kde_filters; QStringList mimetypes; }; KexiStartupFileDialogBase::KexiStartupFileDialogBase( const QString & dirName, const QString & filter, QWidget * parent, const char * name, bool modal ) : QFileDialog( dirName, filter, parent, name, modal ) , d(new KexiStartupFileDialogBasePrivate()) { init(dirName, filter, parent); //find "OK" button QObjectList *l = queryList( "QPushButton", "OK", false ); m_okBtn = dynamic_cast(l->first()); delete l; l = queryList( "QLineEdit", "name/filter editor", false ); m_lineEdit = dynamic_cast(l->first()); delete l; adjustSize(); } KexiStartupFileDialogBase::~KexiStartupFileDialogBase() { } void KexiStartupFileDialogBase::init(const QString& startDir, const QString& filter, QWidget* widget) { //TODO initStatic(); //TODO d = new KFileDialogPrivate(); //(js) d->boxLayout = 0; //TODO d->keepLocation = false; //TODO d->operationMode = Opening; setMode(KFile::File | KFile::ExistingOnly); //(js) default: open action setIcon( KGlobal::iconLoader()->loadIcon("fileopen", KIcon::Desktop) ); //TODO d->hasDefaultFilter = false; //TODO d->hasView = false; //(js) d->mainWidget = new QWidget( this, "KFileDialog::mainWidget"); //(js) setMainWidget( d->mainWidget ); //(js) d->okButton = new KPushButton( KStdGuiItem::ok(), d->mainWidget ); //(js) d->okButton->setDefault( true ); //(js) d->cancelButton = new KPushButton(KStdGuiItem::cancel(), d->mainWidget); //(js) connect( d->okButton, SIGNAL( clicked() ), SLOT( slotOk() )); //(js) connect( d->cancelButton, SIGNAL( clicked() ), SLOT( slotCancel() )); //(js) d->customWidget = widget; //(js) d->autoSelectExtCheckBox = 0; // delayed loading //TODO d->autoSelectExtChecked = false; //(js) d->urlBar = 0; // delayed loading //TODO KConfig *config = KGlobal::config(); //TODO KConfigGroupSaver cs( config, ConfigGroup ); //TODO d->initializeSpeedbar = config->readBoolEntry( "Set speedbar defaults", //TODO true ); //TODO d->completionLock = false; //TODO QtMsgHandler oldHandler = qInstallMsgHandler( silenceQToolBar ); //TODO toolbar = 0; //(js) //(js) toolbar = new KToolBar( d->mainWidget, "KFileDialog::toolbar", true); //(js) toolbar->setFlat(true); //TODO qInstallMsgHandler( oldHandler ); //(js) d->pathCombo = new KURLComboBox( KURLComboBox::Directories, true, //(js) toolbar, "path combo" ); //(js) QToolTip::add( d->pathCombo, i18n("Often used directories") ); //(js) QWhatsThis::add( d->pathCombo, "" + i18n("Commonly used locations are listed here. " //(js) "This includes standard locations, such as your home directory, as well as " //(js) "locations that have been visited recently.") + autocompletionWhatsThisText); /* KURL u; u.setPath( QDir::rootDirPath() ); QString text = i18n("Root Directory: %1").arg( u.path() ); d->pathCombo->addDefaultURL( u, KMimeType::pixmapForURL( u, 0, KIcon::Small ), text ); u.setPath( QDir::homeDirPath() ); text = i18n("Home Directory: %1").arg( u.path( +1 ) ); d->pathCombo->addDefaultURL( u, KMimeType::pixmapForURL( u, 0, KIcon::Small ), text ); KURL docPath; docPath.setPath( KGlobalSettings::documentPath() ); if ( u.path(+1) != docPath.path(+1) ) { text = i18n("Documents: %1").arg( docPath.path( +1 ) ); d->pathCombo->addDefaultURL( u, KMimeType::pixmapForURL( u, 0, KIcon::Small ), text ); } u.setPath( KGlobalSettings::desktopPath() ); text = i18n("Desktop: %1").arg( u.path( +1 ) ); d->pathCombo->addDefaultURL( u, KMimeType::pixmapForURL( u, 0, KIcon::Small ), text ); u.setPath( "/tmp" ); d->url = getStartURL( startDir, d->fileClass ); d->selection = d->url.url(); // If local, check it exists. If not, go up until it exists. if ( d->url.isLocalFile() ) { if ( !QFile::exists( d->url.path() ) ) { d->url = d->url.upURL(); QDir dir( d->url.path() ); while ( !dir.exists() ) { d->url = d->url.upURL(); dir.setPath( d->url.path() ); } } } ops = new KDirOperator(d->url, d->mainWidget, "KFileDialog::ops"); ops->setOnlyDoubleClickSelectsFiles( true ); connect(ops, SIGNAL(urlEntered(const KURL&)), SLOT(urlEntered(const KURL&))); connect(ops, SIGNAL(fileHighlighted(const KFileItem *)), SLOT(fileHighlighted(const KFileItem *))); connect(ops, SIGNAL(fileSelected(const KFileItem *)), SLOT(fileSelected(const KFileItem *))); connect(ops, SIGNAL(finishedLoading()), SLOT(slotLoadingFinished())); ops->setupMenu(KDirOperator::SortActions | KDirOperator::FileActions | KDirOperator::ViewActions); KActionCollection *coll = ops->actionCollection(); // plug nav items into the toolbar coll->action( "up" )->plug( toolbar ); coll->action( "up" )->setWhatsThis(i18n("Click this button to enter the parent directory.

    " "For instance, if the current location is file:/home/%1 clicking this " "button will take you to file:/home.").arg(getlogin())); coll->action( "back" )->plug( toolbar ); coll->action( "back" )->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history.")); coll->action( "forward" )->plug( toolbar ); coll->action( "forward" )->setWhatsThis(i18n("Click this button to move forward one step in the browsing history.")); coll->action( "reload" )->plug( toolbar ); coll->action( "reload" )->setWhatsThis(i18n("Click this button to reload the contents of the current location.")); coll->action( "mkdir" )->setShortcut(Key_F10); coll->action( "mkdir" )->plug( toolbar ); coll->action( "mkdir" )->setWhatsThis(i18n("Click this button to create a new directory.")); d->bookmarkHandler = new KFileBookmarkHandler( this ); toolbar->insertButton(QString::fromLatin1("bookmark"), (int)HOTLIST_BUTTON, true, i18n("Bookmarks")); toolbar->getButton(HOTLIST_BUTTON)->setPopup( d->bookmarkHandler->menu(), true); QWhatsThis::add(toolbar->getButton(HOTLIST_BUTTON), i18n("This button allows you to bookmark specific locations. " "Click on this button to open the bookmark menu where you may add, " "edit or select a bookmark.

    " "These bookmarks are specific to the file dialog, but otherwise operate " "like bookmarks elsewhere in KDE.")); connect( d->bookmarkHandler, SIGNAL( openURL( const QString& )), SLOT( enterURL( const QString& ))); KToggleAction *showSidebarAction = new KToggleAction(i18n("Show Quick Access Navigation Panel"), Key_F9, coll,"toggleSpeedbar"); connect( showSidebarAction, SIGNAL( toggled( bool ) ), SLOT( toggleSpeedbar( bool )) ); KActionMenu *menu = new KActionMenu( i18n("Configure"), "configure", this, "extra menu" ); menu->setWhatsThis(i18n("This is the configuration menu for the file dialog. " "Various options can be accessed from this menu including:

      " "
    • how files are sorted in the list
    • " "
    • types of view, including icon and list
    • " "
    • showing of hidden files
    • " "
    • the Quick Access navigation panel
    • " "
    • file previews
    • " "
    • separating directories from files
    ")); menu->insert( coll->action( "sorting menu" )); menu->insert( coll->action( "separator" )); coll->action( "short view" )->setShortcut(Key_F6); menu->insert( coll->action( "short view" )); coll->action( "detailed view" )->setShortcut(Key_F7); menu->insert( coll->action( "detailed view" )); menu->insert( coll->action( "separator" )); coll->action( "show hidden" )->setShortcut(Key_F8); menu->insert( coll->action( "show hidden" )); menu->insert( showSidebarAction ); coll->action( "preview" )->setShortcut(Key_F11); menu->insert( coll->action( "preview" )); coll->action( "separate dirs" )->setShortcut(Key_F12); menu->insert( coll->action( "separate dirs" )); menu->setDelayed( false ); connect( menu->popupMenu(), SIGNAL( aboutToShow() ), ops, SLOT( updateSelectionDependentActions() )); menu->plug( toolbar ); */ /* * ugly little hack to have a 5 pixel space between the buttons * and the combo box */ /* QWidget *spacerWidget = new QWidget(toolbar); //(js) spacerWidget->setMinimumWidth(spacingHint()); //(js) spacerWidget->setMaximumWidth(spacingHint()); d->m_pathComboIndex = toolbar->insertWidget(-1, -1, spacerWidget); toolbar->insertWidget(PATH_COMBO, 0, d->pathCombo); toolbar->setItemAutoSized (PATH_COMBO); toolbar->setIconText(KToolBar::IconOnly); toolbar->setBarPos(KToolBar::Top); toolbar->setMovingEnabled(false); toolbar->adjustSize(); d->pathCombo->setCompletionObject( ops->dirCompletionObject(), false ); connect( d->pathCombo, SIGNAL( urlActivated( const KURL& )), this, SLOT( enterURL( const KURL& ) )); connect( d->pathCombo, SIGNAL( returnPressed( const QString& )), this, SLOT( enterURL( const QString& ) )); connect( d->pathCombo, SIGNAL(textChanged( const QString& )), SLOT( pathComboChanged( const QString& ) )); connect( d->pathCombo, SIGNAL( completion( const QString& )), SLOT( dirCompletion( const QString& ))); connect( d->pathCombo, SIGNAL( textRotation(KCompletionBase::KeyBindingType) ), d->pathCombo, SLOT( rotateText(KCompletionBase::KeyBindingType) )); QString whatsThisText; // the Location label/edit d->locationLabel = new QLabel(i18n("&Location:"), d->mainWidget); locationEdit = new KURLComboBox(KURLComboBox::Files, true, d->mainWidget, "LocationEdit"); updateLocationWhatsThis (); d->locationLabel->setBuddy(locationEdit); // to get the completionbox-signals connected: locationEdit->setHandleSignals( true ); (void) locationEdit->completionBox(); locationEdit->setFocus(); // locationEdit->setCompletionObject( new KURLCompletion() ); // locationEdit->setAutoDeleteCompletionObject( true ); locationEdit->setCompletionObject( ops->completionObject(), false ); connect( locationEdit, SIGNAL( returnPressed() ), this, SLOT( slotOk())); connect(locationEdit, SIGNAL( activated( const QString& )), this, SLOT( locationActivated( const QString& ) )); connect( locationEdit, SIGNAL( completion( const QString& )), SLOT( fileCompletion( const QString& ))); connect( locationEdit, SIGNAL( textRotation(KCompletionBase::KeyBindingType) ), locationEdit, SLOT( rotateText(KCompletionBase::KeyBindingType) )); // the Filter label/edit whatsThisText = i18n("This is the filter to apply to the file list. " "File names that do not match the filter will not be shown.

    " "You may select from one of the preset filters in the " "drop down menu, or you may enter a custom filter " "directly into the text area.

    " "Wildcards such as * and ? are allowed."); d->filterLabel = new QLabel(i18n("&Filter:"), d->mainWidget); QWhatsThis::add(d->filterLabel, whatsThisText); filterWidget = new KFileFilterCombo(d->mainWidget, "KFileDialog::filterwidget"); QWhatsThis::add(filterWidget, whatsThisText); setFilter(filter); d->filterLabel->setBuddy(filterWidget); connect(filterWidget, SIGNAL(filterChanged()), SLOT(slotFilterChanged())); // the Automatically Select Extension checkbox // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig()) d->autoSelectExtCheckBox = new QCheckBox (d->mainWidget); connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(slotAutoSelectExtClicked())); initGUI(); // activate GM readRecentFiles( config ); adjustSize(); // we set the completionLock to avoid entering pathComboChanged() when // inserting the list of URLs into the combo. d->completionLock = true; ops->setViewConfig( config, ConfigGroup ); readConfig( config, ConfigGroup ); setSelection(d->selection); d->completionLock = false; */ } void KexiStartupFileDialogBase::clearFilter() { d->kde_filters = "";//(js) QFileDialog::setFilter(""); //(js); //todo d->mimetypes.clear(); //todo d->hasDefaultFilter = false; updateAutoSelectExtension (); } KFile::Mode KexiStartupFileDialogBase::mode() const { return d->mode; } void KexiStartupFileDialogBase::setMode( KFile::Mode m ) { //(js) translate mode for QFileDialog d->mode = m; QFileDialog::Mode qm = (QFileDialog::Mode)0; if (m & KFile::File) qm = Mode(qm | QFileDialog::AnyFile); else if (m & KFile::Directory) qm = Mode(qm | QFileDialog::DirectoryOnly); if (m & KFile::Files) qm = Mode(qm | QFileDialog::ExistingFiles); if (m & KFile::ExistingOnly) qm = Mode(qm | QFileDialog::ExistingFile); QFileDialog::setMode( qm ); /*(js) ops->setMode(m); if ( ops->dirOnlyMode() ) { //(js) filterWidget->setDefaultFilter( i18n("*|All Directories") ); } else { //(js) filterWidget->setDefaultFilter( i18n("*|All Files") ); } updateAutoSelectExtension ();*/ } void KexiStartupFileDialogBase::setMode( unsigned int m ) { setMode(static_cast( m )); } void KexiStartupFileDialogBase::setOperationMode( KFileDialog::OperationMode mode ) { // d->operationMode = mode; // d->keepLocation = (mode == Saving); if (mode == KFileDialog::Saving) { setMode( KFile::File ); setIcon( KGlobal::iconLoader()->loadIcon("filesave", KIcon::Desktop) ); } //(js) filterWidget->setEditable( !d->hasDefaultFilter || mode != Saving ); //(js) d->okButton->setGuiItem( (mode == Saving) ? KStdGuiItem::save() : KStdGuiItem::ok() ); //TODO updateLocationWhatsThis (); updateAutoSelectExtension (); } QString KexiStartupFileDialogBase::currentFilter() const { //(js)filterWidget->currentFilter(); //we need to convert Qt filter format to KDE format //Qt format: "some text (*.first *.second)" or "All (*)" //KDE format: "*.first *.second" or "*" QString f = selectedFilter(); if (f.find('(')!=-1) f = f.mid(f.find('(')+1); if (f.mid(f.find(')')!=-1)) f = f.left(f.find(')')); return f; } void KexiStartupFileDialogBase::setFilter(const QString& filter) { d->kde_filters = filter; int pos = d->kde_filters.find('/'); // Check for an un-escaped '/', if found // interpret as a MIME filter. if (pos > 0 && filter[pos - 1] != '\\') { QStringList filters = QStringList::split( " ", d->kde_filters ); setMimeFilter( filters ); return; } // Strip the escape characters from // escaped '/' characters. QString copy (d->kde_filters); for (pos = 0; (pos = copy.find("\\/", pos)) != -1; ++pos) copy.remove(pos, 1); // //we need to convert KDE filter format to Qt format //Qt format: "some text (*.first *.second)" or "All (*)" //KDE format: "*.first *.second" or "*" QStringList filters = QStringList::split("\n",d->kde_filters); QString current; QString converted; //finally - converted filter - for (QStringList::Iterator it = filters.begin(); it!=filters.end();++it) { + for (QStringList::ConstIterator it = filters.constBegin(); it!=filters.constEnd();++it) { current = *it; QString new_f;//filter part QString new_name;//filter name part int p = (*it).find('|'); if (p!=-1) { new_f = current.left(p); new_name = current.mid(p+1); }else { new_f = current; new_name = current; //nothing better } //remove (.....) from name p=new_name.find('('); int p2 = new_name.findRev(')'); QString new_name1, new_name2; if (p!=-1) new_name1 = new_name.left(p); if (p2!=-1) new_name2 = new_name.mid(p2+1); if (!new_name1.isEmpty() || !new_name2.isEmpty()) new_name = new_name1.stripWhiteSpace() + " " + new_name2.stripWhiteSpace(); new_name.replace('(',""); new_name.replace(')',""); new_name = new_name.stripWhiteSpace(); if (!converted.isEmpty()) converted += ";;"; converted += (new_name + " (" + new_f + ")"); } QFileDialog::setFilters(converted); // //(js) ops->clearFilter(); //(js) filterWidget->setFilter(copy); //(js) ops->setNameFilter(filterWidget->currentFilter()); //(js) d->hasDefaultFilter = false; //(js) filterWidget->setEditable( true ); updateAutoSelectExtension (); } void KexiStartupFileDialogBase::setMimeFilter( const QStringList& mimeTypes, const QString& defaultType ) { d->mimetypes = mimeTypes; //(js) filterWidget->setMimeFilter( mimeTypes, defaultType ); //(js) QStringList types = QStringList::split(" ", filterWidget->currentFilter()); //(js) types.append( QString::fromLatin1( "inode/directory" )); //(js) ops->clearFilter(); //(js) ops->setMimeFilter( types ); //(js) d->hasDefaultFilter = !defaultType.isEmpty(); //(js) filterWidget->setEditable( !d->hasDefaultFilter || //(js) d->operationMode != Saving ); //TODO updateAutoSelectExtension (); } diff --git a/kexi/plugins/queries/kexiquerydesignerguieditor.cpp b/kexi/plugins/queries/kexiquerydesignerguieditor.cpp index 36bc22d516..6c70a8ba83 100644 --- a/kexi/plugins/queries/kexiquerydesignerguieditor.cpp +++ b/kexi/plugins/queries/kexiquerydesignerguieditor.cpp @@ -1,907 +1,905 @@ /* This file is part of the KDE project Copyright (C) 2004 Lucijan Busch Copyright (C) 2004 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kexiquerydesignerguieditor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kexiquerypart.h" #include "kexidialogbase.h" #include "kexidatatable.h" #include "kexi_utils.h" #include "kexisectionheader.h" #include "kexitableviewpropertybuffer.h" #include "widget/relations/kexirelationwidget.h" #include "widget/relations/kexirelationviewtable.h" class KexiQueryDesignerGuiEditorPrivate { public: KexiQueryDesignerGuiEditorPrivate() : droppedNewItem(0) , slotTableAdded_enabled(true) { } KexiTableViewData *data; KexiDataTable *dataTable; QGuardedPtr conn; KexiRelationWidget *relations; KexiSectionHeader *head; QSplitter *spl; //! used to remember in slotDroppedAtRow() what data was dropped, //! so we can create appropriate prop. buffer in slotRowInserted() KexiTableViewData *fieldColumnData, *tablesColumnData; KexiTableViewPropertyBuffer* buffers; KexiTableItem *droppedNewItem; QString droppedNewTable, droppedNewField; bool slotTableAdded_enabled : 1; }; //========================================================= KexiQueryDesignerGuiEditor::KexiQueryDesignerGuiEditor( KexiMainWindow *mainWin, QWidget *parent, const char *name) : KexiViewBase(mainWin, parent, name) , d( new KexiQueryDesignerGuiEditorPrivate() ) { d->conn = mainWin->project()->dbConnection(); // m_doc = doc; d->spl = new QSplitter(Vertical, this); d->spl->setChildrenCollapsible(false); // KexiInternalPart::createWidgetInstance("relation", win, s, "relation"); d->relations = new KexiRelationWidget(mainWin, d->spl, "relations"); connect(d->relations, SIGNAL(tableAdded(KexiDB::TableSchema&)), this, SLOT(slotTableAdded(KexiDB::TableSchema&))); connect(d->relations, SIGNAL(tableHidden(KexiDB::TableSchema&)), this, SLOT(slotTableHidden(KexiDB::TableSchema&))); connect(d->relations, SIGNAL(tableFieldDoubleClicked(KexiDB::TableSchema*,const QString&)), this, SLOT(slotTableFieldDoubleClicked(KexiDB::TableSchema*,const QString&))); // addActionProxyChild( m_view->relationView() ); /* KexiRelationPart *p = win->relationPart(); if(p) p->createWidget(s, win);*/ d->head = new KexiSectionHeader(i18n("Query columns"), Vertical, d->spl); // s->setResizeMode(d->head, QSplitter::KeepSize); d->dataTable = new KexiDataTable(mainWin, d->head, "guieditor_dataTable", false); d->dataTable->tableView()->setSpreadSheetMode(); // d->dataTable->tableView()->addDropFilter("kexi/field"); // setFocusProxy(d->dataTable); d->data = new KexiTableViewData(); //just empty data d->buffers = new KexiTableViewPropertyBuffer( this, d->dataTable->tableView() ); initTableColumns(); initTableRows(); // d->dataTable->tableView()->setData(d->data); // d->buffers = new KexiTableViewPropertyBuffer( this, d->dataTable->tableView() ); // //last column occupies the rest of the area // d->dataTable->tableView()->setColumnStretchEnabled( true, d->data->columnsCount()-1 ); // d->dataTable->tableView()->setColumnStretchEnabled(true, 0); // d->dataTable->tableView()->setColumnStretchEnabled(true, 1); // d->dataTable->tableView()->adjustHorizontalHeaderSize(); QValueList c; c << 0 << 1 << 4; d->dataTable->tableView()->maximizeColumnsWidth( c ); // d->dataTable->tableView()->adjustColumnWidthToContents(-1); d->dataTable->tableView()->adjustColumnWidthToContents(2);//'visible' d->dataTable->tableView()->setDropsAtRowEnabled(true); connect(d->dataTable->tableView(), SIGNAL(dragOverRow(KexiTableItem*,int,QDragMoveEvent*)), this, SLOT(slotDragOverTableRow(KexiTableItem*,int,QDragMoveEvent*))); connect(d->dataTable->tableView(), SIGNAL(droppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&)), this, SLOT(slotDroppedAtRow(KexiTableItem*,int,QDropEvent*,KexiTableItem*&))); connect(d->data, SIGNAL(aboutToChangeCell(KexiTableItem*,int,QVariant,KexiDB::ResultInfo*)), this, SLOT(slotBeforeCellChanged(KexiTableItem*,int,QVariant,KexiDB::ResultInfo*))); connect(d->data, SIGNAL(rowInserted(KexiTableItem*,uint,bool)), this, SLOT(slotRowInserted(KexiTableItem*,uint,bool))); connect(d->relations, SIGNAL(tablePositionChanged(KexiRelationViewTableContainer*)), this, SLOT(slotTablePositionChanged(KexiRelationViewTableContainer*))); connect(d->relations, SIGNAL(aboutConnectionRemove(KexiRelationViewConnection*)), this, SLOT(slotAboutConnectionRemove(KexiRelationViewConnection*))); // m_table = new KexiTableView(d->data, s, "designer"); QVBoxLayout *l = new QVBoxLayout(this); l->addWidget(d->spl); addChildView(d->relations); // addActionProxyChild(d->relations); setViewWidget(d->relations); addChildView(d->dataTable); // addActionProxyChild(d->dataTable); // restore(); d->relations->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); d->head->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); updateGeometry(); // d->spl->setResizeMode(d->relations, QSplitter::KeepSize); // d->spl->setResizeMode(d->head, QSplitter::FollowSizeHint); d->spl->setSizes(QValueList()<< 800<<400); } KexiQueryDesignerGuiEditor::~KexiQueryDesignerGuiEditor() { } void KexiQueryDesignerGuiEditor::initTableColumns() { KexiTableViewColumn *col1 = new KexiTableViewColumn(i18n("Column"), KexiDB::Field::Enum); d->fieldColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text); col1->setRelatedData( d->fieldColumnData ); d->data->addColumn(col1); KexiTableViewColumn *col2 = new KexiTableViewColumn(i18n("Table"), KexiDB::Field::Enum); d->tablesColumnData = new KexiTableViewData(KexiDB::Field::Text, KexiDB::Field::Text); col2->setRelatedData( d->tablesColumnData ); d->data->addColumn(col2); KexiTableViewColumn *col3 = new KexiTableViewColumn(i18n("Visible"), KexiDB::Field::Boolean); d->data->addColumn(col3); KexiDB::Field *f = new KexiDB::Field(i18n("Totals"), KexiDB::Field::Enum); QValueVector totalsTypes; totalsTypes.append( i18n("Group by") ); totalsTypes.append( i18n("Sum") ); totalsTypes.append( i18n("Average") ); totalsTypes.append( i18n("Min") ); totalsTypes.append( i18n("Max") ); //todo: more like this f->setEnumHints(totalsTypes); KexiTableViewColumn *col4 = new KexiTableViewColumn(*f); d->data->addColumn(col4); /*TODO f= new KexiDB::Field(i18n("Sort"), KexiDB::Field::Enum); QValueVector sortTypes; sortTypes.append( i18n("Ascending") ); sortTypes.append( i18n("Descending") ); sortTypes.append( i18n("No sorting") ); f->setEnumHints(sortTypes); KexiTableViewColumn *col5 = new KexiTableViewColumn(*f); d->data->addColumn(col5);*/ KexiTableViewColumn *col6 = new KexiTableViewColumn(i18n("Criteria"), KexiDB::Field::Text); d->data->addColumn(col6); // KexiTableViewColumn *col7 = new KexiTableViewColumn(i18n("Or"), KexiDB::Field::Text); // d->data->addColumn(col7); } void KexiQueryDesignerGuiEditor::initTableRows() { // d->data->clear(); d->data->deleteAllRows(); const int columns = d->data->columnsCount(); for (int i=0; i<(int)d->buffers->size(); i++) { // KexiPropertyBuffer *buff = new KexiPropertyBuffer(this); // buff->insert("primaryKey", KexiProperty("pkey", QVariant(false, 4), i18n("Primary Key"))); // buff->insert("len", KexiProperty("len", QVariant(200), i18n("Length"))); // m_fields.insert(i, buff); KexiTableItem *item = new KexiTableItem(columns); d->data->append(item); } d->dataTable->tableView()->setData(d->data); updateColumsData(); } void KexiQueryDesignerGuiEditor::updateColumsData() { -// d->fieldColumnData d->dataTable->tableView()->acceptRowEdit(); QStringList sortedTableNames; for (TablesDictIterator it(*d->relations->tables());it.current();++it) sortedTableNames += it.current()->table()->name(); qHeapSort( sortedTableNames ); //several tables can be hidden now, so remove rows for these tables QValueList rowsToDelete; for (int r = 0; r<(int)d->buffers->size(); r++) { KexiPropertyBuffer *buf = d->buffers->at(r); if (buf) { QString tableName = (*buf)["table"].value().toString(); if (sortedTableNames.end() == qFind( sortedTableNames.begin(), sortedTableNames.end(), tableName )) { //table not found: mark this line for later remove rowsToDelete += r; -// buf->add(new KexiProperty("_rm", 0) ); } } } d->data->deleteRows( rowsToDelete ); //update 'table' and 'field' columns d->tablesColumnData->deleteAllRows(); d->fieldColumnData->deleteAllRows(); // d->tablesColumnData->clear(); // d->fieldColumnData->clear(); - for (QStringList::Iterator it = sortedTableNames.begin(); it!=sortedTableNames.end(); ++it) { + for (QStringList::const_iterator it = sortedTableNames.constBegin(); it!=sortedTableNames.constEnd(); ++it) { //table KexiDB::TableSchema *table = d->relations->tables()->find(*it)->table(); // for (TablesDictIterator it(*d->relations->tables());it.current();++it) { KexiTableItem *item = new KexiTableItem(2); (*item)[0]=table->name(); (*item)[1]=(*item)[0]; d->tablesColumnData->append( item ); //field item = new KexiTableItem(2); (*item)[0]=table->name()+".*"; (*item)[1]=(*item)[0]; d->fieldColumnData->append( item ); for (KexiDB::Field::ListIterator t_it = table->fieldsIterator();t_it.current();++t_it) { item = new KexiTableItem(2); (*item)[0]=table->name()+"."+t_it.current()->name(); (*item)[1]=QString(" ") + t_it.current()->name(); d->fieldColumnData->append( item ); } } //TODO } KexiRelationWidget *KexiQueryDesignerGuiEditor::relationView() const { return d->relations; } void KexiQueryDesignerGuiEditor::addRow(const QString &tbl, const QString &field) { kdDebug() << "KexiQueryDesignerGuiEditor::addRow(" << tbl << ", " << field << ")" << endl; KexiTableItem *item = new KexiTableItem(0); // = QVariant(tbl); item->push_back(QVariant(tbl)); item->push_back(QVariant(field)); item->push_back(QVariant(true)); item->push_back(QVariant()); d->data->append(item); //TODO: this should deffinitly not go here :) // m_table->updateContents(); setDirty(true); } KexiQueryPart::TempData * KexiQueryDesignerGuiEditor::tempData() const { return static_cast(parentDialog()->tempData()); } static QString msgCannotSwitch_EmptyDesign() { return i18n("Cannot switch to data view, because query design is empty.\n" "First, please create your design."); } bool KexiQueryDesignerGuiEditor::buildSchema(QString *errMsg) { //build query schema KexiQueryPart::TempData * temp = tempData(); if (temp->query) temp->query->clear(); else temp->query = new KexiDB::QuerySchema(); //add tables for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) { temp->query->addTable( it.current()->table() ); } //add relations (looking for connections) for (ConnectionListIterator it(*d->relations->connections()); it.current(); ++it) { KexiRelationViewTableContainer *masterTable = it.current()->masterTable(); KexiRelationViewTableContainer *detailsTable = it.current()->detailsTable(); temp->query->addRelationship( masterTable->table()->field(it.current()->masterField()), detailsTable->table()->field(it.current()->detailsField()) ); } //add fields bool fieldsFound = false; for (int i=0; i<(int)d->buffers->size(); i++) { KexiPropertyBuffer *buf = d->buffers->at(i); if (buf) { QString tableName = (*buf)["table"].value().toString().stripWhiteSpace(); if (tableName.isEmpty()) { //expresion? //TODO } else { KexiDB::TableSchema *t = d->conn->tableSchema(tableName); if (!t) { kdWarning() << "query designer: NO TABLE '" << (*buf)["table"].value().toString() << "'" << endl; continue; } QString fieldName = (*buf)["field"].value().toString(); bool fieldVisible = (*buf)["visible"].value().toBool(); if (fieldName=="*") { //all tables asterisk temp->query->addAsterisk( new KexiDB::QueryAsterisk( temp->query, 0 ), fieldVisible ); fieldsFound = true; } else if (fieldName.find(".*")!=-1) { //single-table asterisk: + ".*" + number temp->query->addAsterisk( new KexiDB::QueryAsterisk( temp->query, t ), fieldVisible ); } else { KexiDB::Field *f = t->field( fieldName ); if (!f) { kdWarning() << "query designer: NO FIELD '" << fieldName << "'" << endl; continue; } temp->query->addField(f, fieldVisible); fieldsFound = true; } } } } if (!fieldsFound) { if (errMsg) *errMsg = msgCannotSwitch_EmptyDesign(); return false; } //TODO return true; } tristate KexiQueryDesignerGuiEditor::beforeSwitchTo(int mode, bool &dontStore) { kdDebug() << "KexiQueryDesignerGuiEditor::beforeSwitch()" << mode << endl; if (mode==Kexi::DesignViewMode) { //todo return true; } else if (mode==Kexi::DataViewMode) { if (!dirty() && parentDialog()->neverSaved()) { KMessageBox::information(this, msgCannotSwitch_EmptyDesign()); return cancelled; } if (dirty() || !tempData()->query) { //remember current design in a temporary structure dontStore=true; QString errMsg; //build schema; problems are not allowed if (!buildSchema(&errMsg)) { KMessageBox::information(this, errMsg); return cancelled; } } //TODO return true; } else if (mode==Kexi::TextViewMode) { dontStore=true; //build schema; ignore problems buildSchema(); /* if (tempData()->query && tempData()->query->fieldCount()==0) { //no fields selected: let's add "*" (all-tables asterisk), // otherwise SQL statement will be invalid tempData()->query->addAsterisk( new KexiDB::QueryAsterisk( tempData()->query ) ); }*/ //todo return true; } return false; } tristate KexiQueryDesignerGuiEditor::afterSwitchFrom(int mode) { if (mode==Kexi::NoViewMode || (mode==Kexi::DataViewMode && !tempData()->query)) { //this is not a SWITCH but fresh opening in this view mode if (!m_dialog->neverSaved()) { if (!loadLayout()) { //err msg parentDialog()->setStatus(parentDialog()->mainWin()->project()->dbConnection(), i18n("Query definition loading failed."), i18n("Query data may be corrupted.")); return false; } showFieldsForQuery( static_cast(parentDialog()->schemaData()) ); //todo: load global query properties } } else if (mode==Kexi::TextViewMode) { if (tempData()->queryChangedInPreviousView) { //previous view changed query data //-clear and regenerate GUI items d->relations->clear(); initTableRows(); //todo if (tempData()->query) { //there is a query schema to show showTablesAndConnectionsForQuery( tempData()->query ); //-show fields showFieldsForQuery( tempData()->query ); } } //todo: load global query properties } else if (mode==Kexi::DataViewMode) { d->dataTable->tableView()->ensureCellVisible(0,0); d->dataTable->tableView()->setCursor(0,0); } tempData()->queryChangedInPreviousView = false; return true; } KexiDB::SchemaData* KexiQueryDesignerGuiEditor::storeNewData(const KexiDB::SchemaData& sdata, bool &/*cancel*/) { buildSchema(); KexiQueryPart::TempData * temp = tempData(); (KexiDB::SchemaData&)*temp->query = sdata; //copy main attributes bool ok = m_mainWin->project()->dbConnection()->storeObjectSchemaData( *temp->query, true /*newObject*/ ); m_dialog->setId( temp->query->id() ); if (ok) ok = storeLayout(); KexiDB::QuerySchema *query = temp->query; temp->query = 0; //will be returned, so: don't keep it if (!ok) { delete query; return 0; } return query; } tristate KexiQueryDesignerGuiEditor::storeData() { tristate res = KexiViewBase::storeData(); if (~res) return res; if (res) { buildSchema(); res = storeLayout(); } return res; } void KexiQueryDesignerGuiEditor::showTablesAndConnectionsForQuery(KexiDB::QuerySchema *query) { d->relations->clear(); d->slotTableAdded_enabled = false; //speedup //-show tables: //(todo: instead of hiding all tables and showing some tables, // show only these new and hide these unncecessary; the same for connections) for (KexiDB::TableSchema::ListIterator it(*query->tables()); it.current(); ++it) { d->relations->addTable( it.current() ); } //-show relationships: KexiDB::Relationship *rel; for (KexiDB::Relationship::ListIterator it(*query->relationships()); (rel=it.current()); ++it) { SourceConnection conn; //@todo: now only sigle-field relationships are implemented! KexiDB::Field *masterField = rel->masterIndex()->fields()->first(); KexiDB::Field *detailsField = rel->detailsIndex()->fields()->first(); conn.masterTable = masterField->table()->name(); //<<name(); conn.detailsTable = detailsField->table()->name(); conn.detailsField = detailsField->name(); d->relations->addConnection( conn ); } d->slotTableAdded_enabled = true; updateColumsData(); } void KexiQueryDesignerGuiEditor::showFieldsForQuery(KexiDB::QuerySchema *query) { const bool was_dirty = dirty(); //add fields uint row_num = 0; KexiDB::Field *field; for (KexiDB::Field::ListIterator it(*query->fields()); (field = it.current()); ++it, row_num++) { //add row QString tableName, fieldName, columnAlias; if (field->isQueryAsterisk()) { if (field->table()) {//single-table asterisk tableName = field->table()->name(); fieldName = "*"; } else {//all-tables asterisk tableName = "*"; fieldName = ""; } } else { columnAlias = query->columnAlias(row_num); if (field->isExpression()) { // if (columnAlias.isEmpty()) { // columnAlias = i18n("expression", "expr%1").arg(row_num); //TODO // } fieldName = field->expression()->toString(); } else { tableName = field->table()->name(); fieldName = field->name(); } } KexiTableItem *newItem = createNewRow(tableName, fieldName); //, columnAlias); d->dataTable->tableView()->insertItem(newItem, row_num); //create buffer createPropertyBuffer( row_num, tableName, fieldName, true/*new one*/ ); KexiPropertyBuffer &buf = *propertyBuffer(); if (!columnAlias.isEmpty()) {//add alias buf["alias"].setValue(columnAlias, false); } } propertyBufferSwitched(); if (!was_dirty) setDirty(false); d->dataTable->tableView()->ensureCellVisible(0,0); } bool KexiQueryDesignerGuiEditor::loadLayout() { QString xml; // if (!loadDataBlock( xml, "query_layout" )) { loadDataBlock( xml, "query_layout" ); //TODO errmsg // return false; // } if (xml.isEmpty()) { //in a case when query layout was not saved, build layout by hand showTablesAndConnectionsForQuery( static_cast(parentDialog()->schemaData()) ); return true; } QDomDocument doc; doc.setContent(xml); QDomElement doc_el = doc.documentElement(), el; if (doc_el.tagName()!="query_layout") { //TODO errmsg return false; } const bool was_dirty = dirty(); //add tables and relations to the relation view for (el = doc_el.firstChild().toElement(); !el.isNull(); el=el.nextSibling().toElement()) { if (el.tagName()=="table") { KexiDB::TableSchema *t = d->conn->tableSchema(el.attribute("name")); int x = el.attribute("x","-1").toInt(); int y = el.attribute("y","-1").toInt(); int width = el.attribute("width","-1").toInt(); int height = el.attribute("height","-1").toInt(); QRect rect; if (x!=-1 || y!=-1 || width!=-1 || height!=-1) rect = QRect(x,y,width,height); d->relations->addTable( t, rect ); } else if (el.tagName()=="conn") { SourceConnection src_conn; src_conn.masterTable = el.attribute("mtable"); src_conn.masterField = el.attribute("mfield"); src_conn.detailsTable = el.attribute("dtable"); src_conn.detailsField = el.attribute("dfield"); d->relations->addConnection(src_conn); } } if (!was_dirty) setDirty(false); return true; } bool KexiQueryDesignerGuiEditor::storeLayout() { KexiQueryPart::TempData * temp = tempData(); QString sqlText = mainWin()->project()->dbConnection()->selectStatement( *temp->query ); if (!storeDataBlock( sqlText, "sql" )) { return false; } //serialize detailed XML query definition QString xml = "", tmp; for (TablesDictIterator it(*d->relations->tables()); it.current(); ++it) { KexiRelationViewTableContainer *table_cont = it.current(); tmp = QString("table()->name()+"\" x=\"" +QString::number(table_cont->x()) +"\" y=\""+QString::number(table_cont->y()) +"\" width=\""+QString::number(table_cont->width()) +"\" height=\""+QString::number(table_cont->height()) +"\"/>"; xml += tmp; } KexiRelationViewConnection *con; for (ConnectionListIterator it(*d->relations->connections()); (con = it.current()); ++it) { tmp = QString("masterTable()->table()->name() + "\" mfield=\"" + con->masterField() + "\" dtable=\"" + con->detailsTable()->table()->name() + "\" dfield=\"" + con->detailsField() + "\"/>"; xml += tmp; } xml += ""; if (!storeDataBlock( xml, "query_layout" )) { return false; } return true; } QSize KexiQueryDesignerGuiEditor::sizeHint() const { QSize s1 = d->relations->sizeHint(); QSize s2 = d->head->sizeHint(); return QSize(QMAX(s1.width(),s2.width()), s1.height()+s2.height()); } #if 0 KexiTableItem* KexiQueryDesignerGuiEditor::createNewRow(const QString& tableName, const QString& fieldName) const { return createNewRowInternal(tableName+"."+fieldName, tableName); } KexiTableItem* KexiQueryDesignerGuiEditor::createNewRow(const KexiDB::Field &exprField) const { exprField. return createNewRowInternal(tableName+"."+fieldName, tableName); } #endif KexiTableItem* KexiQueryDesignerGuiEditor::createNewRow(const QString& tableName, const QString& fieldName) const { KexiTableItem *newItem = new KexiTableItem(d->data->columnsCount()); QString key; // if (!alias.isEmpty()) // key=alias+": "; if (!tableName.isEmpty()) key = (tableName+"."); key += fieldName; (*newItem)[0]=key; (*newItem)[1]=tableName; (*newItem)[2]=QVariant(true,1);//visible (*newItem)[3]=QVariant(0);//totals return newItem; } void KexiQueryDesignerGuiEditor::slotDragOverTableRow(KexiTableItem * /*item*/, int /*row*/, QDragMoveEvent* e) { if (e->provides("kexi/field")) { e->acceptAction(true); } } void KexiQueryDesignerGuiEditor::slotDroppedAtRow(KexiTableItem * /*item*/, int /*row*/, QDropEvent *ev, KexiTableItem*& newItem) { //TODO: better check later if the source is really a table QString srcTable; QString srcField; QString dummy; KexiFieldDrag::decode(ev,dummy,srcTable,srcField); //insert new row at specific place newItem = createNewRow(srcTable, srcField); d->droppedNewItem = newItem; d->droppedNewTable = srcTable; d->droppedNewField = srcField; //TODO } void KexiQueryDesignerGuiEditor::slotRowInserted(KexiTableItem* item, uint row, bool /*repaint*/) { if (d->droppedNewItem && d->droppedNewItem==item) { createPropertyBuffer( row, d->droppedNewTable, d->droppedNewField, true ); propertyBufferSwitched(); d->droppedNewItem=0; } } void KexiQueryDesignerGuiEditor::slotTableAdded(KexiDB::TableSchema & /*t*/) { if (!d->slotTableAdded_enabled) return; updateColumsData(); setDirty(); } void KexiQueryDesignerGuiEditor::slotTableHidden(KexiDB::TableSchema & /*t*/) { updateColumsData(); setDirty(); } void KexiQueryDesignerGuiEditor::slotBeforeCellChanged(KexiTableItem *item, int colnum, QVariant newValue, KexiDB::ResultInfo* /*result*/) { if (colnum==0) {//'field' if (newValue.isNull()) { d->data->updateRowEditBuffer(item, 1, QVariant(), false/*!allowSignals*/); d->data->updateRowEditBuffer(item, 2, QVariant(false,1));//invisible d->data->updateRowEditBuffer(item, 3, QVariant());//remove totals d->buffers->removeCurrentPropertyBuffer(); } else { //auto fill 'table' column QString fieldName = newValue.toString(); QString tableName = fieldName; int id = tableName.find('.'); if (id>=0) tableName = tableName.left(id); d->data->updateRowEditBuffer(item, 1, tableName, false/*!allowSignals*/); d->data->updateRowEditBuffer(item, 2, QVariant(true,1));//visible d->data->updateRowEditBuffer(item, 3, QVariant(0));//totals if (!propertyBuffer()) { createPropertyBuffer( d->dataTable->tableView()->currentRow(), tableName, fieldName, true ); propertyBufferSwitched(); } //update property if (propertyBuffer()) { KexiPropertyBuffer &buf = *propertyBuffer(); buf["field"] = fieldName.mid(id+1); buf["alias"] = QString::null; buf["caption"] = QString::null; buf["table"] = tableName; } } } else if (colnum==1) {//'table' if (newValue.isNull()) { if (!item->at(0).toString().isEmpty()) d->data->updateRowEditBuffer(item, 0, QVariant(), false/*!allowSignals*/); d->data->updateRowEditBuffer(item, 2, QVariant(false,1));//invisible d->data->updateRowEditBuffer(item, 3, QVariant());//remove totals d->buffers->removeCurrentPropertyBuffer(); } //update property if (propertyBuffer()) { KexiPropertyBuffer &buf = *propertyBuffer(); buf["table"] = newValue; buf["caption"] = QString::null; } } else if (colnum==2) {//'visible' if (!propertyBuffer()) { createPropertyBuffer( d->dataTable->tableView()->currentRow(), item->at(1).toString(), item->at(0).toString(), true ); d->data->updateRowEditBuffer(item, 3, QVariant(0));//totals propertyBufferSwitched(); } if (propertyBuffer()) { KexiPropertyBuffer &buf = *propertyBuffer(); buf["visible"] = newValue; } } else if (colnum==3) {//'criteria' //TODO: //unused yet } else if (colnum==4) {//'totals' //TODO: //unused yet } } void KexiQueryDesignerGuiEditor::slotTablePositionChanged(KexiRelationViewTableContainer*) { setDirty(true); } void KexiQueryDesignerGuiEditor::slotAboutConnectionRemove(KexiRelationViewConnection*) { setDirty(true); } void KexiQueryDesignerGuiEditor::slotTableFieldDoubleClicked( KexiDB::TableSchema* table, const QString& fieldName ) { if (!table || (!table->field(fieldName) && fieldName!="*")) return; int row_num; //find last filled row in the GUI table for (row_num=d->buffers->size()-1; !d->buffers->at(row_num) && row_num>=0; row_num--) ; row_num++; //after //add row KexiTableItem *newItem = createNewRow(table->name(), fieldName); d->dataTable->tableView()->insertItem(newItem, row_num); d->dataTable->tableView()->setCursor(row_num, 0); //create buffer createPropertyBuffer( row_num, table->name(), fieldName, true/*new one*/ ); propertyBufferSwitched(); d->dataTable->setFocus(); } KexiPropertyBuffer *KexiQueryDesignerGuiEditor::propertyBuffer() { return d->buffers->currentPropertyBuffer(); } KexiPropertyBuffer * KexiQueryDesignerGuiEditor::createPropertyBuffer( int row, const QString& tableName, const QString& fieldName, bool newOne ) { const bool asterisk = (tableName=="*"); QString typeName = "KexiQueryDesignerGuiEditor::Column"; KexiPropertyBuffer *buff = new KexiPropertyBuffer(this, typeName); KexiProperty *prop; buff->add(prop = new KexiProperty("table", QVariant(tableName)) ); prop->setVisible(false);//always hidden buff->add(prop = new KexiProperty("field", QVariant(fieldName)) ); prop->setVisible(false);//always hidden buff->add(prop = new KexiProperty("caption", QVariant(QString::null), i18n("Caption") ) ); if (asterisk) prop->setVisible(false); buff->add(prop = new KexiProperty("alias", QVariant(QString::null), i18n("Alias")) ); if (asterisk) prop->setVisible(false); buff->add(prop = new KexiProperty("visible", QVariant(true, 4)) ); prop->setVisible(false); /*TODO: buff->add(prop = new KexiProperty("totals", QVariant(QString::null)) ); prop->setVisible(false);*/ //sorting QStringList slist, nlist; slist << "nosorting" << "ascending" << "descending"; nlist << i18n("None") << i18n("Ascending") << i18n("Descending"); buff->add(prop = new KexiProperty("sorting", slist[0], slist, nlist, i18n("Sorting"))); d->buffers->insert(row, buff, newOne); return buff; } #include "kexiquerydesignerguieditor.moc" diff --git a/kexi/plugins/relations/kexirelationmaindlg.cpp b/kexi/plugins/relations/kexirelationmaindlg.cpp index 13dc0d5f68..9ff187df68 100644 --- a/kexi/plugins/relations/kexirelationmaindlg.cpp +++ b/kexi/plugins/relations/kexirelationmaindlg.cpp @@ -1,81 +1,81 @@ /* This file is part of the KDE project Copyright (C) 2004 Lucijan Busch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kexirelationmaindlg.h" #include #include #include #include #include #include "keximainwindow.h" #include "kexiproject.h" #include "kexirelationwidget.h" #include "kexirelationview.h" KexiRelationMainDlg::KexiRelationMainDlg(KexiMainWindow *mainWin, QWidget *parent, const char *name) : KexiViewBase(mainWin, parent, name) { kdDebug() << "KexiRelationMainDlg()" << endl; // setIcon(SmallIcon("relation")); m_defaultIconName = "relation"; setCaption( i18n("Relationships") ); // setDocID( win->generatePrivateDocID() ); m_rel = new KexiRelationWidget(mainWin, this); //the view can receive some our actions addActionProxyChild( m_rel ); // addActionProxyChild( m_view->relationView() ); QVBoxLayout *g = new QVBoxLayout(this); g->addWidget(m_rel); //show all tables KexiDB::Connection *conn = mainWin->project()->dbConnection(); QStringList tables = conn->tableNames(); - for (QStringList::Iterator it = tables.begin(); it!=tables.end(); ++it) { + for (QStringList::ConstIterator it = tables.constBegin(); it!=tables.constEnd(); ++it) { m_rel->addTable( *it ); } } KexiRelationMainDlg::~KexiRelationMainDlg() { } QSize KexiRelationMainDlg::sizeHint() const { return QSize(600,300); } QWidget* KexiRelationMainDlg::mainWidget() { return m_rel; } QString KexiRelationMainDlg::itemIcon() { return "relation"; } #include "kexirelationmaindlg.moc" diff --git a/kexi/tableview/kexitableviewdata.cpp b/kexi/tableview/kexitableviewdata.cpp index 79e75f7aad..7736519e73 100644 --- a/kexi/tableview/kexitableviewdata.cpp +++ b/kexi/tableview/kexitableviewdata.cpp @@ -1,679 +1,679 @@ /* This file is part of the KDE project Copyright (C) 2002 Lucijan Busch Daniel Molkentin Copyright (C) 2003-2004 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Original Author: Till Busch Original Project: buX (www.bux.at) */ #include "kexitableviewdata.h" #include "kexivalidator.h" #include #include #include #include #include #include #include unsigned short KexiTableViewData::charTable[]= { #include "chartable.txt" }; KexiTableViewColumn::KexiTableViewColumn(KexiDB::Field& f, bool owner) : fieldinfo(0) , m_field(&f) { isDBAware = false; m_fieldOwned = owner; m_captionAliasOrName = m_field->captionOrName(); init(); } KexiTableViewColumn::KexiTableViewColumn(const QString& name, KexiDB::Field::Type ctype, uint cconst, uint options, uint length, uint precision, QVariant defaultValue, const QString& caption, const QString& description, uint width ) : fieldinfo(0) { m_field = new KexiDB::Field( name, ctype, cconst, options, length, precision, defaultValue, caption, description, width); isDBAware = false; m_fieldOwned = true; m_captionAliasOrName = m_field->captionOrName(); init(); } KexiTableViewColumn::KexiTableViewColumn( const KexiDB::QuerySchema &query, KexiDB::QueryColumnInfo& fi) // const KexiDB::QuerySchema &query, KexiDB::Field& f) : fieldinfo(&fi) , m_field(fi.field) { isDBAware = true; m_fieldOwned = false; //setup column's caption: if (!fieldinfo->field->caption().isEmpty()) { m_captionAliasOrName = fieldinfo->field->caption(); } else { //reuse alias if available: m_captionAliasOrName = fieldinfo->alias; //last hance: use field name if (m_captionAliasOrName.isEmpty()) m_captionAliasOrName = fieldinfo->field->name(); //todo: compute other auto-name? } init(); //setup column's readonly flag: true if this is parent table's field m_readOnly = (query.parentTable()!=fieldinfo->field->table()); // m_visible = query.isFieldVisible(&f); } KexiTableViewColumn::KexiTableViewColumn(bool) : fieldinfo(0) , m_field(0) { isDBAware = false; init(); } KexiTableViewColumn::~KexiTableViewColumn() { if (m_fieldOwned) delete m_field; setValidator( 0 ); delete m_relatedData; } void KexiTableViewColumn::init() { m_relatedData = 0; m_readOnly = false; m_visible = true; m_data = 0; m_validator = 0; } void KexiTableViewColumn::setValidator( KexiValidator* v ) { if (m_validator) {//remove old one if (!m_validator->parent()) //destroy if has no parent delete m_validator; } m_validator = v; } void KexiTableViewColumn::setRelatedData(KexiTableViewData *data) { if (isDBAware) return; if (m_relatedData) delete m_relatedData; m_relatedData = 0; if (!data) return; //find a primary key KexiTableViewColumn::ListIterator it( data->columns ); for (int id = 0;it.current();++it, id++) { if (it.current()->field()->isPrimaryKey()) { //found, remember m_relatedDataPKeyID = id; m_relatedData = data; return; } } } bool KexiTableViewColumn::acceptsFirstChar(const QChar& ch) const { if (m_field->isNumericType()) { if (ch=="-") return !m_field->isUnsigned(); if (ch=="+" || (ch>="0" && ch<="9")) return true; return false; } switch (m_field->type()) { case KexiDB::Field::Boolean: return false; case KexiDB::Field::Date: case KexiDB::Field::DateTime: case KexiDB::Field::Time: return ch>="0" && ch<="9"; default:; } return true; } //------------------------------------------------------ KexiTableViewData::KexiTableViewData() : QObject() , KexiTableViewDataBase() { init(); } KexiTableViewData::KexiTableViewData(KexiDB::Cursor *c) : QObject() , KexiTableViewDataBase() { init(); m_cursor = c; KexiDB::QueryColumnInfo::Vector vector = m_cursor->query()->fieldsExpanded(); KexiTableViewColumn* col; const uint count = vector.count(); for (uint i=0;ivisible) { col=new KexiTableViewColumn(*m_cursor->query(), *fi); //col->setVisible( detailedVisibility[i] ); addColumn( col ); } } } KexiTableViewData::KexiTableViewData( const QValueList &keys, const QValueList &values, KexiDB::Field::Type keyType, KexiDB::Field::Type valueType) : QObject() , KexiTableViewDataBase() { init(keys, values, keyType, valueType); } KexiTableViewData::KexiTableViewData( KexiDB::Field::Type keyType, KexiDB::Field::Type valueType) { const QValueList empty; init(empty, empty, keyType, valueType); } KexiTableViewData::~KexiTableViewData() { emit destroying(); } void KexiTableViewData::init( const QValueList &keys, const QValueList &values, KexiDB::Field::Type keyType, KexiDB::Field::Type valueType) { init(); KexiDB::Field *keyField = new KexiDB::Field("key", keyType); keyField->setPrimaryKey(true); KexiTableViewColumn *keyColumn = new KexiTableViewColumn(*keyField, true); keyColumn->setVisible(false); addColumn(keyColumn); KexiDB::Field *valueField = new KexiDB::Field("value", valueType); KexiTableViewColumn *valueColumn = new KexiTableViewColumn(*valueField, true); addColumn(valueColumn); uint cnt = QMIN(keys.count(), values.count()); QValueList::ConstIterator it_keys = keys.constBegin(); QValueList::ConstIterator it_values = values.constBegin(); for (;cnt>0;++it_keys, ++it_values, cnt--) { KexiTableItem *item = new KexiTableItem(2); (*item)[0] = (*it_keys); (*item)[1] = (*it_values); append( item ); } } /* KexiTableViewData::KexiTableViewData(KexiTableViewColumnList& cols) : KexiTableViewDataBase() , columns(cols) , m_key(0) , m_order(1) , m_type(1) , m_pRowEditBuffer(0) , m_readOnly(false) , m_insertingEnabled(true) { setAutoDelete(true); columns.setAutoDelete(true); }*/ void KexiTableViewData::init() { m_key = 0; m_order = 1; m_type = 1; m_pRowEditBuffer = 0; m_cursor = 0; m_readOnly = false; m_insertingEnabled = true; setAutoDelete(true); columns.setAutoDelete(true); m_visibleColumnsCount=0; m_visibleColumnsIDs.resize(100); m_globalColumnsIDs.resize(100); m_autoIncrementedColumn = -2; } void KexiTableViewData::addColumn( KexiTableViewColumn* col ) { // if (!col->isDBAware) { // if (!m_simpleColumnsByName) // m_simpleColumnsByName = new QDict(101); // m_simpleColumnsByName->insert(col->caption,col);//for faster lookup // } columns.append( col ); col->m_data = this; if (m_globalColumnsIDs.size() < columns.count()) {//sanity m_globalColumnsIDs.resize( m_globalColumnsIDs.size()*2 ); } if (col->visible()) { m_visibleColumnsCount++; if (m_visibleColumnsIDs.size() < m_visibleColumnsCount) {//sanity m_visibleColumnsIDs.resize( m_visibleColumnsIDs.size()*2 ); } m_visibleColumnsIDs[ columns.count()-1 ] = m_visibleColumnsCount-1; m_globalColumnsIDs[ m_visibleColumnsCount-1 ] = columns.count()-1; } else { m_visibleColumnsIDs[ columns.count()-1 ] = -1; } m_autoIncrementedColumn = -2; //clear cache; } /*void KexiTableViewData::addColumns( KexiDB::QuerySchema *query, KexiDB::Field *field ) { field->isQueryAsterisk }*/ QString KexiTableViewData::dbTableName() const { if (m_cursor && m_cursor->query() && m_cursor->query()->parentTable()) return m_cursor->query()->parentTable()->name(); return QString::null; } void KexiTableViewData::setSorting(int column, bool ascending) { m_order = (ascending ? 1 : -1); if (column>=0 && column<(int)columns.count()) { m_key = column; } else { m_key = -1; return; } const int t = columns.at(m_key)->field()->type(); if (t == KexiDB::Field::Boolean || KexiDB::Field::isNumericType(t)) cmpFunc = &KexiTableViewData::cmpInt; else cmpFunc = &KexiTableViewData::cmpStr; } int KexiTableViewData::compareItems(Item item1, Item item2) { return ((this->*cmpFunc) (item1, item2)); } int KexiTableViewData::cmpInt(Item item1, Item item2) { return m_order* (((KexiTableItem *)item1)->at(m_key).toInt() - ((KexiTableItem *)item2)->at(m_key).toInt()); } int KexiTableViewData::cmpStr(Item item1, Item item2) { const QString &as =((KexiTableItem *)item1)->at(m_key).toString(); const QString &bs =((KexiTableItem *)item2)->at(m_key).toString(); const QChar *a = as.unicode(); const QChar *b = bs.unicode(); if ( a == b ) return 0; if ( a == 0 ) return 1; if ( b == 0 ) return -1; unsigned short au; unsigned short bu; int l=QMIN(as.length(),bs.length()); au = a->unicode(); bu = b->unicode(); au = (au <= 0x17e ? charTable[au] : 0xffff); bu = (bu <= 0x17e ? charTable[bu] : 0xffff); while (l-- && au == bu) { a++,b++; au = a->unicode(); bu = b->unicode(); au = (au <= 0x17e ? charTable[au] : 0xffff); bu = (bu <= 0x17e ? charTable[bu] : 0xffff); } if ( l==-1 ) return m_order*(as.length()-bs.length()); return m_order*(au-bu); } void KexiTableViewData::setReadOnly(bool set) { if (m_readOnly == set) return; m_readOnly = set; if (m_readOnly) setInsertingEnabled(false); } void KexiTableViewData::setInsertingEnabled(bool set) { if (m_insertingEnabled == set) return; m_insertingEnabled = set; if (m_insertingEnabled) setReadOnly(false); } void KexiTableViewData::clearRowEditBuffer() { //init row edit buffer if (!m_pRowEditBuffer) m_pRowEditBuffer = new KexiDB::RowEditBuffer(isDBAware()); else m_pRowEditBuffer->clear(); } /*bool KexiTableViewData::isDBAware() { return m_cursor!=0; }*/ bool KexiTableViewData::updateRowEditBuffer(KexiTableItem *item, int colnum, QVariant newval, bool allowSignals) { m_result.clear(); if (allowSignals) emit aboutToChangeCell(item, colnum, newval, &m_result); if (!m_result.success) return false; kdDebug() << "KexiTableViewData::updateRowEditBuffer() column #" << colnum << " = " << newval.toString() << endl; KexiTableViewColumn* col = columns.at(colnum); if (!col) { kdDebug() << "KexiTableViewData::updateRowEditBuffer(): column #" << colnum<<" not found! col==0" << endl; return false; } if (m_pRowEditBuffer->isDBAware()) { if (!(col->fieldinfo)) { kdDebug() << "KexiTableViewData::updateRowEditBuffer(): column #" << colnum<<" not found!" << endl; return false; } // if (!(static_cast(col)->field)) { m_pRowEditBuffer->insert( *col->fieldinfo, newval); return true; } if (!(col->field())) { kdDebug() << "KexiTableViewData::updateRowEditBuffer(): column #" << colnum<<" not found!" << endl; return false; } //not db-aware: const QString colname = col->field()->name(); if (colname.isEmpty()) { kdDebug() << "KexiTableViewData::updateRowEditBuffer(): column #" << colnum<<" not found!" << endl; return false; } m_pRowEditBuffer->insert(colname, newval); return true; } //get a new value (of present in the buffer), or the old one, otherwise //(taken here for optimization) #define GET_VALUE if (!val) { \ val = m_cursor ? rowEditBuffer()->at( *it_f.current()->fieldinfo ) : rowEditBuffer()->at( *f ); \ if (!val) \ val = &(*it_r); /* get old value */ \ } //js TODO: if there can be multiple views for this data, we need multiple buffers! bool KexiTableViewData::saveRow(KexiTableItem& item, bool insert, bool repaint) { if (!m_pRowEditBuffer) return true; //nothing to do //check constraints: //-check if every NOT NULL and NOT EMPTY field is filled KexiTableViewColumn::ListIterator it_f(columns); - KexiDB::RowData::iterator it_r = item.begin(); + KexiDB::RowData::ConstIterator it_r = item.constBegin(); int col = 0; const QVariant *val; - for (;it_f.current() && it_r!=item.end();++it_f,++it_r,col++) { + for (;it_f.current() && it_r!=item.constEnd();++it_f,++it_r,col++) { KexiDB::Field *f = it_f.current()->field(); val = 0; if (f->isNotNull()) { GET_VALUE; //check it if (val->isNull() && !f->isAutoIncrement()) { //NOT NULL violated m_result.msg = i18n("\"%1\" column requires a value to be entered.") .arg(f->captionOrName()) + "\n\n" + KexiValidator::msgYouCanImproveData(); m_result.desc = i18n("The column's constraint is declared as NOT NULL."); m_result.column = col; return false; } } if (f->isNotEmpty()) { GET_VALUE; if (!f->isAutoIncrement() && (val->isNull() || KexiDB::isEmptyValue( f, *val ))) { //NOT EMPTY violated m_result.msg = i18n("\"%1\" column requires a value to be entered.") .arg(f->captionOrName()) + "\n\n" + KexiValidator::msgYouCanImproveData(); m_result.desc = i18n("The column's constraint is declared as NOT EMPTY."); m_result.column = col; return false; } } } if (m_cursor) {//db-aware if (insert) { if (!m_cursor->insertRow( static_cast(item), *rowEditBuffer() )) { m_result.msg = i18n("Row inserting failed.") + "\n\n" + KexiValidator::msgYouCanImproveData(); KexiDB::getHTMLErrorMesage(m_cursor, &m_result); /* if (desc) *desc = js: TODO: use KexiMainWindowImpl::showErrorMessage(const QString &title, KexiDB::Object *obj) after it will be moved somewhere to kexidb (this will require moving other showErrorMessage() methods from KexiMainWindowImpl to libkexiutils....) then: just call: *desc = KexiDB::errorMessage(m_cursor); */ return false; } } else { if (!m_cursor->updateRow( static_cast(item), *rowEditBuffer() )) { m_result.msg = i18n("Row changing failed.") + "\n\n" + KexiValidator::msgYouCanImproveData(); KexiDB::getHTMLErrorMesage(m_cursor, m_result.desc); return false; } } } else {//js UNTESTED!!! - not db-aware version KexiDB::RowEditBuffer::SimpleMap b = m_pRowEditBuffer->simpleBuffer(); - for (KexiDB::RowEditBuffer::SimpleMap::Iterator it = b.begin();it!=b.end();++it) { + for (KexiDB::RowEditBuffer::SimpleMap::ConstIterator it = b.constBegin();it!=b.constEnd();++it) { uint i=0; for (KexiTableViewColumn::ListIterator it2(columns);it2.current();++it2, i++) { if (it2.current()->field()->name()==it.key()) { kdDebug() << it2.current()->field()->name()<< ": "< "<deleteRow( static_cast(item) )) { m_result.msg = i18n("Row deleting failed."); /*js: TODO: use KexiDB::errorMessage() for description (desc) as in KexiTableViewData::saveRow() */ KexiDB::getHTMLErrorMesage(m_cursor, &m_result); m_result.success = false; return false; } } if (!removeRef(&item)) { //aah - this shouldn't be! kdWarning() << "KexiTableViewData::deleteRow(): !removeRef() - IMPL. ERROR?" << endl; m_result.success = false; return false; } emit rowDeleted(); return true; } void KexiTableViewData::deleteRows( const QValueList &rowsToDelete, bool repaint ) { if (rowsToDelete.isEmpty()) return; int last_r=0; first(); - for (QValueList::const_iterator r_it = rowsToDelete.begin(); r_it!=rowsToDelete.end(); ++r_it) { + for (QValueList::ConstIterator r_it = rowsToDelete.constBegin(); r_it!=rowsToDelete.constEnd(); ++r_it) { for (; last_r<(*r_it); last_r++) { next(); } remove(); last_r++; } emit refreshRequested(); //! \todo more effective? emit rowsDeleted( rowsToDelete ); } void KexiTableViewData::insertRow(KexiTableItem& item, uint index, bool repaint) { if (!insert( index = QMIN(index, count()), &item )) return; emit rowInserted(&item, index, repaint); } //void KexiTableViewData::clear() void KexiTableViewData::clearInternal() { clearRowEditBuffer(); KexiTableViewDataBase::clear(); } bool KexiTableViewData::deleteAllRows(bool repaint) { clearInternal(); bool res = true; if (m_cursor) { //db-aware res = m_cursor->deleteAllRows(); } if (repaint) emit refreshRequested(); return res; } int KexiTableViewData::autoIncrementedColumn() { if (m_autoIncrementedColumn==-2) { //find such a column m_autoIncrementedColumn = 0; KexiTableViewColumn::ListIterator it(columns); for (; it.current(); ++it, m_autoIncrementedColumn++) { if (it.current()->field()->isAutoIncrement()) break; } if (!it.current()) m_autoIncrementedColumn = -1; } return m_autoIncrementedColumn; } #include "kexitableviewdata.moc" diff --git a/kexi/tableview/kexitableviewpropertybuffer.cpp b/kexi/tableview/kexitableviewpropertybuffer.cpp index e9e6a01940..cb72062a17 100644 --- a/kexi/tableview/kexitableviewpropertybuffer.cpp +++ b/kexi/tableview/kexitableviewpropertybuffer.cpp @@ -1,229 +1,229 @@ /* This file is part of the KDE project Copyright (C) 2004 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kexitableviewpropertybuffer.h" #include "kexiviewbase.h" #include "kexitableview.h" #include "kexitableviewdata.h" #define MAX_FIELDS 101 //nice prime number (default buffer vector size) KexiTableViewPropertyBuffer::KexiTableViewPropertyBuffer(KexiViewBase *view, KexiTableView* tableView) : QObject( view, QCString(view->name())+"KexiTableViewPropertyBuffer" ) , m_view(view) , m_tableView(tableView) , m_row(-99) { m_buffers.setAutoDelete(true); connect(m_tableView, SIGNAL(dataSet(KexiTableViewData*)), this, SLOT(slotDataSet(KexiTableViewData*))); connect(m_tableView, SIGNAL(cellSelected(int,int)), this, SLOT(slotCellSelected(int,int))); slotDataSet( m_tableView->data() ); const bool wasDirty = view->dirty(); clear(); if (!wasDirty) view->setDirty(false); } KexiTableViewPropertyBuffer::~KexiTableViewPropertyBuffer() { } void KexiTableViewPropertyBuffer::slotDataSet( KexiTableViewData *data ) { if (!m_currentTVData.isNull()) { m_currentTVData->disconnect( this ); } m_currentTVData = data; if (!m_currentTVData.isNull()) { connect(m_currentTVData, SIGNAL(rowDeleted()), this, SLOT(slotRowDeleted())); connect(m_currentTVData, SIGNAL(rowsDeleted( const QValueList & )), this, SLOT(slotRowsDeleted( const QValueList & ))); connect(m_currentTVData, SIGNAL(rowInserted(KexiTableItem*,uint,bool)), this, SLOT(slotRowInserted(KexiTableItem*,uint,bool))); connect(m_currentTVData, SIGNAL(refreshRequested()), this, SLOT(slotRefreshRequested())); } } void KexiTableViewPropertyBuffer::removeCurrentPropertyBuffer() { remove( m_tableView->currentRow() ); } void KexiTableViewPropertyBuffer::remove(uint row) { KexiPropertyBuffer *buf = m_buffers.at(row); if (!buf) return; buf->debug(); m_buffers.remove(row); m_view->setDirty(); m_view->propertyBufferSwitched(); } uint KexiTableViewPropertyBuffer::size() const { return m_buffers.size(); } void KexiTableViewPropertyBuffer::clear(uint minimumSize) { m_buffers.clear(); m_buffers.resize(QMAX(minimumSize, MAX_FIELDS)); m_view->setDirty(true); m_view->propertyBufferSwitched(); } void KexiTableViewPropertyBuffer::slotRefreshRequested() { clear(); } void KexiTableViewPropertyBuffer::insert(uint row, KexiPropertyBuffer* buf, bool newOne) { // m_buffers.remove(row);//sanity /* //let's move down all buffers that are below m_buffers.setAutoDelete(false);//to avoid auto deleting in insert() m_buffers.resize(m_buffers.size()+1); for (int i=int(m_buffers.size()-1); i>(int)row; i--) { KexiPropertyBuffer *b = m_buffers[i-1]; m_buffers.insert( i , b ); }*/ m_buffers.insert(row, buf); // m_buffers.setAutoDelete(true);//revert the flag connect(buf,SIGNAL(propertyChanged()), m_view, SLOT(setDirty())); if (newOne) { //add a special property indicating that this is brand new buffer, //not just changed KexiProperty* prop = new KexiProperty("newrow", QVariant()); prop->setVisible(false); buf->add( prop ); m_view->setDirty(); } } KexiPropertyBuffer* KexiTableViewPropertyBuffer::currentPropertyBuffer() const { return (m_tableView->currentRow() >= 0) ? m_buffers.at( m_tableView->currentRow() ) : 0; } void KexiTableViewPropertyBuffer::slotRowDeleted() { m_view->setDirty(); removeCurrentPropertyBuffer(); //let's move up all buffers that are below that deleted m_buffers.setAutoDelete(false);//to avoid auto deleting in insert() const int r = m_tableView->currentRow(); for (int i=r;ipropertyBufferSwitched(); emit rowDeleted(); } void KexiTableViewPropertyBuffer::slotRowsDeleted( const QValueList &rows ) { //let's move most buffers up & delete unwanted m_buffers.setAutoDelete(false);//to avoid auto deleting in insert() const int orig_size = size(); int prev_r = -1; int num_removed = 0, cur_r = -1; - for (QValueList::const_iterator r_it = rows.begin(); r_it!=rows.end() && *r_it < orig_size; ++r_it) { + for (QValueList::ConstIterator r_it = rows.constBegin(); r_it!=rows.constEnd() && *r_it < orig_size; ++r_it) { cur_r = *r_it;// - num_removed; if (prev_r>=0) { // kdDebug() << "move " << prev_r+num_removed-1 << ".." << cur_r-1 << " to " << prev_r+num_removed-1 << ".." << cur_r-2 << endl; int i=prev_r; KexiPropertyBuffer *b = m_buffers.take(i+num_removed); kdDebug() << "buffer " << i+num_removed << " deleted" << endl; delete b; num_removed++; for (; (i+num_removed)=0) { KexiPropertyBuffer *b = m_buffers.take(cur_r); kdDebug() << "buffer " << cur_r << " deleted" << endl; delete b; num_removed++; for (int i=prev_r; (i+num_removed)0) m_view->setDirty(); m_view->propertyBufferSwitched(); } //void KexiTableViewPropertyBuffer::slotEmptyRowInserted(KexiTableItem*, uint /*index*/) void KexiTableViewPropertyBuffer::slotRowInserted(KexiTableItem*, uint row, bool /*repaint*/) { m_view->setDirty(); //let's move down all buffers that are below m_buffers.setAutoDelete(false);//to avoid auto deleting in insert() // const int r = m_tableView->currentRow(); m_buffers.resize(m_buffers.size()+1); for (int i=int(m_buffers.size()); i>(int)row; i--) { KexiPropertyBuffer *b = m_buffers[i-1]; m_buffers.insert( i , b ); } m_buffers.insert( row, 0 ); m_buffers.setAutoDelete(true);//revert the flag m_view->propertyBufferSwitched(); emit rowInserted(); } void KexiTableViewPropertyBuffer::slotCellSelected(int, int row) { if(row == m_row) return; m_row = row; m_view->propertyBufferSwitched(); } #include "kexitableviewpropertybuffer.moc" diff --git a/kexi/tests/newapi/main.cpp b/kexi/tests/newapi/main.cpp index dd0812d816..45837007f4 100644 --- a/kexi/tests/newapi/main.cpp +++ b/kexi/tests/newapi/main.cpp @@ -1,262 +1,262 @@ /* This file is part of the KDE project Copyright (C) 2003 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; QCString prgname; QCString db_name; QCString drv_name; QCString test_name; int cursor_options = 0; bool db_name_required = true; KexiDB::ConnectionData conn_data; QGuardedPtr conn; QGuardedPtr driver; KApplication *app = 0; KInstance *instance = 0; static KCmdLineOptions options[] = { { "test ", "Available tests:\n" " cursors: test for cursors behaviour\n" " schema: test for db schema retrieving\n" " dbcreation: test for new db creation\n" " tables: test for tables creation and data\n" " inserting\n" " tableview: test for KexiDataTableView data-aware\n" " widget\n" " parser: test for parsing sql statements,\n" " returns debug string for a given\n" " sql statement or error message\n" " dr_prop: shows properties of selected driver" , 0}, { "buffered-cursors", "Optional switch :turns cursors used in any tests\n" " to be buffered", 0}, { "", " Notes:\n" "1. 'dr_prop' requires argument.\n" "2. 'parser' test requires ,\n" " and arguments\n" "3. All other tests require \n" " and arguments.\n" "4. 'tables' test automatically runs 'dbcreation'\n" " test. ( is removed if already exists.\n" "5. must be a valid kexi database\n" " e.g. created with 'tables' test.", 0}, { "+driver_name", "Driver name", 0}, { "+[db_name]", "Database name", 0}, { "+[sql_statement]", "Optional SQL statement (for parser test)", 0}, KCmdLineLastOption }; #include "dbcreation_test.h" #include "cursors_test.h" #include "schema_test.h" #include "tables_test.h" #include "tableview_test.h" #include "parser_test.h" #include "dr_prop_test.h" #define RETURN(code) \ kdDebug()<< test_name << " TEST: " << (code==0?"PASSED":"ERROR") << endl; \ return code int main(int argc, char** argv) { int minargs = 2; bool gui = false; /* if (argc < minargs) { usage(); RETURN(0); }*/ QFileInfo info=QFileInfo(argv[0]); prgname = info.baseName().latin1(); KCmdLineArgs::init(argc, argv, new KAboutData( prgname, "KexiDBTest", "0.1.2", "", KAboutData::License_GPL, "(c) 2003-2004, Kexi Team\n" "(c) 2003-2004, OpenOffice Polska Ltd.\n", "", "http://www.koffice.org/kexi", "submit@bugs.kde.org" ) ); // KCmdLineArgs::init(argc, argv, (const char*)prgname, "KexiDBTest", "", ""); KCmdLineArgs::addCmdLineOptions( options ); KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); QCStringList tests; tests << "cursors" << "schema" << "dbcreation" << "tables" << "tableview" << "parser" << "dr_prop"; if (!args->isSet("test")) { kdDebug() << "No test specified. Use --help." << endl; RETURN(1); } test_name = args->getOption("test"); if (!tests.contains(test_name)) { kdDebug() << QString("No such test \"%1\". Use --help.").arg(test_name) << endl; RETURN(1); } if (test_name=="tableview") { gui = true; } else if (test_name=="parser") { minargs = 3; } else if (test_name=="dr_prop") { minargs = 1; db_name_required = false; } if ((int)args->count()addAppDir("kexi"); } else { instance = new KInstance(prgname); } drv_name = args->arg(0); KexiDB::DriverManager manager;// = new KexiDB::DriverManager; QStringList names = manager.driverNames(); kdDebug() << "DRIVERS: " << endl; - for (QStringList::iterator it = names.begin(); it != names.end() ; ++it) + for (QStringList::ConstIterator it = names.constBegin(); it != names.constEnd() ; ++it) kdDebug() << *it << endl; if (manager.error() || names.isEmpty()) { manager.debugError(); RETURN(1); } //get driver driver = manager.driver(drv_name); if (!driver || manager.error()) { manager.debugError(); RETURN(1); } kdDebug() << "MIME type for '" << driver->name() << "': " << driver->fileDBDriverMimeType() << endl; //connection data // conn_data.host = "myhost"; // conn_data.password = "mypwd"; //open connection if (args->count() >= 2) db_name = args->arg(1); if (db_name_required && db_name.isEmpty()) { kdDebug() << prgname << ": database name?" << endl; RETURN(1); } if (!db_name.isEmpty()) { //additional switches: if (args->isSet("buffered-cursors")) { cursor_options |= KexiDB::Cursor::Buffered; } /* else { kdWarning() << "Unknown switch: '" << QCString(argv[i]) << "'" << endl; usage(); RETURN(1); } }*/ conn_data.setFileName( db_name ); conn = driver->createConnection(conn_data); if (!conn || driver->error()) { driver->debugError(); RETURN(1); } if (!conn->connect()) { conn->debugError(); RETURN(1); } } //start test: int r=0; if (test_name == "cursors") r=cursorsTest(); else if (test_name == "schema") r=schemaTest(); else if (test_name == "dbcreation") r=dbCreationTest(); else if (test_name == "tables") r=tablesTest(); else if (test_name == "tableview") r=tableViewTest(); else if (test_name == "parser") r=parserTest(args->arg(2)); else if (test_name == "dr_prop") r=drPropTest(); else { kdWarning() << "No such test: " << test_name << endl; // usage(); RETURN(1); } if (app && r==0) app->exec(); if (r) kdDebug() << "RECENT SQL STATEMENT: " << conn->recentSQLString() << endl; if (conn && !conn->disconnect()) r = 1; // kdDebug() << "!!! KexiDB::Transaction::globalcount == " << KexiDB::Transaction::globalCount() << endl; // kdDebug() << "!!! KexiDB::TransactionData::globalcount == " << KexiDB::TransactionData::globalCount() << endl; // delete manager; delete app; RETURN(r); } diff --git a/kexi/tests/newapi/mysqlcursor.cpp b/kexi/tests/newapi/mysqlcursor.cpp index 60a64bddb4..7f0e92231b 100644 --- a/kexi/tests/newapi/mysqlcursor.cpp +++ b/kexi/tests/newapi/mysqlcursor.cpp @@ -1,121 +1,121 @@ #include #include #include #include #include #include int main(int argc, char * argv[]) { KInstance instance("newapi"); KexiDB::DriverManager manager; QStringList names = manager.driverNames(); kdDebug() << "DRIVERS: " << endl; - for (QStringList::iterator it = names.begin(); it != names.end() ; ++it) + for (QStringList::ConstIterator it = names.constBegin(); it != names.constEnd() ; ++it) kdDebug() << *it << endl; if (manager.error()) { kdDebug() << manager.errorMsg() << endl; return 1; } //get driver KexiDB::Driver *driver = manager.driver("mySQL"); if (manager.error()) { kdDebug() << manager.errorMsg() << endl; return 1; } //connection data that can be later reused KexiDB::ConnectionData conn_data; conn_data.userName="root"; if (argc>1) conn_data.password=argv[1]; else conn_data.password="mysql"; conn_data.hostName="localhost"; KexiDB::Connection *conn = driver->createConnection(conn_data); if (driver->error()) { kdDebug() << driver->errorMsg() << endl; return 1; } if (!conn->connect()) { kdDebug() << conn->errorMsg() << endl; return 1; } if (!conn->useDatabase( "test" )) { kdDebug() <<"use db:"<< conn->errorMsg() << endl; return 1; } kdDebug()<<"Creating first cursor"<executeQuery("select * from Applications"); if (!c) kdDebug()<errorMsg()<executeQuery("select * from Applications"); if (!c2) kdDebug()<errorMsg()<databaseNames(); if (l.isEmpty()) kdDebug()<errorMsg()<moveNext()) { kdDebug()<<"Cursor: Value(0)"<value(0).asString()<value(1).asString()<errorMsg()<moveNext()) { kdDebug()<<"Cursor2: Value(0)"<value(0).asString()<value(1).asString()<movePrev()) { kdDebug()<<"Cursor: Value(0)"<value(0).asString()<value(1).asString()<moveNext(); kdDebug()<<"Cursor: Value(0)"<value(0).asString()<value(1).asString()<moveNext(); kdDebug()<<"Cursor: Value(0)"<value(0).asString()<value(1).asString()<movePrev(); kdDebug()<<"Cursor: Value(0)"<value(0).asString()<value(1).asString()<movePrev(); kdDebug()<<"Cursor: Value(0)"<value(0).asString()<value(1).asString()<tableSchema( "persons" ); if (t) t->debug(); t = conn->tableSchema( "cars" ); if (t) t->debug(); // conn->tableNames(); if (!conn->disconnect()) { kdDebug() << conn->errorMsg() << endl; return 1; } debug("before del"); delete conn; debug("after del"); #endif return 0; } diff --git a/kexi/tests/parser/main.cpp b/kexi/tests/parser/main.cpp index 5613881e8c..f6ee574216 100644 --- a/kexi/tests/parser/main.cpp +++ b/kexi/tests/parser/main.cpp @@ -1,103 +1,103 @@ #include #include #include #include #include #include #include #include #include #include using namespace std; QCString prgname; int main(int argc, char **argv) { kdDebug() << "main()" << endl; QFileInfo info=QFileInfo(argv[0]); prgname = info.baseName().latin1(); KInstance instance( prgname ); if (argc<2) { return 1; } QCString drv_name(argv[1]); QCString db_name = QString(argv[2]).lower().latin1(); KexiDB::DriverManager manager; // = KexiDB::DriverManager::self(); QStringList names = manager.driverNames(); kdDebug() << "DRIVERS: " << endl; - for (QStringList::iterator it = names.begin(); it != names.end() ; ++it) + for (QStringList::ConstIterator it = names.constBegin(); it != names.constEnd() ; ++it) kdDebug() << *it << endl; if (manager.error()) { kdDebug() << manager.errorMsg() << endl; return 1; } //get driver KexiDB::Driver *driver = manager.driver(drv_name); if (!driver || manager.error()) { kdDebug() << manager.errorMsg() << endl; return 1; } //connection data that can be later reused KexiDB::ConnectionData conn_data; conn_data.setFileName(db_name); KexiDB::Connection *conn = driver->createConnection(conn_data); if (!conn || driver->error()) { kdDebug() << "error: " << driver->errorMsg() << endl; return 1; } if (!conn->connect()) { kdDebug() << "error: " << conn->errorMsg() << endl; return 1; } if (!conn->useDatabase( db_name )) { kdDebug() << "error: " << conn->errorMsg() << endl; return 1; } KexiDB::Parser *parser = new KexiDB::Parser(conn); std::string cmd; while(cmd != "quit") { std::cout << "SQL> "; getline(std::cin, cmd); parser->parse(cmd.c_str()); switch(parser->operation()) { case KexiDB::Parser::OP_Error: kdDebug() << "***********************" << endl; kdDebug() << "* error *" << endl; kdDebug() << "***********************" << endl; break; case KexiDB::Parser::OP_CreateTable: { kdDebug() << "Schema of table: " << parser->table()->name() << endl; parser->table()->debug(); break; } case KexiDB::Parser::OP_Select: { kdDebug() << "Select statement: " << endl; KexiDB::QuerySchema *q = parser->query(); q->debug(); delete q; break; } default: kdDebug() << "main(): not implemented in main.cpp" << endl; } parser->clear(); } return 0; } diff --git a/kexi/widget/kexiquerydesignersqleditor.cpp b/kexi/widget/kexiquerydesignersqleditor.cpp index 336e237d2e..619b7ed89b 100644 --- a/kexi/widget/kexiquerydesignersqleditor.cpp +++ b/kexi/widget/kexiquerydesignersqleditor.cpp @@ -1,380 +1,380 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2004 Jaroslaw Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "kexiquerydesignersqleditor.h" #include #include //uncomment this to enable KTextEdit-based editor //#define KTEXTEDIT_BASED_SQL_EDITOR //TODO: detect if KTextEditor returned something, if not- force KTEXTEDIT_BASED_SQL_EDITOR option #include #include #include #include #include #include #include #include #ifdef KTEXTEDIT_BASED_SQL_EDITOR # include class KTextEdit_KexiSharedActionConnector : public KexiSharedActionConnector { public: KTextEdit_KexiSharedActionConnector( KexiActionProxy* proxy, KTextEdit *obj ) : KexiSharedActionConnector( proxy, obj ) { plugSharedAction("edit_cut", SLOT(cut())); plugSharedAction("edit_copy", SLOT(copy())); plugSharedAction("edit_paste", SLOT(paste())); plugSharedAction("edit_clear", SLOT(clear())); plugSharedAction("edit_undo", SLOT(undo())); plugSharedAction("edit_redo", SLOT(redo())); } }; #else # include # include # include # include # include # include class KTextEditor_View_KexiSharedActionConnector : public KexiSharedActionConnector { public: KTextEditor_View_KexiSharedActionConnector( KexiActionProxy* proxy, KTextEditor::View *obj ) : KexiSharedActionConnector( proxy, obj ) { QValueList actions; actions << "edit_cut" << "edit_copy" << "edit_paste" << "edit_clear" << "edit_undo" << "edit_redo"; plugSharedActionsToExternalGUI(actions, obj); } }; #endif class KexiQueryDesignerSQLEditorPrivate { public: KexiQueryDesignerSQLEditorPrivate() // : firstActivation(true) // , katepartGuiClientAdded(false) : keyEventFilter_enabled(true) {} #ifdef KTEXTEDIT_BASED_SQL_EDITOR KTextEdit *view; #else KTextEditor::Document *doc; KTextEditor::View *view; #endif // bool firstActivation : 1; // bool katepartGuiClientAdded : 1; bool keyEventFilter_enabled : 1; }; //===================== KexiQueryDesignerSQLEditor::KexiQueryDesignerSQLEditor( KexiMainWindow *mainWin, QWidget *parent, const char *name) : KexiViewBase(mainWin, parent, name) ,d(new KexiQueryDesignerSQLEditorPrivate()) { QVBoxLayout *lyr = new QVBoxLayout(this); #ifdef KTEXTEDIT_BASED_SQL_EDITOR d->view = new KTextEdit( "", QString::null, this, "sqlDoc_editor" ); //adjust font connect(d->view, SIGNAL(textChanged()), this, SIGNAL(textChanged())); QFont f("Courier"); f.setStyleStrategy(QFont::PreferAntialias); f.setPointSize(d->view->font().pointSize()); d->view->setFont( f ); //other settings d->view->setCheckSpellingEnabled(false); KTextEdit_KexiSharedActionConnector c(this, d->view); #else QFrame *fr = new QFrame(this); fr->setFrameStyle(QFrame::Sunken|QFrame::WinPanel); lyr->addWidget(fr); lyr = new QVBoxLayout(fr); lyr->setMargin( 2 ); d->doc = KTextEditor::EditorChooser::createDocument(fr, "sqlDoc"); d->view = d->doc->createView(fr, 0L); // f->resetContainer("codefolding"); // void KAction::unplug ( QWidget * w ) [virtual] // KAction *a = d->view->action("edit_cut"); // plugSharedAction("edit_cut", a, SLOT(activate())); plugSharedActionToExternalGUI("edit_cut", d->view); plugSharedActionToExternalGUI("edit_copy", d->view); plugSharedActionToExternalGUI("edit_paste", d->view); plugSharedActionToExternalGUI("edit_undo", d->view); plugSharedActionToExternalGUI("edit_redo", d->view); //part()->instanceGuiClient(viewMode())->insertChildClient(d->view); #if 0 QAsciiDict enabledActionsDict(101, true, false); static const char* enabledActions[] = { "edit_undo", "edit_redo", "edit_cut", "edit_copy", "edit_paste", "edit_select_all", "edit_find", "edit_find_next", "edit_find_prev", "edit_replace", "go_goto_line", //"view_dynamic_word_wrap", //"dynamic_word_wrap_indicators", //"view_word_wrap_marker" "view_border", "view_line_numbers", //"view_folding_markers", "tools_indent", "tools_unindent", "tools_cleanIndent", // "tools_comment", // "tools_uncomment", // "tools_join_lines", "tools_apply_wordwrap", //"set_confdlg", "folding_toplevel", 0}; for (const char** a = enabledActions; *a; a++) enabledActionsDict.insert(*a, (char*)1); QAsciiDict disabledMenusDict(17, true, false); static const char* disabledMenus[] = { "codefolding", 0 }; for (const char** m = disabledMenus; *m; m++) disabledMenusDict.insert(*m, (char*)1); if (part() && part()->instanceGuiClient(viewMode())) { part()->instanceGuiClient(viewMode())->insertChildClient(d->view); } // mainWin()->guiFactory()->addClient(d->view); KActionCollection *ac = d->view->actionCollection(); KActionPtrList alist = ac->actions(); - for (KActionPtrList::iterator it=alist.begin(); it!=alist.end(); ++it) { + for (KActionPtrList::ConstIterator it=alist.constBegin(); it!=alist.constEnd(); ++it) { if (!enabledActionsDict[(*it)->name()]) { // ac->remove(*it); (*it)->unplugAll(); } } KXMLGUIFactory *f = d->view->factory(); // KAction *a = d->view->action("codefolding"); // KAction *a = ac->action("folding_toplevel"); /* QWidget *popup =f->container("codefolding", d->view); QPopupMenu *par_popup = static_cast(popup->parentWidget()); int i; QString txt = i18n("&Code Folding"); for (i=0; icount(); i++) { if (par_popup->text(i)==txt) break; } if (icount()) { par_popup->removeItemAt(i); }*/ #if 1 //store menu popups list // QObjectList *l = mainWin->queryList( "KActionMenu" ); QObjectList *l = this->mainWin()->queryList( "QPopupMenu" ); for (QObjectListIt it( *l ); it.current(); ++it ) { kdDebug() << "name=" <name() << " cname="<className()<name(),"view")==0) { QString txt = i18n("&Code Folding"); QPopupMenu *par_popup = static_cast(it.current()); uint i; bool wasSeparator = false; for (i=0; icount(); i++) { kdDebug() << par_popup->idAt(i) <<" '" << par_popup->text( par_popup->idAt(i) ) << "'" << endl; if (par_popup->text( par_popup->idAt(i) )==txt) break; wasSeparator = par_popup->text( par_popup->idAt(i) ).isEmpty(); } if (icount()) { par_popup->setItemVisible( par_popup->idAt(i), false );//removeItemAt(i); i++; if (wasSeparator && icount()) { if (par_popup->text( par_popup->idAt(i) ).isEmpty()) { par_popup->setItemVisible( par_popup->idAt(i), false );//removeItemAt(i); } } } } /* if ( disabledMenusDict[ it.current()->name() ] ) { kdDebug() << "REMOVE" << endl; KPopupMenu *menu = static_cast(it.current()); menu->hide(); // menu->reparent(0, 0, QPoint(0,0)); // delete menu; }*/ } delete l; #endif //TODO: remove redundant separators in ALL menus #endif //0 KTextEditor::HighlightingInterface *hl = KTextEditor::highlightingInterface(d->doc); for(uint i=0; i < hl->hlModeCount(); i++) { // kdDebug() << "hlmode("<hlModeName(i) << endl; if (hl->hlModeName(i).contains("sql", false)) { hl->setHlMode(i); break; } } connect(d->doc, SIGNAL(textChanged()), this, SIGNAL(textChanged())); #endif setViewWidget(d->view); d->view->installEventFilter(this); // setFocusProxy(d->view); lyr->addWidget(d->view); // QPushButton *btnQuery = new QPushButton(i18n("&Query"), this); // btnQuery->setFlat(true); // QPushButton *btnClear = new QPushButton(i18n("&Clear"), this); // btnClear->setFlat(true); // g->addWidget(btnQuery, 0, 0); // g->addWidget(btnClear, 0, 1); // g->addMultiCellWidget(m_view, 1, 1, 0, 1); } KexiQueryDesignerSQLEditor::~KexiQueryDesignerSQLEditor() { // mainWin()->guiFactory()->removeClient(d->view); } void KexiQueryDesignerSQLEditor::updateActions(bool activated) { KexiViewBase::updateActions(activated); } void KexiQueryDesignerSQLEditor::jump(int character) { //find row and column for this character #ifdef KTEXTEDIT_BASED_SQL_EDITOR const int numRows = d->view->paragraphs(); #else KTextEditor::EditInterface *ei = KTextEditor::editInterface(d->doc); const int numRows = ei->numLines(); #endif int row = 0, col = 0; for (int ch = 0; row < numRows; row++) { #ifdef KTEXTEDIT_BASED_SQL_EDITOR const int rowLen = d->view->paragraphLength(row)+1; #else const int rowLen = ei->lineLength(row)+1; #endif if ((ch + rowLen) > character) { col = character-ch; break; } ch += rowLen; } #ifdef KTEXTEDIT_BASED_SQL_EDITOR d->view->setCursorPosition(row, col); #else KTextEditor::ViewCursorInterface *ci = KTextEditor::viewCursorInterface(d->view); ci->setCursorPositionReal(row, col); #endif } bool KexiQueryDesignerSQLEditor::eventFilter(QObject *o, QEvent *ev) { if(ev->type() == QEvent::KeyPress && o==d->view) { QKeyEvent *ke = static_cast(ev); kdDebug() << ke->key() << endl; } return false; } // === KexiQueryDesignerSQLEditor implementation using KTextEditor === #ifdef KTEXTEDIT_BASED_SQL_EDITOR # include "kexiquerydesignersqleditor_qt.cpp" #else QString KexiQueryDesignerSQLEditor::text() { KTextEditor::EditInterface *eIface = KTextEditor::editInterface(d->doc); kdDebug() << "KexiQueryDesignerSQLEditor::getText(): iface: " << eIface << " " << eIface->text() << endl; return eIface->text(); } void KexiQueryDesignerSQLEditor::setText(const QString &text) { const bool was_dirty = dirty(); KTextEditor::EditInterface *eIface = KTextEditor::editInterface(d->doc); eIface->setText(text); setDirty(was_dirty); } #endif //!KTEXTEDIT_BASED_SQL_EDITOR #include "kexiquerydesignersqleditor.moc" diff --git a/kexi/widget/pixmapcollection.cpp b/kexi/widget/pixmapcollection.cpp index 1ce78d5c9e..dba976f227 100644 --- a/kexi/widget/pixmapcollection.cpp +++ b/kexi/widget/pixmapcollection.cpp @@ -1,437 +1,437 @@ /* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pixmapcollection.h" /// Pixmap Collection PixmapCollection::PixmapCollection(const QString &collectionName, QObject *parent, const char *name) : QObject(parent, name) { m_name = collectionName; } QString PixmapCollection::addPixmapPath(const KURL &url) { QString name = url.filename(); while(m_pixmaps.contains(name)) { bool ok; int num = name.right(1).toInt(&ok, 10); if(ok) name = name.left(name.length()-1) + QString::number(num+1); else name += "2"; } m_pixmaps.insert(name, qMakePair(url.path(), 0)); return name; } QString PixmapCollection::addPixmapName(const QString &icon, int size) { QString name = icon; while(m_pixmaps.contains(name)) { bool ok; int num = name.right(1).toInt(&ok, 10); if(ok) name = name.left(name.length()-1) + QString::number(num+1); else name += "2"; } m_pixmaps.insert(name, qMakePair(icon, size)); return name; } void PixmapCollection::removePixmap(const QString &name) { m_pixmaps.remove(name); } QPixmap PixmapCollection::getPixmap(const QString &name) { if(!m_pixmaps.contains(name)) { kdDebug() << " The icon " << name << " you requested is not in the collection" << endl; return QPixmap(); } if(m_pixmaps[name].second != 0) { return kapp->iconLoader()->loadIcon(m_pixmaps[name].first, KIcon::NoGroup, m_pixmaps[name].second); } else return QPixmap(m_pixmaps[name].first); } bool PixmapCollection::contains(const QString &name) { return m_pixmaps.contains(name); } void PixmapCollection::save(QDomNode parentNode) { if(m_pixmaps.isEmpty()) return; QDomDocument domDoc = parentNode.ownerDocument(); QDomElement collection = domDoc.createElement("collection"); parentNode.appendChild(collection); - PixmapMap::Iterator it; - PixmapMap::Iterator endIt = m_pixmaps.end(); - for(it = m_pixmaps.begin(); it != endIt; ++it) + PixmapMap::ConstIterator it; + PixmapMap::ConstIterator endIt = m_pixmaps.constEnd(); + for(it = m_pixmaps.constBegin(); it != endIt; ++it) { QDomElement item = domDoc.createElement("pixmap"); collection.appendChild(item); item.setAttribute("name", it.key()); if(it.data().second != 0) item.setAttribute("size", QString::number(it.data().second)); QString text = it.data().first; QDomText textNode = domDoc.createTextNode(text); item.appendChild(textNode); } } void PixmapCollection::load(QDomNode node) { QDomDocument domDoc = node.ownerDocument(); for(QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement el = n.toElement(); QPair pair = qMakePair(el.text(), el.attribute("size").toInt()); m_pixmaps[el.attribute("name")] = pair; } } //// A dialog to load a KDE icon by its name LoadIconDialog::LoadIconDialog(QWidget *parent) : KDialogBase(parent, "loadicon_dialog", true, i18n("Load KDE Icon by Name"), Ok|Cancel, Ok, false) { QFrame *frame = makeMainWidget(); QGridLayout *l = new QGridLayout(frame, 2, 3, 0, 6); // Name input QLabel *name = new QLabel(i18n("&Name:"), frame); l->addWidget(name, 0, 0); name->setAlignment(Qt::AlignRight|Qt::AlignVCenter); m_nameInput = new KLineEdit("kexi", frame); l->addWidget(m_nameInput, 0, 1); name->setBuddy(m_nameInput); // Choose size QLabel *size = new QLabel(i18n("&Size:"), frame); l->addWidget(size, 1, 0); size->setAlignment(Qt::AlignRight|Qt::AlignVCenter); KComboBox *combo = new KComboBox(frame); l->addWidget(combo, 1, 1); size->setBuddy(combo); QStringList list; list << i18n("Small") << i18n("Medium") << i18n("Large") << i18n("Huge"); combo->insertStringList(list); combo->setCurrentItem(2); connect(combo, SIGNAL(activated(int)), this, SLOT(changeIconSize(int))); // Icon chooser button m_button = new KIconButton(frame); m_button->setIcon("kexi"); m_button->setIconSize(KIcon::SizeMedium); l->addMultiCellWidget(m_button, 0, 1, 2, 2); connect(m_button, SIGNAL(iconChanged(QString)), this, SLOT(updateIconName(QString))); connect(m_nameInput, SIGNAL(textChanged(const QString &)), this, SLOT(setIcon(const QString &))); } void LoadIconDialog::updateIconName(QString icon) { m_nameInput->setText(icon); } void LoadIconDialog::setIcon(const QString &icon) { m_button->setIcon(icon); } void LoadIconDialog::changeIconSize(int index) { int size = KIcon::SizeMedium; switch(index) { case 0: size = KIcon::SizeSmall; break; //case 1: size = KIcon::SizeSmallMedium; break; case 1: size = KIcon::SizeMedium; break; case 2: size = KIcon::SizeLarge; break; #if !defined(Q_WS_WIN) && KDE_IS_VERSION(3,1,9) case 3: size = KIcon::SizeHuge; break; #endif default:; } m_button->setIconSize(size); } int LoadIconDialog::iconSize() { return m_button->iconSize(); } QString LoadIconDialog::iconName() { return m_button->icon(); } /// Pixmap Collection Editor Dialog PixmapCollectionEditor::PixmapCollectionEditor(PixmapCollection *collection, QWidget *parent) : KDialogBase(parent, "pixcollection_dialog", true, i18n("Edit Pixmap Collection: %1").arg(collection->collectionName()), Close, Close, false) { m_collection = collection; QFrame *frame = makeMainWidget(); QHBoxLayout *l = new QHBoxLayout(frame, 0, 6); setInitialSize(QSize(400, 200), true); //// Setup the icon toolbar ///////////////// QVBoxLayout *vlayout = new QVBoxLayout(l, 3); QToolButton *newItemPath = new QToolButton(frame); newItemPath->setIconSet(BarIconSet("fileopen")); newItemPath->setTextLabel(i18n("&Add File"), true); vlayout->addWidget(newItemPath); m_buttons.insert(BNewItemPath, newItemPath); connect(newItemPath, SIGNAL(clicked()), this, SLOT(newItemByPath())); QToolButton *newItemName = new QToolButton(frame); newItemName->setIconSet(BarIconSet("icons")); newItemName->setTextLabel(i18n("&Add an icon"), true); vlayout->addWidget(newItemName); m_buttons.insert(BNewItemName, newItemName); connect(newItemName, SIGNAL(clicked()), this, SLOT(newItemByName())); QToolButton *delItem = new QToolButton(frame); delItem->setIconSet(BarIconSet("edit_remove")); delItem->setTextLabel(i18n("&Remove selected item"), true); vlayout->addWidget(delItem); m_buttons.insert(BDelItem, delItem); connect(delItem, SIGNAL(clicked()), this, SLOT(removeItem())); vlayout->addStretch(); // Setup the iconView m_iconView = new KIconView(frame, "pixcollection_iconView"); m_iconView->resize(100,100); m_iconView->setArrangement(QIconView::LeftToRight); m_iconView->setAutoArrange(true); m_iconView->setMode(KIconView::Select); l->addWidget(m_iconView); connect(m_iconView, SIGNAL(contextMenuRequested(QIconViewItem*, const QPoint&)), this, SLOT(displayMenu(QIconViewItem*, const QPoint&))); connect(m_iconView, SIGNAL(itemRenamed(QIconViewItem*, const QString &)), this, SLOT(renameCollectionItem(QIconViewItem*, const QString&))); - PixmapMap::Iterator it; - PixmapMap::Iterator endIt = collection->m_pixmaps.end(); - for(it = collection->m_pixmaps.begin(); it != endIt; ++it) + PixmapMap::ConstIterator it; + PixmapMap::ConstIterator endIt = collection->m_pixmaps.end(); + for(it = collection->m_pixmaps.constBegin(); it != endIt; ++it) createIconViewItem(it.key()); } void PixmapCollectionEditor::newItemByName() { LoadIconDialog d(parentWidget()); if(d.exec()== QDialog::Accepted) { if(d.iconName().isEmpty()) return; QString name = m_collection->addPixmapName(d.iconName(), d.iconSize()); createIconViewItem(name); } } void PixmapCollectionEditor::newItemByPath() { KURL url = KFileDialog::getImageOpenURL("::kexi", parentWidget()); if(url.isEmpty()) return; QString name = m_collection->addPixmapPath(url); createIconViewItem(name); } void PixmapCollectionEditor::removeItem() { QIconViewItem *item = m_iconView->currentItem(); if( !item ) return; int confirm = KMessageBox::questionYesNo(parentWidget(), QString("")+ i18n("Do you want to remove item \"%1\" from collection \"%2\"?") .arg(item->text()).arg(m_collection->collectionName()) + ""); if(confirm == KMessageBox::No) return; m_collection->removePixmap(item->text()); delete item; } void PixmapCollectionEditor::renameItem() { m_iconView->currentItem()->rename(); } void PixmapCollectionEditor::createIconViewItem(const QString &name) { PixmapIconViewItem *item = new PixmapIconViewItem(m_iconView, name, getPixmap(name)); item->setRenameEnabled(true); } QPixmap PixmapCollectionEditor::getPixmap(const QString &name) { QPixmap pixmap = m_collection->getPixmap(name); if((pixmap.width() <= 48) && (pixmap.height() <= 48)) return pixmap; KPixmapIO io; QImage image = io.convertToImage(pixmap); pixmap = io.convertToPixmap(image.scale(48, 48, QImage::ScaleMin)); return pixmap; } void PixmapCollectionEditor::renameCollectionItem(QIconViewItem *it, const QString &name) { PixmapIconViewItem *item = static_cast(it); if(!m_collection->m_pixmaps.contains(item->name())) return; // We just rename the collection item QPair pair = m_collection->m_pixmaps[item->name()]; m_collection->m_pixmaps.remove(item->name()); m_collection->m_pixmaps[name] = pair; item->setName(name); } void PixmapCollectionEditor::displayMenu(QIconViewItem *it, const QPoint &p) { if(!it) return; KPopupMenu *menu = new KPopupMenu(); menu->insertItem(SmallIconSet("edit"), i18n("Rename Item"), this, SLOT(renameItem())); menu->insertItem(SmallIconSet("remove"), i18n("Remove Item"), this, SLOT(removeItem())); menu->exec(p); } //// A Dialog to choose a pixmap from the PixmapCollection PixmapCollectionChooser::PixmapCollectionChooser(PixmapCollection *collection, const QString &selectedItem, QWidget *parent) : KDialogBase(parent, "pixchoose_dialog", true, i18n("Select a pixmap from %1").arg(collection->collectionName()), User1|Ok|Cancel, Ok, false, KGuiItem(i18n("Edit Collection..."))) { m_collection = collection; setInitialSize(QSize(400, 200), true); m_iconView = new KIconView(this, "pixchooser_iconView"); setMainWidget(m_iconView); m_iconView->setArrangement(QIconView::LeftToRight); m_iconView->setAutoArrange(true); m_iconView->setMode(KIconView::Select); - PixmapMap::Iterator it; - PixmapMap::Iterator endIt = collection->m_pixmaps.end(); - for(it = collection->m_pixmaps.begin(); it != endIt; ++it) + PixmapMap::ConstIterator it; + PixmapMap::ConstIterator endIt = collection->m_pixmaps.constEnd(); + for(it = collection->m_pixmaps.constBegin(); it != endIt; ++it) new PixmapIconViewItem(m_iconView, it.key(), getPixmap(it.key())); QIconViewItem *item = m_iconView->findItem(selectedItem, Qt::ExactMatch); if(item && !selectedItem.isEmpty()) m_iconView->setCurrentItem(item); } QPixmap PixmapCollectionChooser::pixmap() { QString name = m_iconView->currentItem()->text(); return m_collection->getPixmap(name); } QString PixmapCollectionChooser::pixmapName() { return m_iconView->currentItem()->text(); } QPixmap PixmapCollectionChooser::getPixmap(const QString &name) { QPixmap pixmap = m_collection->getPixmap(name); if((pixmap.width() <= 48) && (pixmap.height() <= 48)) return pixmap; // We scale the pixmap down to 48x48 to fit in the iconView KPixmapIO io; QImage image = io.convertToImage(pixmap); pixmap = io.convertToPixmap(image.scale(48, 48, QImage::ScaleMin)); return pixmap; } void PixmapCollectionChooser::slotUser1() { PixmapCollectionEditor dialog(m_collection, parentWidget()); dialog.exec(); m_iconView->clear(); - PixmapMap::Iterator it; - PixmapMap::Iterator endIt = m_collection->m_pixmaps.end(); - for(it = m_collection->m_pixmaps.begin(); it != endIt; ++it) + PixmapMap::ConstIterator it; + PixmapMap::ConstIterator endIt = m_collection->m_pixmaps.constEnd(); + for(it = m_collection->m_pixmaps.constBegin(); it != endIt; ++it) new PixmapIconViewItem(m_iconView, it.key(), getPixmap(it.key())); } #include "pixmapcollection.moc" diff --git a/kexi/widget/propertyeditor/propertyeditorlist.cpp b/kexi/widget/propertyeditor/propertyeditorlist.cpp index 8ceb6b23b3..0df4d9b946 100644 --- a/kexi/widget/propertyeditor/propertyeditorlist.cpp +++ b/kexi/widget/propertyeditor/propertyeditorlist.cpp @@ -1,314 +1,314 @@ /* This file is part of the KDE project Copyright (C) 2002 Lucijan Busch This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include "propertyeditorlist.h" #include "kexiproperty.h" PropComboBox::PropComboBox(QWidget *parent, bool multi) : KComboBox(parent) { m_listbox = 0; m_eventFilterEnabled = true; if(multi) { m_listbox = new KListBox(this); m_listbox->setSelectionMode(QListBox::Multi); setEditable(true); m_eventFilterEnabled = false; //disable filter (crashed!) setListBox(m_listbox); m_eventFilterEnabled = true; disconnect(m_listbox, 0, this, 0); connect(m_listbox, SIGNAL(selected(QListBoxItem*)), this, SLOT(updateEdit())); connect(m_listbox, SIGNAL(returnPressed(QListBoxItem *)), this, SLOT(hideList())); } } bool PropComboBox::eventFilter(QObject *o, QEvent *e) { if (!m_eventFilterEnabled) return false; if(o == lineEdit()) { if(e->type() == QEvent::KeyPress) { QKeyEvent* ev = static_cast(e); if((ev->key()==Key_Up || ev->key()==Key_Down) && ev->state()!=ControlButton) { parentWidget()->eventFilter(o, e); return true; } } } if(o==m_listbox) { if(e->type() == QEvent::Show) { QString s = lineEdit()->text(); setSelected(QStringList::split("|",s)); } } return KComboBox::eventFilter(o, e); } void PropComboBox::setSelected(const QStringList &list) { QStringList strlist(list); m_listbox->clearSelection(); - for(QStringList::iterator it = strlist.begin(); it != strlist.end(); ++it) + for(QStringList::ConstIterator it = strlist.constBegin(); it != strlist.constEnd(); ++it) { QListBoxItem *item = m_listbox->findItem(*it, Qt::ExactMatch); if(item) m_listbox->setSelected(item,true); } setEditText(list.join("|")); } QStringList PropComboBox::getSelected() { QStringList list; for(uint i=0; i < m_listbox->count(); i++) { if(m_listbox->isSelected(i)) list.append(m_listbox->text(i)); } return list; } void PropComboBox::updateEdit() { QStringList list = getSelected(); if(!list.isEmpty()) { setEditText(list.join("|")); } else setEditText(""); emit activated(1); } void PropComboBox::hideList() { m_listbox->hide(); lineEdit()->setFocus(); } //EDITOR PropertyEditorList::PropertyEditorList(QWidget *parent, KexiProperty *property, const char *name) : KexiPropertySubEditor(parent, property, name) { QHBox *box = new QHBox(this); m_combo = new PropComboBox(box, false); m_combo->setGeometry(frameGeometry()); m_combo->setEditable(true); m_combo->setInsertionPolicy(QComboBox::NoInsertion); m_combo->setAutoCompletion(true); m_combo->setMinimumSize(10, 0); // to allow the combo to be resized to a small size // TMP if(m_property->name() == "signals") { kdDebug() << "Creating a buton " << endl; m_button = new QToolButton(box); m_button->setIconSet(SmallIconSet("goto")); m_button->setFixedWidth(height()); connect(m_button, SIGNAL(clicked()), this, SLOT(itemExecuted())); } if(m_property->names() && m_property->keys()) { m_combo->insertStringList(*(m_property->names())); int idx = m_property->keys()->findIndex( property->value().asString() ); if (idx>=0) { // m_combo->setCurrentText(property->value().asString()); m_combo->setCurrentItem(idx); KCompletion *comp = m_combo->completionObject(); comp->insertItems(*(m_property->names())); } } setWidget(box); // TMP //setWidget(m_combo); connect(m_combo, SIGNAL(activated(int)), SLOT(valueChanged())); } QVariant PropertyEditorList::value() { if (!m_property->keys() || !m_property->names()) return QVariant(); int idx = m_combo->currentItem(); if (idx<0) return QVariant(); return QVariant( (*m_property->keys())[idx] ); // return QVariant(m_combo->currentText()); } void PropertyEditorList::setValue(const QVariant &value) { int idx = m_property->keys()->findIndex( value.toString() ); if (idx>=0) { m_combo->setCurrentItem(idx); } else { kdWarning() << "PropertyEditorList::setValue(): NO SUCH KEY! '" << value.toString() << "'" << endl; m_combo->setCurrentText(QString::null); } emit changed(this); } void PropertyEditorList::setList(QStringList l) { m_combo->insertStringList(l); } void PropertyEditorList::valueChanged() { emit changed(this); } // TMP! until we have custom editors void PropertyEditorList::itemExecuted() { m_property->execute(m_combo->currentText()); } //Multiple selection editor (for OR'ed values) PropertyEditorMultiList::PropertyEditorMultiList(QWidget *parent, KexiProperty *property, const char *name) : KexiPropertySubEditor(parent, property, name) { m_combo = new PropComboBox(this, true); m_combo->setGeometry(frameGeometry()); m_combo->setInsertionPolicy(QComboBox::NoInsertion); m_combo->setAutoCompletion(true); // if(property->list()) if(m_property->names() && m_property->keys()) { m_combo->insertStringList(*(m_property->names())); int idx = m_property->keys()->findIndex( property->value().asString() ); if (idx>=0) { m_combo->setCurrentItem(idx); KCompletion *comp = m_combo->completionObject(); comp->insertItems(*(m_property->names())); //js TODO } /*original code: m_combo->insertStringList(*(property->list())); m_combo->setSelected(property->value().asStringList()); m_combo->setEditText(property->value().toStringList().join("|")); KCompletion *comp = m_combo->completionObject(); comp->insertItems(*(property->list()));*/ } m_combo->show(); setWidget(m_combo); connect(m_combo, SIGNAL(activated(int)), SLOT(valueChanged())); } QVariant PropertyEditorMultiList::value() { return QVariant(m_combo->getSelected()); } void PropertyEditorMultiList::setValue(const QVariant &value) { m_combo->setSelected(value.toStringList()); emit changed(this); } void PropertyEditorMultiList::valueChanged() { emit changed(this); } void PropertyEditorMultiList::setList(QStringList l) { m_combo->insertStringList(l); } // Cursor Editor PropertyEditorCursor::PropertyEditorCursor(QWidget *parent, KexiProperty *property, const char *name) : PropertyEditorList(parent, property, name) { m_combo->setEditable(false); m_combo->insertItem(i18n("Arrow"), Qt::ArrowCursor); m_combo->insertItem(i18n("Up Arrow"), Qt::UpArrowCursor); m_combo->insertItem(i18n("Cross"), Qt::CrossCursor); m_combo->insertItem(i18n("Waiting"), Qt::WaitCursor); m_combo->insertItem(i18n("iBeam"), Qt::IbeamCursor); m_combo->insertItem(i18n("Size Vertical"), Qt::SizeVerCursor); m_combo->insertItem(i18n("Size Horizontal"), Qt::SizeHorCursor); m_combo->insertItem(i18n("Size Slash"), Qt::SizeBDiagCursor); m_combo->insertItem(i18n("Size Backslash"), Qt::SizeFDiagCursor); m_combo->insertItem(i18n("Size All"), Qt::SizeAllCursor); m_combo->insertItem(i18n("Blank"), Qt::BlankCursor); m_combo->insertItem(i18n("Split Vertical"), Qt::SplitVCursor); m_combo->insertItem(i18n("Split Horizontal"), Qt::SplitHCursor); m_combo->insertItem(i18n("Pointing Hand"), Qt::PointingHandCursor); m_combo->insertItem(i18n("Forbidden"), Qt::ForbiddenCursor); m_combo->insertItem(i18n("What's this"), Qt::WhatsThisCursor); m_combo->setCurrentItem(property->value().toCursor().shape()); } QVariant PropertyEditorCursor::value() { return QCursor(m_combo->currentItem()); } void PropertyEditorCursor::setValue(const QVariant &value) { m_combo->setCurrentItem(value.toCursor().shape()); emit changed(this); } #include "propertyeditorlist.moc"