diff options
Diffstat (limited to 'kexi/core')
76 files changed, 11869 insertions, 0 deletions
diff --git a/kexi/core/Makefile.am b/kexi/core/Makefile.am new file mode 100644 index 00000000..4a69feda --- /dev/null +++ b/kexi/core/Makefile.am @@ -0,0 +1,41 @@ +include $(top_srcdir)/kexi/Makefile.global + +lib_LTLIBRARIES = libkexicore.la +libkexicore_la_SOURCES = kexi_global.cpp kexi.cpp kexiaboutdata.cpp \ + keximainwindow.cpp \ + kexidbconnectionset.cpp kexiprojectset.cpp \ + kexiactionproxy.cpp kexiactioncategories.cpp kexisharedactionhost.cpp \ + kexiproject.cpp kexidialogbase.cpp kexiviewbase.cpp \ + kexipartmanager.cpp kexipartinfo.cpp kexipartitem.cpp kexipart.cpp \ + kexiprojectdata.cpp kexiinternalpart.cpp \ + kexidragobjects.cpp \ + kexiuseraction.cpp kexiuseractionmethod.cpp \ + kexistartupdata.cpp kexiguimsghandler.cpp kexitextmsghandler.cpp \ + kexidataiteminterface.cpp kexievents.cpp \ + kexidbshortcutfile.cpp \ + kexiblobbuffer.cpp kexistaticpart.cpp \ + kexitabledesignerinterface.cpp kexisearchandreplaceiface.cpp \ + kexitemplateloader.cpp + +#kexipartdatasource.cpp + +libkexicore_la_LDFLAGS = $(KDE_RPATH) $(all_libraries) $(VER_INFO) -Wnounresolved -no-undefined + +SUBDIRS = . + +libkexicore_la_LIBADD = $(LIB_KEXI_KMDI) \ + $(top_builddir)/kexi/kexiutils/libkexiutils.la \ + $(top_builddir)/kexi/kexidb/libkexidb.la \ + $(top_builddir)/kexi/kexidb/parser/libkexidbparser.la \ + $(top_builddir)/lib/koproperty/libkoproperty.la + +INCLUDES = $(LIB_KEXI_KMDI_INCLUDES) \ + -I$(top_srcdir)/lib/kofficecore \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/kexi \ + $(all_includes) + +noinst_HEADERS = kexiactionproxy_p.h kexicontexthelp_p.h kexisharedactionhost_p.h \ + kexipartinfo_p.h + +METASOURCES = AUTO diff --git a/kexi/core/kexi.cpp b/kexi/core/kexi.cpp new file mode 100644 index 00000000..74e158f6 --- /dev/null +++ b/kexi/core/kexi.cpp @@ -0,0 +1,348 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexi.h" +#include "kexiaboutdata.h" +#include "kexicmdlineargs.h" + +#include <kexiutils/identifier.h> +#include <kexidb/msghandler.h> + +#include <qtimer.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qpixmapcache.h> +#include <qcolor.h> +#include <qfileinfo.h> + +#include <kdebug.h> +#include <kcursor.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <kiconeffect.h> +#include <ksharedptr.h> +#include <kmimetype.h> +#include <kstaticdeleter.h> +#include <kglobalsettings.h> + +using namespace Kexi; + +//! used for speedup +//! @internal +class KexiInternal : public KShared +{ + public: + KexiInternal() : KShared() + , connset(0) + , smallFont(0) + { + } + ~KexiInternal() + { + delete connset; + delete smallFont; + } + KexiDBConnectionSet* connset; + KexiProjectSet recentProjects; + KexiDBConnectionSet recentConnections; + KexiDB::DriverManager driverManager; + KexiPart::Manager partManager; + QFont *smallFont; +}; + +static KStaticDeleter<KexiInternal> Kexi_intDeleter; +KexiInternal* _int = 0; + +#define _INIT_SHARED { if (!_int) Kexi_intDeleter.setObject( _int, new KexiInternal() ); } + +KexiDBConnectionSet& Kexi::connset() +{ + _INIT_SHARED; + //delayed + if (!_int->connset) { + //load stored set data, OK? + _int->connset = new KexiDBConnectionSet(); + _int->connset->load(); + } + return *_int->connset; +} + +KexiProjectSet& Kexi::recentProjects() { + _INIT_SHARED; + return _int->recentProjects; +} + +KexiDB::DriverManager& Kexi::driverManager() +{ + _INIT_SHARED; + return _int->driverManager; +} + +KexiPart::Manager& Kexi::partManager() +{ + _INIT_SHARED; + return _int->partManager; +} + +void Kexi::deleteGlobalObjects() +{ + delete _int; +} + +//temp +bool _tempShowForms = true; +bool& Kexi::tempShowForms() { +#ifndef KEXI_FORMS_SUPPORT + _tempShowForms = false; +#endif + return _tempShowForms; +} + +bool _tempShowReports = true; +bool& Kexi::tempShowReports() { +#ifndef KEXI_REPORTS_SUPPORT + _tempShowReports = false; +#endif + return _tempShowReports; +} + +bool _tempShowMacros = true; +bool& Kexi::tempShowMacros() { +#ifndef KEXI_MACROS_SUPPORT + _tempShowMacros = false; +#endif + return _tempShowMacros; +} + +bool _tempShowScripts = true; +bool& Kexi::tempShowScripts() { +#ifndef KEXI_SCRIPTS_SUPPORT + _tempShowScripts = false; +#endif + return _tempShowScripts; +} + +//-------------------------------------------------------------------------------- + +QFont Kexi::smallFont(QWidget *init) +{ + _INIT_SHARED; + if (!_int->smallFont) { + _int->smallFont = new QFont( init->font() ); + const int wdth = KGlobalSettings::desktopGeometry(init).width(); + int size = 10 + QMAX(0, wdth - 1100) / 100; + size = QMIN( init->fontInfo().pixelSize(), size ); + _int->smallFont->setPixelSize( size ); + } + return *_int->smallFont; +} + +//-------------------------------------------------------------------------------- +QString Kexi::nameForViewMode(int m) +{ + if (m==NoViewMode) return i18n("No View"); + else if (m==DataViewMode) return i18n("Data View"); + else if (m==DesignViewMode) return i18n("Design View"); + else if (m==TextViewMode) return i18n("Text View"); + + return i18n("Unknown"); +} + +//-------------------------------------------------------------------------------- + +QString Kexi::msgYouCanImproveData() { + return i18n("You can correct data in this row or use \"Cancel row changes\" function."); +} + +//-------------------------------------------------------------------------------- + +ObjectStatus::ObjectStatus() +: msgHandler(0) +{ +} + +ObjectStatus::ObjectStatus(const QString& message, const QString& description) +: msgHandler(0) +{ + setStatus(message, description); +} + +ObjectStatus::ObjectStatus(KexiDB::Object* dbObject, const QString& message, const QString& description) +: msgHandler(0) +{ + setStatus(dbObject, message, description); +} + +ObjectStatus::~ObjectStatus() +{ + delete msgHandler; +} + +const ObjectStatus& ObjectStatus::status() const +{ + return *this; +} + +bool ObjectStatus::error() const +{ + return !message.isEmpty() + || (dynamic_cast<KexiDB::Object*>((QObject*)dbObj) && dynamic_cast<KexiDB::Object*>((QObject*)dbObj)->error()); +} + +void ObjectStatus::setStatus(const QString& message, const QString& description) +{ + this->dbObj=0; + this->message=message; + this->description=description; +} + +void ObjectStatus::setStatus(KexiDB::Object* dbObject, const QString& message, const QString& description) +{ + if (dynamic_cast<QObject*>(dbObject)) { + dbObj = dynamic_cast<QObject*>(dbObject); + } + this->message=message; + this->description=description; +} + +void ObjectStatus::setStatus(KexiDB::ResultInfo* result, const QString& message, const QString& description) +{ + if (result) { + if (message.isEmpty()) + this->message = result->msg; + else + this->message = message + " " + result->msg; + + if (description.isEmpty()) + this->description = result->desc; + else + this->description = description + " " + result->desc; + } + else + clearStatus(); +} + +void ObjectStatus::setStatus(KexiDB::Object* dbObject, KexiDB::ResultInfo* result, + const QString& message, const QString& description) +{ + if (!dbObject) + setStatus(result, message, description); + else if (!result) + setStatus(dbObject, message, description); + else { + setStatus(dbObject, message, description); + setStatus(result, this->message, this->description); + } +} + +void ObjectStatus::clearStatus() +{ + message=QString::null; + description=QString::null; +} + +QString ObjectStatus::singleStatusString() const { + if (message.isEmpty() || description.isEmpty()) + return message; + return message + " " + description; +} + +void ObjectStatus::append( const ObjectStatus& otherStatus ) { + if (message.isEmpty()) { + message = otherStatus.message; + description = otherStatus.description; + return; + } + const QString s( otherStatus.singleStatusString() ); + if (s.isEmpty()) + return; + if (description.isEmpty()) { + description = s; + return; + } + description = description + " " + s; +} + +//! @internal +class ObjectStatusMessageHandler : public KexiDB::MessageHandler +{ + public: + ObjectStatusMessageHandler(ObjectStatus *status) + : KexiDB::MessageHandler() + , m_status(status) + { + } + virtual ~ObjectStatusMessageHandler() + { + } + + virtual void showErrorMessage(const QString &title, + const QString &details = QString::null) + { + m_status->setStatus(title, details); + } + + virtual void showErrorMessage(KexiDB::Object *obj, const QString& msg = QString::null) + { + m_status->setStatus(obj, msg); + } + + ObjectStatus *m_status; +}; + +ObjectStatus::operator KexiDB::MessageHandler*() +{ + if (!msgHandler) + msgHandler = new ObjectStatusMessageHandler(this); + return msgHandler; +} + +void Kexi::initCmdLineArgs(int argc, char *argv[], KAboutData* aboutData) +{ + KAboutData *about = aboutData; + if (!about) + about = Kexi::createAboutData(); +#ifdef CUSTOM_VERSION +# include "../custom_startup.h" +#endif + KCmdLineArgs::init( argc, argv, about ); + KCmdLineArgs::addCmdLineOptions( options ); +} + +void KEXI_UNFINISHED(const QString& feature_name, const QString& extra_text) +{ + QString msg; + if (feature_name.isEmpty()) + msg = i18n("This function is not available for version %1 of %2 application.") + .arg(KEXI_VERSION_STRING) + .arg(KEXI_APP_NAME); + else { + QString feature_name_(feature_name); + msg = i18n("\"%1\" function is not available for version %2 of %3 application.") + .arg(feature_name_.replace("&","")) + .arg(KEXI_VERSION_STRING) + .arg(KEXI_APP_NAME); + } + + QString extra_text_(extra_text); + if (!extra_text_.isEmpty()) + extra_text_.prepend("\n"); + + KMessageBox::sorry(0, msg + extra_text_); +} diff --git a/kexi/core/kexi.h b/kexi/core/kexi.h new file mode 100644 index 00000000..8490ca29 --- /dev/null +++ b/kexi/core/kexi.h @@ -0,0 +1,147 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXI_H +#define KEXI_H + +#include <qguardedptr.h> +#include <qfont.h> + +#include <kexi_version.h> +#include "kexiprojectdata.h" +#include "kexipartmanager.h" +#include "kexidbconnectionset.h" +#include "kexiprojectset.h" +#include <kexidb/drivermanager.h> +#include <kexidb/driver.h> + +#include <klocale.h> +#include <kmessagebox.h> + +namespace Kexi +{ + KEXICORE_EXPORT void initCmdLineArgs(int argc, char *argv[], KAboutData* aboutData = 0); + + /*! Modes of view for the dialogs. Used mostly for parts and KexiDialogBase. */ + enum ViewMode { + AllViewModes = 0, //!< Usable primarily in KexiPart::initInstanceActions() + NoViewMode = 0, //!< In KexiViewBase::afterSwitchFrom() and KexiViewBase::beforeSwitchTo() + //!< means that parent dialog of the view has not yet view defined. + DataViewMode = 1, + DesignViewMode = 2, + TextViewMode = 4 //!< Also known as SQL View Mode + }; + //! i18n'ed name of view mode \a m + KEXICORE_EXPORT QString nameForViewMode(int m); + + //! A set of known connections + KEXICORE_EXPORT KexiDBConnectionSet& connset(); + + //! A set avaiulable of project infos + KEXICORE_EXPORT KexiProjectSet& recentProjects(); + + //! shared driver manager + KEXICORE_EXPORT KexiDB::DriverManager& driverManager(); + + //! shared part manager + KEXICORE_EXPORT KexiPart::Manager& partManager(); + + //! can be called to delete global objects like driverManager and partManager + //! (and thus, all loaded factories/plugins) + //! before KLibrary::~KLibrary() do this for us + KEXICORE_EXPORT void deleteGlobalObjects(); + + //some temporary flags + + //! false by default, flag loaded on main window startup + KEXICORE_EXPORT bool& tempShowForms(); + + //! false by default, flag loaded on main window startup + KEXICORE_EXPORT bool& tempShowReports(); + + //! false by default, flag loaded on main window startup + KEXICORE_EXPORT bool& tempShowMacros(); + + //! false by default, flag loaded on main window startup + KEXICORE_EXPORT bool& tempShowScripts(); + + /*! A global setting for minimal readable font. + Note: this is defined because KDE has no such setting yet. + \a init is a widget that should be passed if no qApp->mainWidget() is available yet. */ + KEXICORE_EXPORT QFont smallFont(QWidget *init = 0); + + /*! Helper class for storing object status. */ + class KEXICORE_EXPORT ObjectStatus + { + public: + ObjectStatus(); + + ObjectStatus(const QString& message, const QString& description); + + ObjectStatus(KexiDB::Object* dbObject, const QString& message, const QString& description); + + ~ObjectStatus(); + + const ObjectStatus& status() const; + + bool error() const; + + void setStatus(const QString& message, const QString& description); + + //! Note: for safety, \a dbObject needs to be derived from QObject, + //! otherwise it won't be assigned + void setStatus(KexiDB::Object* dbObject, + const QString& message = QString::null, const QString& description = QString::null); + + void setStatus(KexiDB::ResultInfo* result, + const QString& message = QString::null, const QString& description = QString::null); + + void setStatus(KexiDB::Object* dbObject, KexiDB::ResultInfo* result, + const QString& message = QString::null, const QString& description = QString::null); + + void clearStatus(); + + QString singleStatusString() const; + + void append( const ObjectStatus& otherStatus ); + + KexiDB::Object *dbObject() const { return dynamic_cast<KexiDB::Object*>((QObject*)dbObj); } + + //! Helper returning pseudo handler that just updates this ObjectStatus object + //! by receiving a message + operator KexiDB::MessageHandler*(); + + QString message, description; + protected: + QGuardedPtr<QObject> dbObj; //! This is in fact KexiDB::Object + KexiDB::MessageHandler* msgHandler; + }; + + KEXICORE_EXPORT QString msgYouCanImproveData(); + +}//namespace Kexi + +//! Displays information that feature "feature_name" is not availabe in the current application version +KEXICORE_EXPORT void KEXI_UNFINISHED(const QString& feature_name, const QString& extra_text = QString::null); + +//! Like above - for use inside KexiActionProxy subclass - reuses feature name from shared action's text +#define KEXI_UNFINISHED_SHARED_ACTION(action_name) \ + KEXI_UNFINISHED(sharedAction(action_name) ? sharedAction(action_name)->text() : QString::null) + +#endif diff --git a/kexi/core/kexi_global.cpp b/kexi/core/kexi_global.cpp new file mode 100644 index 00000000..db0b9b60 --- /dev/null +++ b/kexi/core/kexi_global.cpp @@ -0,0 +1,50 @@ +/* This file is part of the KOffice libraries + Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl> + + (version information based on kofficeversion.h) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexi_version.h" + +using namespace Kexi; + +KEXICORE_EXPORT unsigned int version() +{ + return KEXI_VERSION; +} + +KEXICORE_EXPORT unsigned int versionMajor() +{ + return KEXI_VERSION_MAJOR; +} + +KEXICORE_EXPORT unsigned int versionMinor() +{ + return KEXI_VERSION_MINOR; +} + +KEXICORE_EXPORT unsigned int versionRelease() +{ + return KEXI_VERSION_RELEASE; +} + +KEXICORE_EXPORT const char *versionString() +{ + return KEXI_VERSION_STRING; +} + diff --git a/kexi/core/kexiaboutdata.cpp b/kexi/core/kexiaboutdata.cpp new file mode 100644 index 00000000..eeaaf07e --- /dev/null +++ b/kexi/core/kexiaboutdata.cpp @@ -0,0 +1,81 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexiaboutdata.h" +#include <kexi_version.h> +#include <kdeversion.h> +#include <kofficeversion.h> //only for KOFFICE_VERSION_STRING +#include <klocale.h> + +static const char *description = + I18N_NOOP("Database creation for everyone") +#ifndef CUSTOM_VERSION +#ifdef KEXI_STANDALONE + "\n\n" I18N_NOOP("This is standalone version of the application distributed outside of KOffice suite.") +#else + "\n\n" I18N_NOOP("This application version is distributed with KOffice suite.") +#endif +#endif + ; + +using namespace Kexi; + +KAboutData* Kexi::createAboutData() +{ + KAboutData *aboutData=new KAboutData( "kexi", KEXI_APP_NAME, + KEXI_VERSION_STRING +#ifndef CUSTOM_VERSION + " (KOffice " KOFFICE_VERSION_STRING ")" +#endif + , description, + KAboutData::License_LGPL_V2, + I18N_NOOP( "(c) 2002-2007, Kexi Team\n" + "(c) 2003-2007, OpenOffice Polska LLC\n"), + I18N_NOOP( "This software is developed by Kexi Team - an international group\n" + "of independent developers, with additional assistance and support\n" + "from the OpenOffice Polska company.\n\n" + "Visit the company Home Page: http://www.openoffice.com.pl"), + "http://www.koffice.org/kexi", + "submit@bugs.kde.org" + ); + // authors sorted by last contribution date + aboutData->addAuthor("Jarosław Staniek / OpenOffice Polska", I18N_NOOP("Project maintainer & developer, design, KexiDB, commercially supported version, win32 port"), "js@iidea.pl"); + aboutData->addAuthor("Lucijan Busch",I18N_NOOP("Former project maintainer & developer"), "lucijan@kde.org"); + aboutData->addAuthor("Cedric Pasteur", I18N_NOOP("KexiPropertyEditor and FormDesigner"), "cedric.pasteur@free.fr"); + aboutData->addAuthor("Adam Pigg", I18N_NOOP("PostgreSQL database driver, Migration module"), "adam@piggz.fsnet.co.uk"); + aboutData->addAuthor("Martin Ellis", I18N_NOOP("Contributions for MySQL and KexiDB, fixes, Migration module, MDB support"), "martin.ellis@kdemail.net"); + aboutData->addAuthor("Sebastian Sauer", I18N_NOOP("Scripting module (KROSS), Python language bindings, design"), "mail@dipe.org"); + aboutData->addAuthor("Christian Nitschkowski", I18N_NOOP("Graphics effects, helper dialogs"), "segfault_ii@web.de"); + aboutData->addAuthor("Peter Simonsson",I18N_NOOP("Former developer"),"psn@linux.se"); + aboutData->addAuthor("Joseph Wenninger", I18N_NOOP("Original Form Designer, original user interface & much more"), "jowenn@kde.org"); + aboutData->addAuthor("Seth Kurzenberg",I18N_NOOP("CQL++, SQL assistance"), "seth@cql.com"); + aboutData->addAuthor("Laurent Montel", I18N_NOOP("Original code cleanings"), "montel@kde.org"); + aboutData->addAuthor("Till Busch", I18N_NOOP("Bugfixes, original Table Widget"), "till@bux.at"); + aboutData->addCredit("Daniel Molkentin",I18N_NOOP("Initial design improvements"), "molkentin@kde.org"); + aboutData->addCredit("Kristof Borrey", I18N_NOOP("Icons and user interface research"), "kristof.borrey@skynet.be"); + aboutData->addCredit("Tomas Krassnig", I18N_NOOP("Coffee sponsoring"), "tkrass05@hak1.at"); + aboutData->addCredit("Paweł Wirecki / OpenOffice Polska", I18N_NOOP("Numerous bug reports, usability tests, technical support"), ""); + aboutData->setTranslator(I18N_NOOP("_: NAME OF TRANSLATORS\nYour names"), I18N_NOOP("_: EMAIL OF TRANSLATORS\nYour emails")); +#if defined(CUSTOM_VERSION) && defined(Q_WS_WIN) + aboutData->setProgramLogo(KEXI_APP_LOGO); +#endif + return aboutData; +} diff --git a/kexi/core/kexiaboutdata.h b/kexi/core/kexiaboutdata.h new file mode 100644 index 00000000..948906c5 --- /dev/null +++ b/kexi/core/kexiaboutdata.h @@ -0,0 +1,33 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef _KEXI_ABOU_DATA_ +#define _KEXI_ABOU_DATA_ + +#include <kaboutdata.h> + +namespace Kexi { + +KEXICORE_EXPORT KAboutData* createAboutData(); + +} + +#endif diff --git a/kexi/core/kexiactioncategories.cpp b/kexi/core/kexiactioncategories.cpp new file mode 100644 index 00000000..4f399342 --- /dev/null +++ b/kexi/core/kexiactioncategories.cpp @@ -0,0 +1,149 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexiactioncategories.h" + +#include <kstaticdeleter.h> +#include <kdebug.h> + +#include <qmap.h> +#include <qasciidict.h> + +namespace Kexi { + +//! @internal +class ActionInternal +{ + public: + ActionInternal(int _categories) + : categories(_categories) + , supportedObjectTypes(0) + , allObjectTypesAreSupported(false) + { + } + ~ActionInternal() { + delete supportedObjectTypes; + } + int categories; + QMap<int, bool> *supportedObjectTypes; + bool allObjectTypesAreSupported : 1; +}; + +static KStaticDeleter<ActionCategories> Kexi_actionCategoriesDeleter; +ActionCategories* Kexi_actionCategories = 0; + +//! @internal +class ActionCategories::Private +{ + public: + Private() + { + actions.setAutoDelete(true); + } + + QAsciiDict<ActionInternal> actions; +}; + +KEXICORE_EXPORT ActionCategories *actionCategories() +{ + if (!Kexi_actionCategories) + Kexi_actionCategoriesDeleter.setObject( Kexi_actionCategories, new ActionCategories() ); + return Kexi_actionCategories; +} + +} + +using namespace Kexi; + +//---------------------------------- + +ActionCategories::ActionCategories() + : d( new Private() ) +{ +} + +ActionCategories::~ActionCategories() +{ + delete d; +} + +void ActionCategories::addAction(const char* name, int categories, + KexiPart::ObjectTypes supportedObjectType1, KexiPart::ObjectTypes supportedObjectType2, + KexiPart::ObjectTypes supportedObjectType3, KexiPart::ObjectTypes supportedObjectType4, + KexiPart::ObjectTypes supportedObjectType5, KexiPart::ObjectTypes supportedObjectType6, + KexiPart::ObjectTypes supportedObjectType7, KexiPart::ObjectTypes supportedObjectType8) +{ + ActionInternal * a = d->actions.find( name ); + if (a) { + a->categories |= categories; + } + else { + a = new ActionInternal(categories); + d->actions.insert(name, a); + } + if (supportedObjectType1) { + if (!a->supportedObjectTypes) + a->supportedObjectTypes = new QMap<int, bool>(); + a->supportedObjectTypes->insert(supportedObjectType1, true); + if (supportedObjectType2) { + a->supportedObjectTypes->insert(supportedObjectType2, true); + if (supportedObjectType3) { + a->supportedObjectTypes->insert(supportedObjectType3, true); + if (supportedObjectType4) { + a->supportedObjectTypes->insert(supportedObjectType4, true); + if (supportedObjectType5) { + a->supportedObjectTypes->insert(supportedObjectType5, true); + if (supportedObjectType6) { + a->supportedObjectTypes->insert(supportedObjectType6, true); + if (supportedObjectType7) { + a->supportedObjectTypes->insert(supportedObjectType7, true); + if (supportedObjectType8) { + a->supportedObjectTypes->insert(supportedObjectType8, true); + } + } + } + } + } + } + } + } +} + +void ActionCategories::setAllObjectTypesSupported(const char* name, bool set) +{ + ActionInternal * a = d->actions.find( name ); + if (a) + a->allObjectTypesAreSupported = set; + else + kexiwarn << "ActionCategories::setAllObjectTypesSupported(): no such action \"" << name << "\"" << endl; +} + +int ActionCategories::actionCategories(const char* name) const +{ + const ActionInternal * a = d->actions.find( name ); + return a ? a->categories : 0; +} + +bool ActionCategories::actionSupportsObjectType(const char* name, KexiPart::ObjectTypes objectType) const +{ + const ActionInternal * a = d->actions.find( name ); + if (a && a->allObjectTypesAreSupported) + return true; + return (a && a->supportedObjectTypes) ? a->supportedObjectTypes->contains(objectType) : false; +} diff --git a/kexi/core/kexiactioncategories.h b/kexi/core/kexiactioncategories.h new file mode 100644 index 00000000..6e672133 --- /dev/null +++ b/kexi/core/kexiactioncategories.h @@ -0,0 +1,108 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXI_ACTION_CATEGORIES_H +#define KEXI_ACTION_CATEGORIES_H + +#include <ksharedptr.h> +#include "kexipart.h" + +namespace Kexi { + +enum ActionCategory +{ + NoActionCategory = 0, //!< no category at all + GlobalActionCategory = 1, //!< global application action like editcopy; + //!< can be applied to focused widget (of many types) + PartItemActionCategory = 2,//!< action related to part item, e.g. data_execute; + //!< requires context, used only in the navigator + WindowActionCategory = 4 //!< action related to active window, which can display + //!< table, query, form, report... +}; + +//! @short A set of functions used to declare action categories +/*! Note: we do not declare actions used in design/text view modes, + because the categories are used in the data view, + for now in the 'assign action to a push button' function. */ +class KEXICORE_EXPORT ActionCategories : public KShared +{ + public: + ActionCategories(); + ~ActionCategories(); + + /*! Declares action \a name for categories \a category (a combination of ActionCategory enum values). + The categories is merged with the previous declaration (if any). + \a supportedObjectTypes can be specified for ActionCategory::WindowAction to declare what object types + the action allows, it is a combination of KexiPart::ObjectTypes enum values. */ + void addAction(const char* name, int categories, + KexiPart::ObjectTypes supportedObjectType1 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType2 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType3 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType4 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType5 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType6 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType7 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType8 = (KexiPart::ObjectTypes)0); + + void addGlobalAction(const char* name) + { addAction(name, Kexi::GlobalActionCategory); } + + //! Convenience function for adding action of category "part item", uses \ref addAction(). + void addPartItemAction(const char* name) + { addAction(name, Kexi::PartItemActionCategory); } + + /*! Convenience function for adding action of category "window", uses \ref addAction(). + \a supportedObjectTypes is a combination of KexiPart::ObjectTypes enum values describing + object types supported by the action. */ + void addWindowAction(const char* name, + KexiPart::ObjectTypes supportedObjectType1 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType2 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType3 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType4 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType5 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType6 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType7 = (KexiPart::ObjectTypes)0, + KexiPart::ObjectTypes supportedObjectType8 = (KexiPart::ObjectTypes)0) + { addAction(name, Kexi::WindowActionCategory, supportedObjectType1, supportedObjectType2, + supportedObjectType3, supportedObjectType4, supportedObjectType5, supportedObjectType6, + supportedObjectType7, supportedObjectType8); } + + /*! If \a set is true, action with name \a name will support any possible object type + that can be checked by actionSupportsObjectType(). + Makes sense for action of category Kexi::WindowActionCategory. */ + void setAllObjectTypesSupported(const char* name, bool set); + + //! \return categories for action \a name (a combination of ActionCategory enum values). + //! If there is no such actions declared at all, -1 is returned. + int actionCategories(const char* name) const; + + /*! \return true if action \a name supports \a objectType. + Only works for actions of WindowAction category. */ + bool actionSupportsObjectType(const char* name, KexiPart::ObjectTypes objectType) const; + protected: + class Private; + Private *d; +}; + +//! \return ActionCategories singleton object +KEXICORE_EXPORT ActionCategories *actionCategories(); + +} + +#endif diff --git a/kexi/core/kexiactionproxy.cpp b/kexi/core/kexiactionproxy.cpp new file mode 100644 index 00000000..0dbcf637 --- /dev/null +++ b/kexi/core/kexiactionproxy.cpp @@ -0,0 +1,282 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexiactionproxy.h" +#include "kexiactionproxy_p.h" + +#include <kdebug.h> +#include <kaction.h> +#include <kmainwindow.h> +#include <kshortcut.h> + +#include <qwidget.h> +#include <qsignal.h> +#include <qiconset.h> + +KAction_setEnabled_Helper::KAction_setEnabled_Helper(KexiActionProxy* proxy) + : QObject(0,"KAction_setEnabled_Helper") + , m_proxy( proxy ) +{ +} + +void KAction_setEnabled_Helper::slotSetEnabled(bool enabled) +{ + if (sender()->inherits("KAction")) { + const KAction *a = static_cast<const KAction*>(sender()); + m_proxy->setAvailable(a->name(), enabled); + } +} + +//======================= + +KexiSharedActionConnector::KexiSharedActionConnector( KexiActionProxy* proxy, QObject *obj ) + : m_proxy(proxy) + , m_object(obj) +{ +} + +KexiSharedActionConnector::~KexiSharedActionConnector() +{ +} + +void KexiSharedActionConnector::plugSharedAction(const char *action_name, const char *slot) +{ + m_proxy->plugSharedAction(action_name, m_object, slot); +} + +void KexiSharedActionConnector::plugSharedActionToExternalGUI( + const char *action_name, KXMLGUIClient *client) +{ + m_proxy->plugSharedActionToExternalGUI(action_name, client); +} + +void KexiSharedActionConnector::plugSharedActionsToExternalGUI( + const QValueList<QCString>& action_names, KXMLGUIClient *client) +{ + m_proxy->plugSharedActionsToExternalGUI(action_names, client); +} + + +//======================= + +KexiActionProxy::KexiActionProxy(QObject *receiver, KexiSharedActionHost *host) + : m_host( host ? host : &KexiSharedActionHost::defaultHost() ) + , m_receiver(receiver) + , m_signals(47) + , m_actionProxyParent(0) + , m_signal_parent( 0, "signal_parent" ) + , m_KAction_setEnabled_helper( new KAction_setEnabled_Helper(this) ) + , m_focusedChild(0) +{ + m_signals.setAutoDelete(true); + m_sharedActionChildren.setAutoDelete(false); + m_alternativeActions.setAutoDelete(true); + m_host->plugActionProxy( this ); +} + +KexiActionProxy::~KexiActionProxy() +{ + QPtrListIterator<KexiActionProxy> it(m_sharedActionChildren); + //detach myself from every child + for (;it.current();++it) { + it.current()->setActionProxyParent_internal( 0 ); + } + //take me from parent + if (m_actionProxyParent) + m_actionProxyParent->takeActionProxyChild( this ); + + m_host->takeActionProxyFor(m_receiver); + + delete m_KAction_setEnabled_helper; +} + +void KexiActionProxy::plugSharedAction(const char *action_name, QObject* receiver, const char *slot) +{ + if (!action_name)// || !receiver || !slot) + return; + QPair<QSignal*,bool> *p = m_signals[action_name]; + if (!p) { + p = new QPair<QSignal*,bool>( new QSignal(&m_signal_parent), true ); + m_signals.insert(action_name, p); + } + if (receiver && slot) + p->first->connect( receiver, slot ); +} + +void KexiActionProxy::unplugSharedAction(const char *action_name) +{ + QPair<QSignal*,bool> *p = m_signals.take(action_name); + if (!p) + return; + delete p->first; + delete p; +} + +int KexiActionProxy::plugSharedAction(const char *action_name, QWidget* w) +{ + KAction *a = sharedAction(action_name); + if (!a) { + kdWarning() << "KexiActionProxy::plugSharedAction(): NO SUCH ACTION: " << action_name << endl; + return -1; + } + return a->plug(w); +} + +void KexiActionProxy::unplugSharedAction(const char *action_name, QWidget* w) +{ + KAction *a = sharedAction(action_name); + if (!a) { + kdWarning() << "KexiActionProxy::unplugSharedAction(): NO SUCH ACTION: " << action_name << endl; + return; + } + a->unplug(w); +} + +KAction* KexiActionProxy::plugSharedAction(const char *action_name, const QString& alternativeText, QWidget* w) +{ + KAction *a = sharedAction(action_name); + if (!a) { + kdWarning() << "KexiActionProxy::plugSharedAction(): NO SUCH ACTION: " << action_name << endl; + return 0; + } + QCString altName = a->name(); + altName += "_alt"; + KAction *alt_act = new KAction(alternativeText, a->iconSet(), a->shortcut(), + 0, 0, a->parent(), altName); + QObject::connect(alt_act, SIGNAL(activated()), a, SLOT(activate())); + alt_act->plug(w); + +//OK? + m_host->updateActionAvailable(action_name, true, m_receiver); + + return alt_act; +} + +void KexiActionProxy::plugSharedActionToExternalGUI(const char *action_name, KXMLGUIClient *client) +{ + KAction *a = client->action(action_name); + if (!a) + return; + plugSharedAction(a->name(), a, SLOT(activate())); + + //update availability + setAvailable(a->name(), a->isEnabled()); + //changes will be signaled + QObject::connect(a, SIGNAL(enabled(bool)), m_KAction_setEnabled_helper, SLOT(slotSetEnabled(bool))); +} + +void KexiActionProxy::plugSharedActionsToExternalGUI( + const QValueList<QCString>& action_names, KXMLGUIClient *client) +{ + for (QValueList<QCString>::const_iterator it = action_names.constBegin(); it!=action_names.constEnd(); ++it) { + plugSharedActionToExternalGUI(*it, client); + } +} + +bool KexiActionProxy::activateSharedAction(const char *action_name, bool alsoCheckInChildren) +{ + QPair<QSignal*,bool> *p = m_signals[action_name]; + if (!p || !p->second) { + //try in children... + if (alsoCheckInChildren) { + QPtrListIterator<KexiActionProxy> it( m_sharedActionChildren ); + for( ; it.current(); ++it ) { + if (it.current()->activateSharedAction( action_name, alsoCheckInChildren )) + return true; + } + } + return m_actionProxyParent ? m_actionProxyParent->activateSharedAction(action_name, false) : false; //last chance: parent + } + //activate in this proxy... + p->first->activate(); + return true; +} + +KAction* KexiActionProxy::sharedAction(const char* action_name) +{ + return m_host->mainWindow()->actionCollection()->action(action_name); +} + +bool KexiActionProxy::isSupported(const char* action_name) const +{ + QPair<QSignal*,bool> *p = m_signals[action_name]; + if (!p) { + //not supported explicitly - try in children... + if (m_focusedChild) + return m_focusedChild->isSupported(action_name); + QPtrListIterator<KexiActionProxy> it( m_sharedActionChildren ); + for( ; it.current(); ++it ) { + if (it.current()->isSupported(action_name)) + return true; + } + return false; //not suported + } + return p != 0; +} + +bool KexiActionProxy::isAvailable(const char* action_name, bool alsoCheckInChildren) const +{ + QPair<QSignal*,bool> *p = m_signals[action_name]; + if (!p) { + //not supported explicitly - try in children... + if (alsoCheckInChildren) { + if (m_focusedChild) + return m_focusedChild->isAvailable(action_name, alsoCheckInChildren); + QPtrListIterator<KexiActionProxy> it( m_sharedActionChildren ); + for( ; it.current(); ++it ) { + if (it.current()->isSupported(action_name)) + return it.current()->isAvailable(action_name, alsoCheckInChildren); + } + } + return m_actionProxyParent ? m_actionProxyParent->isAvailable(action_name, false) : false; //last chance: parent + } + //supported explicitly: + return p->second != 0; +} + +void KexiActionProxy::setAvailable(const char* action_name, bool set) +{ + QPair<QSignal*,bool> *p = m_signals[action_name]; + if (!p) + return; + p->second = set; + m_host->updateActionAvailable(action_name, set, m_receiver); +} + +void KexiActionProxy::addActionProxyChild( KexiActionProxy* child ) +{ + if (!child || child==this) + return; + child->setActionProxyParent_internal( this ); + m_sharedActionChildren.append( child ); +} + +void KexiActionProxy::takeActionProxyChild( KexiActionProxy* child ) +{ + if (m_sharedActionChildren.findRef( child ) != -1) + m_sharedActionChildren.take(); +} + +void KexiActionProxy::setActionProxyParent_internal( KexiActionProxy* parent ) +{ + m_actionProxyParent = parent; +} + +#include "kexiactionproxy_p.moc" + diff --git a/kexi/core/kexiactionproxy.h b/kexi/core/kexiactionproxy.h new file mode 100644 index 00000000..63093d29 --- /dev/null +++ b/kexi/core/kexiactionproxy.h @@ -0,0 +1,190 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIACTIONPROXY_H +#define KEXIACTIONPROXY_H + +#include <qguardedptr.h> +#include <qasciidict.h> +#include <qobject.h> +#include <qpair.h> +#include <qptrlist.h> + +#include <kaction.h> + +#include "kexiproject.h" +#include "kexisharedactionhost.h" + +class QSignal; +class KAction; +class KXMLGUIClient; +class KAction_setEnabled_Helper; +class KexiActionProxy; + +//! Abstract helper class used to connect shared actions from outside of shared-action-aware object. +/*! Methods like KexiActionProxy::plugSharedAction() are not public, but + sometimes there's need for plugging an object that implements KexiActionProxy interface + from outside. + + Reimplement KexiSharedActionConnector: do all needed connections in the constructor. + + For example, with KexiQueryDesignerSQLEditor class we're using KTextEdit + (or KTextEditor::View) that's not shared-action-aware. So it's needed to conenct + e.g. "edit_undo" shared action to undo() slot, and so on. It is impelmented in more + generic way by implementing KTextEdit_SharedActionConnector class, + so the conenction can be reused many times by just allocating KTextEdit_SharedActionConnector + object for any KTextEditor when required (not only within KexiQueryDesignerSQLEditor). +*/ +//TODO add method for setAvailable() +class KEXICORE_EXPORT KexiSharedActionConnector +{ + public: + /* Connects shared actions offered by \a proxy to \a obj. */ + KexiSharedActionConnector(KexiActionProxy* proxy, QObject *obj); + ~KexiSharedActionConnector(); + + protected: + void plugSharedAction(const char *action_name, const char *slot); + + void plugSharedActionToExternalGUI(const char *action_name, KXMLGUIClient *client); + + void plugSharedActionsToExternalGUI( + const QValueList<QCString>& action_names, KXMLGUIClient *client); + + KexiActionProxy* m_proxy; + QObject *m_object; +}; + +//! An interface that acts as proxy for shared actions within the application. +/*! + For example, edit->copy action can be reused to copy different types of items. + Availability and meaning of given action depends on the context, while + the context changes e.g. when another window is activated. + This class is mostly used by subclassing in KexiDialogBase or KexiDockBase + - you can subclass in a similar way. +*/ + +class KEXICORE_EXPORT KexiActionProxy +{ + public: + /*! Constructs action proxy for object \a receiver, using \a host. + If \a host is NULL, KexiSharedActionHost::defaultHost() is used. + (you must be sure that it's true) -- it is casted to QObject and assigned as the receiver.*/ + KexiActionProxy(QObject *receiver , KexiSharedActionHost *host = 0 ); + virtual ~KexiActionProxy(); + + /*! Activates action named \a action_name for this proxy. If the action is executed + (accepted), true is returned. */ + bool activateSharedAction(const char *action_name, bool alsoCheckInChildren = true); + + /*! Sets host to \a host; rerely used. */ + void setSharedActionHost(KexiSharedActionHost& host) { m_host = &host; } + + /*! \return true, if action named \a action_name is enabled within the proxy. + False is returned either if the action is not available or is not supported. + \ sa isSupported() */ + bool isAvailable(const char* action_name, bool alsoCheckInChildren = true) const; + + /*! \return true, if action named \a action_name is supported by the proxy. */ + bool isSupported(const char* action_name) const; + + protected: + /*! Plugs shared action named \a action_name to slot \a slot in \a receiver. + \a Receiver is usually a child of _this_ widget. */ + void plugSharedAction(const char *action_name, QObject* receiver, const char *slot); + + void unplugSharedAction(const char *action_name); + + /*! Typical version of plugAction() method -- plugs action named \a action_name + to slot \a slot in _this_ widget. */ + inline void plugSharedAction(const char *action_name, const char *slot) { + plugSharedAction(action_name, m_receiver, slot); + } + + /*! Plugs action named \a action_name to a widget \a w, so the action is visible on this widget + as an item. \a w will typically be a menu, popup menu or a toolbar. + Does nothing if no action found, so generally this is safer than just caling e.g. + <code> action("myaction")->plug(myPopup); </code> + \return index of inserted item, or -1 if there is not action with name \a action_name. + \sa action(), KAction::plug(QWidget*, int) */ + int plugSharedAction(const char *action_name, QWidget* w); + + void plugSharedActionToExternalGUI(const char *action_name, KXMLGUIClient *client); + + void plugSharedActionsToExternalGUI( + const QValueList<QCString>& action_names, KXMLGUIClient *client); + + /*! Unplugs action named \a action_name from a widget \a w. + \sa plugSharedAction(const char *action_name, QWidget* w) */ + void unplugSharedAction(const char *action_name, QWidget* w); + + /*! Like above, but creates alternative action as a copy of \a action_name, + with \a alternativeText set. When this action is activated, just original action + specified by \a action_name is activated. The aternative action has autmatically set name as: + action_name + "_alt". + \return newly created action or 0 if \a action_name not found. */ + KAction* plugSharedAction(const char *action_name, const QString& alternativeText, QWidget* w); + + /*! \return action named with \a name or NULL if there is no such action. */ + virtual KAction* sharedAction(const char* action_name); + + inline QObject *receiver() const { return m_receiver; } + + virtual void setAvailable(const char* action_name, bool set); + + /*! Adds \a child of this proxy. Children will receive activateSharedAction() event, + If activateSharedAction() "event" is not consumed by the main proxy, + we start to iterate over proxy children (in unspecified order) to and call + activateSharedAction() on every child until one of them accept the "event". + + If proxy child is destroyed, it is automatically detached from its parent proxy. + Parent proxy is 0 by default. This pointer is properly cleared when parent proxy is destroyed. */ + void addActionProxyChild( KexiActionProxy* child ); + + void takeActionProxyChild( KexiActionProxy* child ); + + KexiSharedActionHost *m_host; + QGuardedPtr<QObject> m_receiver; + QAsciiDict< QPair<QSignal*,bool> > m_signals; + + QPtrList<KexiActionProxy> m_sharedActionChildren; + + QPtrList<KAction> m_alternativeActions; + + KexiActionProxy* m_actionProxyParent; + + QObject m_signal_parent; //!< it's just to have common parent for owned signals + + //! For internal use by plugSharedActionToExternalGUI() + KAction_setEnabled_Helper *m_KAction_setEnabled_helper; + + public: + //! For internal use by addActionProxyChild(). \a parent can be 0. + void setActionProxyParent_internal( KexiActionProxy* parent ); + + //! @internal + KexiActionProxy *m_focusedChild; + + friend class KexiSharedActionHost; + friend class KAction_setEnabled_Helper; + friend class KexiSharedActionConnector; +}; + +#endif + diff --git a/kexi/core/kexiactionproxy_p.h b/kexi/core/kexiactionproxy_p.h new file mode 100644 index 00000000..73579299 --- /dev/null +++ b/kexi/core/kexiactionproxy_p.h @@ -0,0 +1,42 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIACTIONPROXY_P_H +#define KEXIACTIONPROXY_P_H + +#include <qobject.h> + +class KexiActionProxy; + +//! Helper class for KexiActionProxy::plugSharedActionToExternalGUI() method. +class KAction_setEnabled_Helper : public QObject +{ + Q_OBJECT + public: + KAction_setEnabled_Helper(KexiActionProxy* proxy); + + public slots: + void slotSetEnabled(bool enabled); + + protected: + KexiActionProxy *m_proxy; +}; + +#endif + diff --git a/kexi/core/kexiblobbuffer.cpp b/kexi/core/kexiblobbuffer.cpp new file mode 100644 index 00000000..d66d69af --- /dev/null +++ b/kexi/core/kexiblobbuffer.cpp @@ -0,0 +1,373 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexiblobbuffer.h" + +#include <assert.h> + +#include <qfile.h> +#include <qfileinfo.h> +#include <qbuffer.h> + +#include <kdebug.h> +#include <kstaticdeleter.h> +#include <kimageio.h> + +#include <kexidb/connection.h> + +static KStaticDeleter<KexiBLOBBuffer> m_bufferDeleter; +static KexiBLOBBuffer* m_buffer = 0; + +//----------------- + +class KexiBLOBBuffer::Private +{ + public: + Private() + : maxId(0) + , inMemoryItems(1009) + , storedItems(1009) + , itemsByURL(1009) + { + } + Id_t maxId; //!< Used to compute maximal recently used identifier for unstored BLOB +//! @todo will be changed to QHash<quint64, Item> + QIntDict<Item> inMemoryItems; //!< for unstored BLOBs + QIntDict<Item> storedItems; //!< for stored items + QDict<Item> itemsByURL; + QGuardedPtr<KexiDB::Connection> conn; +}; + +//----------------- + +KexiBLOBBuffer::Handle::Handle(Item* item) + : m_item(item) +{ + if (m_item) + m_item->refs++; +} + +KexiBLOBBuffer::Handle::Handle(const Handle& handle) +{ + *this = handle; +} + +KexiBLOBBuffer::Handle::Handle() + : m_item(0) +{ +} + +KexiBLOBBuffer::Handle::~Handle() +{ + if (m_item) { + m_item->refs--; + if (m_item->refs<=0) + KexiBLOBBuffer::self()->removeItem(m_item->id, m_item->stored); + } +} + +KexiBLOBBuffer::Handle& KexiBLOBBuffer::Handle::operator=(const Handle& handle) +{ + m_item = handle.m_item; + if (m_item) + m_item->refs++; + return *this; +} + +void KexiBLOBBuffer::Handle::setStoredWidthID(KexiBLOBBuffer::Id_t id) +{ + if (!m_item) + return; + if (m_item->stored) { + kdWarning() << "KexiBLOBBuffer::Handle::setStoredWidthID(): object for id=" << id + << " is aleady stored" << endl; + return; + } + + KexiBLOBBuffer::self()->takeItem(m_item); + m_item->id = id; //new id + m_item->stored = true; +//! @todo What about other handles for this item? +//! @todo They were assuming it's unstored item, but it's stored now.... + KexiBLOBBuffer::self()->insertItem(m_item); +} + +//----------------- + +KexiBLOBBuffer::Item::Item(const QByteArray& data, KexiBLOBBuffer::Id_t ident, bool _stored, + const QString& _name, const QString& _caption, const QString& _mimeType, + Id_t _folderId, const QPixmap& pixmap) + : name(_name), caption(_caption), mimeType(_mimeType), refs(0), + id(ident), folderId(_folderId), stored(_stored), + m_pixmapLoaded(new bool(false)/*workaround for pixmap() const*/) +{ + if (pixmap.isNull()) + m_pixmap = new QPixmap(); + else + m_pixmap = new QPixmap(pixmap); + + if (data.isEmpty()) + m_data = new QByteArray(); + else + m_data = new QByteArray(data); +} + +KexiBLOBBuffer::Item::~Item() +{ + kexipluginsdbg << "KexiBLOBBuffer::Item::~Item()" << endl; + delete m_pixmap; + m_pixmap = 0; + delete m_data; + m_data = 0; + delete m_pixmapLoaded; +} + +QPixmap KexiBLOBBuffer::Item::pixmap() const +{ + if (!*m_pixmapLoaded && m_pixmap->isNull() && !m_data->isEmpty()) { + QString type( KImageIO::typeForMime(mimeType) ); + if (!KImageIO::canRead( type ) || !m_pixmap->loadFromData(*m_data, type.latin1())) { + //! @todo inform about error? + } + *m_pixmapLoaded = true; + } + return *m_pixmap; +} + +QByteArray KexiBLOBBuffer::Item::data() const +{ + if (!m_data->isEmpty()) + return *m_data; + + if (m_data->isEmpty() && m_pixmap->isNull()) + return QByteArray(); + + if (m_data->isEmpty() && !m_pixmap->isNull()) { + //convert pixmap to byte array + //(do it only on demand) + QBuffer buffer( *m_data ); + buffer.open( IO_WriteOnly ); + m_pixmap->save( &buffer, mimeType.isEmpty() ? (const char*)"PNG"/*! @todo default? */ : mimeType.latin1() ); + } + return *m_data; +} + +//----------------- + +KexiBLOBBuffer::KexiBLOBBuffer() + : QObject() + , d(new Private()) +{ + Q_ASSERT(!m_buffer); + d->inMemoryItems.setAutoDelete(true); + d->storedItems.setAutoDelete(true); +} + +KexiBLOBBuffer::~KexiBLOBBuffer() +{ + delete d; +} + +KexiBLOBBuffer::Handle KexiBLOBBuffer::insertPixmap(const KURL& url) +{ + if (url.isEmpty() ) + return KexiBLOBBuffer::Handle(); + if (!url.isValid()) { + kexipluginswarn << "::insertPixmap: INVALID URL '" << url << "'" << endl; + return KexiBLOBBuffer::Handle(); + } +//! @todo what about searching by filename only and then compare data? + Item * item = d->itemsByURL.find(url.prettyURL()); + if (item) + return KexiBLOBBuffer::Handle(item); + + QString fileName = url.isLocalFile() ? url.path() : url.prettyURL(); +//! @todo download the file if remote, then set fileName properly + QFile f(fileName); + if (!f.open(IO_ReadOnly)) { + //! @todo err msg + return KexiBLOBBuffer::Handle(); + } + QString mimeType( KImageIO::mimeType( fileName ) ); + + QByteArray data( f.readAll() ); + if (f.status()!=IO_Ok) { + //! @todo err msg + return KexiBLOBBuffer::Handle(); + } + QFileInfo fi(url.fileName()); + QString caption(fi.baseName().replace('_', " ").simplifyWhiteSpace()); + + item = new Item(data, ++d->maxId, /*!stored*/false, url.fileName(), caption, mimeType); + insertItem(item); + + //cache + item->prettyURL = url.prettyURL(); + d->itemsByURL.replace(url.prettyURL(), item); + return KexiBLOBBuffer::Handle(item); +} + +KexiBLOBBuffer::Handle KexiBLOBBuffer::insertObject(const QByteArray& data, + const QString& name, const QString& caption, const QString& mimeType, KexiBLOBBuffer::Id_t identifier) +{ + KexiBLOBBuffer::Id_t newIdentifier; + if (identifier>0) + newIdentifier = identifier; + else + newIdentifier = ++d->maxId; + + Item *item = new Item(data, newIdentifier, identifier>0, name, caption, mimeType); + insertItem( item ); + return KexiBLOBBuffer::Handle(item); +} + +KexiBLOBBuffer::Handle KexiBLOBBuffer::insertPixmap(const QPixmap& pixmap) +{ + if (pixmap.isNull()) + return KexiBLOBBuffer::Handle(); + + Item * item = new Item( + QByteArray(), //(pixmap will be converted to byte array on demand) + ++d->maxId, + false, //not stored + QString::null, + QString::null, + "image/png", //!< @todo OK? What about jpegs? + 0, //folder id + pixmap); + + insertItem(item); + return KexiBLOBBuffer::Handle(item); +} + +KexiBLOBBuffer::Handle KexiBLOBBuffer::objectForId(Id_t id, bool stored) +{ + if (id<=0) + return KexiBLOBBuffer::Handle(); + if (stored) { + Item *item = d->storedItems.find(id); + if (item || !d->conn) + return KexiBLOBBuffer::Handle(item); + //retrieve stored BLOB: + +//#if 0 + assert(d->conn); + KexiDB::TableSchema *blobsTable = d->conn->tableSchema("kexi__blobs"); + if (!blobsTable) { + //! @todo err msg + return KexiBLOBBuffer::Handle(); + } +/* QStringList where; + where << "o_id"; + KexiDB::PreparedStatement::Ptr st = d->conn->prepareStatement( + KexiDB::PreparedStatement::SelectStatement, *blobsTable, where);*/ +//! @todo use PreparedStatement + KexiDB::QuerySchema schema; + schema.addField( blobsTable->field("o_data") ); + schema.addField( blobsTable->field("o_name") ); + schema.addField( blobsTable->field("o_caption") ); + schema.addField( blobsTable->field("o_mime") ); + schema.addField( blobsTable->field("o_folder_id") ); + schema.addToWhereExpression(blobsTable->field("o_id"), QVariant((Q_LLONG)id)); + + KexiDB::RowData rowData; + tristate res = d->conn->querySingleRecord( + schema, +// QString::fromLatin1("SELECT o_data, o_name, o_caption, o_mime FROM kexi__blobs where o_id=") +// +QString::number(id), + rowData); + if (res!=true || rowData.size()<4) { + //! @todo err msg + kdWarning() << "KexiBLOBBuffer::objectForId("<<id<<","<<stored + <<"): res!=true || rowData.size()<4; res=="<<res.toString()<<" rowData.size()=="<<rowData.size()<< endl; + return KexiBLOBBuffer::Handle(); + } + + item = new Item( + rowData[0].toByteArray(), + id, + true, //stored + rowData[1].toString(), + rowData[2].toString(), + rowData[3].toString(), + (Id_t)rowData[4].toInt() //!< @todo folder id: fix Id_t for Qt4 + ); + + insertItem(item); + return KexiBLOBBuffer::Handle(item); +//#endif + } + else + return KexiBLOBBuffer::Handle(d->inMemoryItems.find(id)); +} + +KexiBLOBBuffer::Handle KexiBLOBBuffer::objectForId(Id_t id) +{ + KexiBLOBBuffer::Handle h(objectForId(id, false/*!stored*/)); + if (h) + return h; + return objectForId(id, true/*stored*/); +} + +void KexiBLOBBuffer::removeItem(Id_t id, bool stored) +{ + Item *item; + if (stored) + item = d->storedItems.take(id); + else + item = d->inMemoryItems.take(id); + + if (item && !item->prettyURL.isEmpty()) { + d->itemsByURL.remove(item->prettyURL); + } + delete item; +} + +void KexiBLOBBuffer::takeItem(Item *item) +{ + assert(item); + if (item->stored) + d->storedItems.take(item->id); + else + d->inMemoryItems.take(item->id); +} + +void KexiBLOBBuffer::insertItem(Item *item) +{ + assert(item); + if (item->stored) + d->storedItems.insert(item->id, item); + else + d->inMemoryItems.insert(item->id, item); +} + +void KexiBLOBBuffer::setConnection(KexiDB::Connection *conn) +{ + KexiBLOBBuffer::self()->d->conn = conn; +} + +KexiBLOBBuffer* KexiBLOBBuffer::self() +{ + if(!m_buffer) { + m_bufferDeleter.setObject( m_buffer, new KexiBLOBBuffer() ); + } + return m_buffer; +} + +#include "kexiblobbuffer.moc" diff --git a/kexi/core/kexiblobbuffer.h b/kexi/core/kexiblobbuffer.h new file mode 100644 index 00000000..bd593ab2 --- /dev/null +++ b/kexi/core/kexiblobbuffer.h @@ -0,0 +1,223 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIBLOBBUFFER_H +#define KEXIBLOBBUFFER_H + +#include <qobject.h> +#include <qintdict.h> +#include <qdict.h> +#include <qpixmap.h> + +#include <kurl.h> + +namespace KexiDB +{ + class Connection; +} + +//! Application-wide buffer for local BLOB data like pixmaps. +/*! For now only pixmaps are supported + @todo support any KPart-compatible objects and more... + + Use this class by acessing to its singleton: KexiBLOBBuffer::self(). + + This class is used for buffering BLOB data, + to avoid duplicating object's data in memory and a need for loading (decoding) + the same object many times. + The data is always local, what means database storage is not employed here. + + Each BLOB instance is identified by an unsigned integer number (Id_t type), + uniquely generated on BLOB loading. Each BLOB can have assigned source url + it has been loaded from (the url can be empty though, e.g. for data coming from clipboard). + + References to KexiBLOBBuffer are counted, so when last reference is lost, data is freed + and the integer identifier is no longer pointing any valid data. + KexiBLOBBuffer::Handle is value-based class that describes handle based in an identifier. + Objects of this class are obtained e.g. from insertPixmap() method. + + There are two kinds of identifiers: + - integers assigned for BLOBs already saved to a db backend, + when KexiBLOBBuffer::Handle::stored() is true + - temporary integers assigned for new BLOBs not yet saved to a db backend, + when KexiBLOBBuffer::Handle::stored() is false + KexiBLOBBuffer::Handle::setStoredWidthID() can be used to switch from unstored to stored state. + Among others, the state has effect on saving forms: only unstored BLOBs will be saved back + to the database; when a BLOB needs to be removed, only it will be physically removed only if it was stored. + + KexiBLOBBuffer is also useful for two more reasons: + - Property editor's item for "image" property displays a preview of pixmap contents. + Without buffering, it would be needed to load pixmap data again: what if the file + it is loaded from is located remote and connection is slow? Memory would be also unnecessary doubled. + - Undo/Redo framework requires to store previous property values. Having a reference defined + by a single interger, memory can be handled more effectively. + + Example use cases: + A large pixmap file "abc.jpg" is loaded as QByteArray <b>once</b> and buffered: + integer identifier is returned. + Then, multiple image widgets are using "abc.jpg" for displaying. + Duplicating an image widget means only duplicating it's properties + like position and BLOB's id: BLOB itself (data of "abc.jpg") is not duplicated. + Creating a new image widget and assiging the same "abc.jpg" pixmap, means only + referencing KexiBLOBBuffer using the same identifier. +*/ +class KEXICORE_EXPORT KexiBLOBBuffer : public QObject +{ + Q_OBJECT + + private: + class Item; + public: + //! long integer for unique identifying blobs +//! @todo Qt4: will be changed + typedef long Id_t; + + //! Access to KexiBLOBBuffer singleton + static KexiBLOBBuffer* self(); + + static void setConnection(KexiDB::Connection *conn); + + //! Object handle used by KexiBLOBBuffer + class KEXICORE_EXPORT Handle { + public: + //! Constructs a null handle. + //! Null handles have empty pixap and data members, id == 0 and cast to boolean false. + Handle(); + + //! Constructs a copy of \a handle. + Handle(const Handle& handle); + + ~Handle(); + + Id_t id() const { return m_item ? m_item->id : 0; } + + /*! \return true if this BLOB data pointed by this handle is stored at the db backend + or false if it is kept in memory. Null handles return false. */ + bool stored() const { return m_item ? m_item->stored : false; } + + //! \return true if this is null handle (i.e. one not pointing to any data) + operator bool() const { return m_item; } + + Handle& operator=(const Handle& handle); + + QByteArray data() const { return m_item ? m_item->data() : QByteArray(); } + + QPixmap pixmap() const { return m_item ? m_item->pixmap() : QPixmap(); } + + /*! Sets "stored" flag to true by setting non-temporary identifier. + Only call this method for unstored (in memory) BLOBs */ + void setStoredWidthID(Id_t id); + + QString originalFileName() const { return m_item ? m_item->name: QString::null; } + + QString mimeType() const { return m_item ? m_item->mimeType : QString::null; } + + Id_t folderId() const { return m_item ? m_item->folderId : 0; } + + protected: + //! Constructs a handle based on \a item. Null handle is constructed for null \a item. + Handle(Item* item); + private: + Item* m_item; + friend class KexiBLOBBuffer; + }; + + //! @internal + KexiBLOBBuffer(); + + ~KexiBLOBBuffer(); + + /*! Inserts a new pixmap loaded from a file at \a url. + If the same file has already been loaded before, it can be found in cache + and returned instantly. It is assumed that the BLOB is unstored, because it is loaded from + external source, so stored() will be equal to false for returned handle. + \return handle to the pixmap data or a null handle if such pixmap could not be loaded. */ + Handle insertPixmap(const KURL& url); + + /*! Inserts a new BLOB data. + @param data The data for BLOB object. + @param name The name for the object, usually a file name or empty + @param caption The more friendly than name, can be based on file name or empty or + defined by a user (this case is not yet used) + @param mimeType The mimeType for the object for easier and mor accurate decoding. + @param identifier Object's identifier. If positive, the "stored" flag for the data + will be set to true with \a identifer, otherwise (the default) the BLOB data will + have "stored" flag set to false, and a new temporary identifier will be assigned. */ + Handle insertObject(const QByteArray& data, const QString& name, + const QString& caption, const QString& mimeType, Id_t identifier = 0); + + /*! Inserts a new pixmap available in memory, e.g. coming from clipboard. */ + Handle insertPixmap(const QPixmap& pixmap); + + /*! \return an object for a given \a id. If \a stored is true, stored BLOBs buffer + is browsed, otherwise unstored (in memory) BLOBs buffer is browsed. + If no object is cached for this id, null handle is returned. */ + Handle objectForId(Id_t id, bool stored); + + /*! \return an object for a given \a id. First, unstored object is checked, then unstored, + if stored was not found. */ + Handle objectForId(Id_t id); + + protected: + /*! Removes an object for a given \a id. If \a stored is true, stored BLOB is removed, + otherwise unstored (in memory) BLOB is removed. */ + void removeItem(Id_t id, bool stored); + + /*! Takes an object for a \a item out of the buffer. */ + void takeItem(Item* item); + + /*! Inserts an object for a given \a id into the buffer. */ + void insertItem(Item* item); + + private: + class KEXICORE_EXPORT Item { + public: + Item(const QByteArray& data, Id_t ident, + bool stored, + const QString& name = QString::null, + const QString& caption = QString::null, + const QString& mimeType = QString::null, + Id_t folderId = 0, + const QPixmap& pixmap = QPixmap()); + ~Item(); + QPixmap pixmap() const; + QByteArray data() const; +// KexiBLOBBuffer* buf; +// KURL url; + QString name; + QString caption; //!< @todo for future use within image gallery + QString mimeType; + uint refs; + Id_t id; + Id_t folderId; + bool stored : 1; + QString prettyURL; //!< helper + private: + QByteArray *m_data; + QPixmap *m_pixmap; + bool *m_pixmapLoaded; //!< *m_pixmapLoaded will be set in Info::pixmap(), + //!< to avoid multiple pixmap decoding when it previously failed + friend class KexiBLOBBuffer; + }; + class Private; + Private *d; + friend class Handle; +}; + +#endif diff --git a/kexi/core/kexicmdlineargs.h b/kexi/core/kexicmdlineargs.h new file mode 100644 index 00000000..d67c5e61 --- /dev/null +++ b/kexi/core/kexicmdlineargs.h @@ -0,0 +1,181 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2003-2007 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXICMDLINEARGS_H +#define KEXICMDLINEARGS_H + +#include <kcmdlineargs.h> +#include <klocale.h> + +static KCmdLineOptions options[] = +{ + { ":", I18N_NOOP("Options related to entire projects:"), 0 }, + { "createdb", I18N_NOOP( + "Create a new, blank project using specified\n" + "database driver and database name\n" + "and exit immediately.\n" + "You will be asked for confirmation\n" + "if overwriting is needed."), 0 }, + { "create-opendb", I18N_NOOP( + "Like --createdb, but also open newly\n" + "created database.\n"), 0 }, + { "dropdb", I18N_NOOP( + "Drop (remove) a project using specified\n" + "database driver and database name.\n" + "You will be asked for confirmation."), 0 }, + { "drv", 0, 0 }, + { "dbdriver <name>", I18N_NOOP( + "Database driver to be used\n" + "for connecting to a database project\n" + "(SQLite by default).\n" + "Ignored if a shortcut filename\n" + "is provided."), 0 }, + { "t", 0, 0 }, + { "type <name>", I18N_NOOP( + "Specify the type of file provided as an argument.\n" + "This option is only useful if the filename does\n" + "not have a valid extension set and its type\n" + "cannot be determined unambiguously by examining\n" + "its contents.\n" + "This option is ignored if no file is specified as\n" + "an argument.\n" + "Available file types are:\n" + "- \"project\" for a project file (the default)\n" + "- \"shortcut\" for a shortcut file pointing to a\n" + " project.\n" + "- \"connection\" for database connection data.\n" + ), 0 }, + { "conn", 0, 0 }, + { "connection <shortcut_filename>", I18N_NOOP( + "\nSpecify a database connection shortcut .kexic\n" + "file containing connection data.\n" + "Can be used with --createdb or --create-opendb\n" + "for convenience instead of using options like \n" + "--user, --host or --port.\n" + "Note: Options like --user, --host have\n" + "precedence over settings defined in the shortcut\n" + "file." ), 0 }, + { "readonly", I18N_NOOP( + "Specify that any database connections will\n" + "be performed without write support. This option\n" + "is ignored when \"createdb\" option is present,\n" + "otherwise the database could not be created."), 0 }, + { "user-mode", I18N_NOOP( + "Start project in User Mode, regardless \n" + "of the project settings."), 0 }, + { "design-mode", I18N_NOOP( + "Start project in Design Mode, regardless \n" + "of the project settings."), 0 }, + { "show-navigator", I18N_NOOP( + "Show the Project Navigator side pane even\n" + "if Kexi runs in User Mode."), 0 }, + { "skip-startup-dialog", I18N_NOOP( + "Skip displaying startup dialog window.\n" + "If there is no project name specified to open,\n" + "empty application window will appear."), 0 }, + + { ":", I18N_NOOP("Options related to opening objects within a project:"), 0 }, + { "open [<object_type>:]<object_name>", I18N_NOOP( + "\nOpen object of type <object_type>\n" + "and name <object_name> from specified project\n" + "on application start.\n" + "<object_type>: is optional, if omitted - table\n" + "type is assumed.\n" + "Other object types can be query, report, form,\n" + "script (may be more or less, depending on your\n" + "plugins installed).\n" + "Use \"\" chars to specify names containing spaces.\n" + "Examples: --open MyTable,\n" + " --open query:\"My very big query\""), 0 }, + { "design [<object_type>:]<object_name>", I18N_NOOP( + "\nLike --open, but the object will\n" + "be opened in Design Mode, if one is available."), 0 }, + { "edittext [<object_type>:]<object_name>", I18N_NOOP( + "\nLike --open, but the object will\n" + "be opened in Text Mode, if one is available."), 0 }, + { "exec", 0, 0 }, + { "execute [<object_type>:]<object_name>", I18N_NOOP( + "\nStart execution of object of type <object_type>\n" + "and name <object_name> on application start.\n" + "<object_type>: is optional, if omitted - macro\n" + "type is assumed.\n" + "Other object types can be script (may be more\n" + "or less, depending on your plugins installed).\n" + "Use \"\" chars to specify names containing spaces."), 0 }, + { "new <object_type>", I18N_NOOP( + "Start new object design of type <object_type>."), 0 }, + { "print [<object_type>:]<object_name>", I18N_NOOP( + "\nOpen the Print dialog window for an object of type\n" + "<object_type> and name <object_name> in the specified\n" + "project when the application starts, for quick printing\n" + "of the object's data.\n" + "<object_type>: is optional; if omitted, table\n" + "type is assumed. Object type can also be query."), 0 }, + { "print-preview [<object_type>:]<object_name>", I18N_NOOP( + "\nOpen Print Preview window for object\n" + "of type <object_type> and name <object_name>\n" + "from specified project on application start.\n" + "See --print for more details."), 0 }, + + { ":", I18N_NOOP("Options related to database servers:"), 0 }, + { "u", 0, 0 }, + { "user <name>", I18N_NOOP( + "User name to be used\n" + "for connecting to a database project.\n" + "Ignored if a shortcut filename\n" + "is provided."), 0 }, +/* { "password <password>", I18N_NOOP( + "User password to be used\n" + "for connecting to a database project.\n" + "Ignored if a shortcut filename\n" + "is provided."), 0 },*/ + { "h", 0, 0 }, + { "host <name>", I18N_NOOP( + "Server (host) name to be used\n" + "for connecting to a database project.\n" + "Ignored if a shortcut filename\n" + "is provided."), 0 }, + { "port <number>", I18N_NOOP( + "Server's port number to be used\n" + "for connecting to a database project.\n" + "Ignored if a shortcut filename\n" + "is provided."), 0 }, + { "local-socket <filename>", I18N_NOOP( + "Server's local socket filename\n" + "to be used for connecting to a database\n" + "project. Ignored if a shortcut filename\n" + "is provided."), 0 }, + { "skip-conn-dialog", I18N_NOOP( + "Skip displaying connection dialog window\n" + "and connect directly. Available when\n" + "opening .kexic or .kexis shortcut files."), 0 }, + + { "+[project-name]", I18N_NOOP( + "Kexi database project filename,\n" + "Kexi shortcut filename,\n" + "or name of a Kexi database\n" + "project on a server to open."), 0 }, + // INSERT YOUR COMMANDLINE OPTIONS HERE + KCmdLineLastOption +}; + +#endif + diff --git a/kexi/core/kexicontexthelp.cpp b/kexi/core/kexicontexthelp.cpp new file mode 100644 index 00000000..4ccfa2fe --- /dev/null +++ b/kexi/core/kexicontexthelp.cpp @@ -0,0 +1,49 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexicontexthelp.h" +#include <KoContextCelp.h> +#include <kxmlguiclient.h> +#include <kapplication.h> +#include <klocale.h> +#include <kaction.h> +#include <qlayout.h> +#include <kdebug.h> +#include <kiconloader.h> + +KexiContextHelp::KexiContextHelp(KexiMainWindow *view, QWidget *parent) + :KoContextHelpWidget(parent,"kexi_contexthelp") +{ + kdDebug()<<"KexiContextHelp::KexiContextHelp()"<<endl; + setCaption(i18n("Context Help")); + setIcon(SmallIcon("help")); + connect(this,SIGNAL(linkClicked( const QString& )), + this,SLOT(linkClickedInternal( const QString& ))); +} + +void KexiContextHelp::linkClickedInternal(const QString& link) { + kdDebug()<<"KexiContextHelp: Link: "<<link<<endl; + unhandledLink(link); +} + +KexiContextHelp::~KexiContextHelp() +{ +} + +#include "kexicontexthelp.moc" diff --git a/kexi/core/kexicontexthelp.h b/kexi/core/kexicontexthelp.h new file mode 100644 index 00000000..3a97e20c --- /dev/null +++ b/kexi/core/kexicontexthelp.h @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXICONTEXTHELP_H +#define KEXICONTEXTHELP_H + + +#include "keximainwindow.h" +#include <KoContextCelp.h> + +class KEXICORE_EXPORT KexiContextHelp : public KoContextHelpWidget +{ + Q_OBJECT + + public: + KexiContextHelp(KexiMainWindow *view, QWidget *parent=0); + ~KexiContextHelp(); + private slots: + void linkClickedInternal(const QString &link); + + signals: + void unhandledLink( const QString& link ); +}; + +#endif diff --git a/kexi/core/kexicontexthelp_p.h b/kexi/core/kexicontexthelp_p.h new file mode 100644 index 00000000..ab9af166 --- /dev/null +++ b/kexi/core/kexicontexthelp_p.h @@ -0,0 +1,35 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef _kexicontexthelp_p_h_ +#define _kexicontexthelp_p_h_ + +class KexiContextHelpInfo { +public: + KexiContextHelpInfo() { + caption=""; + text=""; + iconName=""; + } + QString caption; + QString text; + QString iconName; + +}; +#endif diff --git a/kexi/core/kexidataiteminterface.cpp b/kexi/core/kexidataiteminterface.cpp new file mode 100644 index 00000000..8fe78c85 --- /dev/null +++ b/kexi/core/kexidataiteminterface.cpp @@ -0,0 +1,146 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexidataiteminterface.h" + +#include <kdebug.h> + +KexiDataItemChangesListener::KexiDataItemChangesListener() +{ +} + +KexiDataItemChangesListener::~KexiDataItemChangesListener() +{ +} + +//----------------------------------------------- + +KexiDataItemInterface::KexiDataItemInterface() + : m_listener(0) + , m_listenerIsQObject(false) + , m_parentDataItemInterface(0) + , m_hasFocusableWidget(true) + , m_disable_signalValueChanged(false) + , m_acceptEditorAfterDeleteContents(false) +{ +} + +KexiDataItemInterface::~KexiDataItemInterface() +{ +} + +void KexiDataItemInterface::setValue(const QVariant& value, const QVariant& add, + bool removeOld, const QVariant* visibleValue) +{ + m_disable_signalValueChanged = true; //to prevent emmiting valueChanged() +//needed? clear(); + if (dynamic_cast<QObject*>(this)) { + kdDebug() << "KexiDataItemInterface::setValue(): " << + dynamic_cast<QObject*>(this)->className() << " " + << dynamic_cast<QWidget*>(this)->name() + << " value=" << value << " add=" << add << endl; + } + m_origValue = value; + setValueInternal(add, removeOld); + if (visibleValue) + setVisibleValueInternal(*visibleValue); + m_disable_signalValueChanged = false; +} + +void KexiDataItemInterface::setVisibleValueInternal(const QVariant& value) +{ + Q_UNUSED(value); +} + +void KexiDataItemInterface::signalValueChanged() +{ + if (m_disable_signalValueChanged || isReadOnly()) + return; + if (m_parentDataItemInterface) { + m_parentDataItemInterface->signalValueChanged(); + return; + } + if (m_listener) { + beforeSignalValueChanged(); + m_listener->valueChanged(this); + } +} + +bool KexiDataItemInterface::valueChanged() +{ +// bool ok; +// kdDebug() << m_origValue.toString() << " ? " << value(ok).toString() << endl; +// return (m_origValue != value(ok)) && ok; + kdDebug() << "KexiDataItemInterface::valueChanged(): " << m_origValue.toString() << " ? " << value().toString() << endl; + return m_origValue != value(); +} + +/* +void KexiDataItemInterface::setValue(const QVariant& value) +{ + m_disable_signalValueChanged = true; //to prevent emmiting valueChanged() + setValueInternal( value ); + m_disable_signalValueChanged = false; +}*/ + +KexiDataItemChangesListener* KexiDataItemInterface::listener() +{ + if (!m_listener || !m_listenerIsQObject) + return m_listener; + if (!m_listenerObject) + m_listener = 0; //destroyed, update pointer + return m_listener; +} + +void KexiDataItemInterface::installListener(KexiDataItemChangesListener* listener) +{ + m_listener = listener; + m_listenerIsQObject = dynamic_cast<QObject*>(listener); + if (m_listenerIsQObject) + m_listenerObject = dynamic_cast<QObject*>(listener); +} + +void KexiDataItemInterface::showFocus( const QRect& r, bool readOnly ) +{ + Q_UNUSED(r); + Q_UNUSED(readOnly); +} + +void KexiDataItemInterface::hideFocus() +{ +} + +void KexiDataItemInterface::clickedOnContents() +{ +} + +bool KexiDataItemInterface::valueIsValid() +{ + return true; +} + +void KexiDataItemInterface::setParentDataItemInterface(KexiDataItemInterface* parentDataItemInterface) +{ + m_parentDataItemInterface = parentDataItemInterface; +} + +bool KexiDataItemInterface::cursorAtNewRow() +{ + return listener() ? listener()->cursorAtNewRow() : false; +} diff --git a/kexi/core/kexidataiteminterface.h b/kexi/core/kexidataiteminterface.h new file mode 100644 index 00000000..dbd38005 --- /dev/null +++ b/kexi/core/kexidataiteminterface.h @@ -0,0 +1,249 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDATAITEMINTERFACE_H +#define KEXIDATAITEMINTERFACE_H + +#include <qvariant.h> +#include <qwidget.h> +#include <qguardedptr.h> + +class KexiDataItemInterface; +namespace KexiDB { + class Field; + class QueryColumnInfo; +} + +//! An helper class used to react on KexiDataItemInterface objects' changes. +class KEXICORE_EXPORT KexiDataItemChangesListener +{ + public: + KexiDataItemChangesListener(); + virtual ~KexiDataItemChangesListener(); + + /*! Implement this to react for change of \a item. + Called by KexiDataItemInterface::valueChanged() */ + virtual void valueChanged(KexiDataItemInterface* item) = 0; + + /*! Implement this to return information whether we're currently at new row or not. + This can be used e.g. by data-aware widgets to determine if "(autonumber)" + label should be displayed. */ + virtual bool cursorAtNewRow() const = 0; +}; + +//! An interface for declaring widgets to be data-aware. +class KEXICORE_EXPORT KexiDataItemInterface +{ + public: + KexiDataItemInterface(); + virtual ~KexiDataItemInterface(); + + /*! Just initializes \a value, and calls setValueInternal(const QString& add, bool removeOld). + If \a removeOld is true, current value is set up as \a add. + If \a removeOld if false, current value is set up as \a value + \a add. + \a value is stored as 'old value' -it'd be usable in the future + (e.g. Combo Box editor can use old value if current value does not + match any item on the list). + + \a visibleValue (if not NULL) is passed to provide visible value to display instead of \a value. + This is currently used only in case of the combo box form widget, where displayed content + (usually a text of image) differs from the value of the widget (a numeric index). + + This method is called by table view's and form's editors. */ + void setValue(const QVariant& value, const QVariant& add = QVariant(), bool removeOld = false, + const QVariant* visibleValue = 0); + + //! \return field information for this item + virtual KexiDB::Field *field() const = 0; + + //! \return column information for this item + virtual KexiDB::QueryColumnInfo* columnInfo() const = 0; + + //! Used internally to set column information. + virtual void setColumnInfo(KexiDB::QueryColumnInfo* cinfo) = 0; + + //! Sets listener. No need to reimplement this. + virtual void installListener(KexiDataItemChangesListener* listener); + +// //! Sets value \a value for a widget. +// //! Just calls setValueInternal(), but also blocks valueChanged() +// //! as you we don't want to react on our own change +// void setValue(const QVariant& value); + + //! \return value currently represented by this item. + virtual QVariant value() = 0; + + //! \return true if editor's value is valid for a given type + //! Used for checking if an entered value is valid, + //! E.g. a part of time value can be entered: "12:8" and this is invalid, not only null. + //! Null time or date is valid in Kexi, so it is not enough to test value().isValid(). + //! Default implementation just returns true. + virtual bool valueIsValid(); + + //! \return true if editor's value is null (not empty) + //! Used for checking if a given constraint within table or form is met. + virtual bool valueIsNull() = 0; + + //! \return true if editor's value is empty (not necessary null). + //! Only few data types can accept "EMPTY" property + //! (use KexiDB::Field::hasEmptyProperty() to check this). + //! Used for checking if a given constraint within table of form is met. + virtual bool valueIsEmpty() = 0; + + //! \return value that should be displayed for this item. + //! Only used for items like combo box, where real value is an integer while + //! displayed value is usually a text. For other item types this method should be empty. + virtual QVariant visibleValue() { return QVariant(); } + + /*! \return 'readOnly' flag for this item. The flag is usually taken from + the item's widget, e.g. KLineEdit::isReadOnly(). + By default, always returns false. */ + virtual bool isReadOnly() const { return false; } + + /*! \return the view widget of this item, e.g. line edit widget. */ + virtual QWidget* widget() = 0; + + /*! Hides item's widget, if available. */ + virtual void hideWidget() { if (widget()) widget()->hide(); } + + /*! Shows item's widget, if available. */ + virtual void showWidget() { if (widget()) widget()->show(); } + + //! \return true if editor's value is changed (compared to original value) + virtual bool valueChanged(); + + /*! \return true if the item widget's cursor (whatever that means, eg. line edit cursor) + is at the beginning of editor's contents. This can inform table/form view that + after pressing "left arrow" key should stop editing and move to a field on the left hand. */ + virtual bool cursorAtStart() = 0; + + /*! \return true if the item widget's cursor (whatever that means, eg. line edit cursor) + is at the end of editor's contents. This can inform table/form view that + after pressing "right arrow" key should stop editing and move to a field on the right hand. */ + virtual bool cursorAtEnd() = 0; + + /*! Moves cursor after the last character (or element). + For implementation in items supporting text cursor's movement; by default does nothing. */ + virtual void moveCursorToEnd() {}; + + /*! Moves cursor before the first character (or element). + For implementation in items supporting text cursor's movement; by default does nothing. */ + virtual void moveCursorToStart() {}; + + /*! Selects all characters (or elements) of the item. + For implementation in items supporting text or elements; by default does nothing. */ + virtual void selectAll() {}; + + //! clears item's data, so the data will contain NULL data + virtual void clear() = 0; + + /*! \return true if this editor offers a widget (e.g. line edit) that we can move focus to. + Editor for boolean values has this set to false (see KexiBoolTableEdit). + This is true by default. You can override this flag by changing + m_hasFocusableWidget in your subclass' constructor. */ + inline bool hasFocusableWidget() const { return m_hasFocusableWidget; } + + /*! Displays additional elements that are needed for indicating that the current cell + is selected. For example, combobox editor (KexiComboBoxTableEdit) moves and shows + dropdown button. \a r is the rectangle for the cell. + If \a readOnly is true, additional elements should be visually disabled, + e.g. dropdown button of the combobox editor should be disabled. + For reimplementation. By default does nothing. */ + virtual void showFocus( const QRect& r, bool readOnly ); + + /*! Hides additional elements that are needed for indicating that the current cell + is selected. + For reimplementation. By default does nothing. */ + virtual void hideFocus(); + + /*! Allows to define reaction for clicking on cell's contents. + Currently it's used for editor of type boolean, where we want to toggle true/false + on single mouse click. \sa hasFocusableWidget(), KexiBoolTableEdit. + Default implementation does nothing. */ + virtual void clickedOnContents(); + + /*! \return true if editing should be accepted immediately after + deleting contents for the cell (usually using Delete key). + This flag is false by default, and is true e.g. for date, time and datetime types. */ + bool acceptEditorAfterDeleteContents() const { return m_acceptEditorAfterDeleteContents; } + + inline virtual void setFocus() { if (widget()) widget()->setFocus(); } + + bool cursorAtNewRow(); + + /*! Sets a pointer to a Parent Data Item Interface. This pointer is 0 by default, + but can be set by parent widget if this interface is a building block of a larger data widget. + It is the case for KexiDBFieldEdit widget (see KexiDBFieldEdit::createEditor()). Use with care. + signalValueChanged() method will check this pointer, and if it's not 0, + m_parentDataItemInterface->signalValueChanged() is called, so a changes can be signalled at higher level. */ + void setParentDataItemInterface(KexiDataItemInterface* parentDataItemInterface); + + /*! \return a pointer to a Parent Data Item Interface. + @see setParentDataItemInterface() */ + inline KexiDataItemInterface* parentInterface() const { return m_parentDataItemInterface; } + + /*! Handles action having standard name \a actionName. + Action could be: "edit_cut", "edit_paste", etc. + For reimplementation. */ + virtual void handleAction(const QString& actionName) { Q_UNUSED(actionName); } + + protected: + /*! Initializes this editor with \a add value, which should be somewhat added to the current + value (already storted in m_origValue). + If \a removeOld is true, a value should be set to \a add, otherwise + -it should be set to current \a m_origValue + \a add, if possible. + Implement this. */ + virtual void setValueInternal(const QVariant& add, bool removeOld) = 0; + + /*! Initializes this editor with \a value visible value. + This is currently used only in case of the combo box form widget, where displayed content + (usually a text of image) differs from the value of the widget (a numeric index). + For implementation in the combo box widget, by default does nothing. */ + virtual void setVisibleValueInternal(const QVariant& value); + +// //! Sets value \a value for a widget. +// //! Implement this method to allow setting value for this widget item. +// virtual void setValueInternal(const QVariant& value) = 0; + + /*! Call this in your implementation when value changes, + so installed listener can react on this change. If there is a parent data item defined + (see setParentDataItemInterface()), parent's signalValueChanged() method will be called instead. */ + virtual void signalValueChanged(); + + /*! Used to perform some actions before signalValueChanged() call. + We need this because the intrface is not QObject and thus has got no real signals. + Used in KexiDBComboBox. */ + virtual void beforeSignalValueChanged() {}; + + KexiDataItemChangesListener* listener(); + +//moved to KexiFormDataItemInterface: QString m_dataSource; + QGuardedPtr<QObject> m_listenerObject; + KexiDataItemChangesListener* m_listener; + bool m_listenerIsQObject; + QVariant m_origValue; + + /*! @see parentDataItemInterface() */ + KexiDataItemInterface* m_parentDataItemInterface; + bool m_hasFocusableWidget : 1; + bool m_disable_signalValueChanged : 1; + bool m_acceptEditorAfterDeleteContents : 1; +}; + +#endif diff --git a/kexi/core/kexidbconnectionset.cpp b/kexi/core/kexidbconnectionset.cpp new file mode 100644 index 00000000..1a0eb774 --- /dev/null +++ b/kexi/core/kexidbconnectionset.cpp @@ -0,0 +1,183 @@ +/* This file is part of the KDE project + Copyright (C) 2003,2005 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kexidbconnectionset.h" +#include "kexidbshortcutfile.h" + +#include <kdebug.h> +#include <kstandarddirs.h> + +#include <qfile.h> + +//! @internal +class KexiDBConnectionSetPrivate +{ +public: + KexiDBConnectionSetPrivate() + : dataForFilenames(101) + { + list.setAutoDelete(true); + maxid=-1; + } + KexiDB::ConnectionData::List list; + QMap<KexiDB::ConnectionData*, QString> filenamesForData; + QDict<KexiDB::ConnectionData> dataForFilenames; + int maxid; +}; + +KexiDBConnectionSet::KexiDBConnectionSet() +: QObject() +, d(new KexiDBConnectionSetPrivate()) +{ +} + +KexiDBConnectionSet::~KexiDBConnectionSet() +{ + delete d; +} + +bool KexiDBConnectionSet::addConnectionData(KexiDB::ConnectionData *data, const QString& _filename) +{ + if (!data) + return false; + if (data->id<0) + data->id = d->maxid+1; + //TODO: check for id-duplicates + + d->maxid = QMAX(d->maxid,data->id); +// d->list.append(data); + + QString filename( _filename ); + bool generateUniqueFilename = filename.isEmpty() + || !filename.isEmpty() && data==d->dataForFilenames[filename]; + + if (generateUniqueFilename) { + QString dir = KGlobal::dirs()->saveLocation("data", "kexi/connections/", false /*!create*/); + if (dir.isEmpty()) + return false; + QString baseFilename( dir + (data->hostName.isEmpty() ? "localhost" : data->hostName) ); + int i = 0; + while (KStandardDirs::exists(baseFilename+(i>0 ? QString::number(i) : QString::null)+".kexic")) + i++; + if (!KStandardDirs::exists(dir)) { + //make 'connections' dir and protect it + if (!KStandardDirs::makeDir(dir, 0700)) + return false; + } + filename = baseFilename+(i>0 ? QString::number(i) : QString::null)+".kexic"; + } + addConnectionDataInternal(data, filename); + bool result = saveConnectionData(data, data); + if (!result) + removeConnectionDataInternal(data); + return result; +} + +void KexiDBConnectionSet::addConnectionDataInternal(KexiDB::ConnectionData *data, const QString& filename) +{ + d->filenamesForData.insert(data, filename); + d->dataForFilenames.insert(filename, data); + d->list.append(data); +} + +bool KexiDBConnectionSet::saveConnectionData(KexiDB::ConnectionData *oldData, + KexiDB::ConnectionData *newData) +{ + if (!oldData || !newData) + return false; + QMap<KexiDB::ConnectionData*, QString>::ConstIterator it = d->filenamesForData.find( oldData ); + if (it == d->filenamesForData.constEnd() || it.data().isEmpty()) + return false; + const QString filename( it.data() ); + KexiDBConnShortcutFile shortcutFile(filename); + if (!shortcutFile.saveConnectionData(*newData, newData->savePassword)) // true/*savePassword*/)) + return false; + if (oldData!=newData) + *oldData = *newData; + return true; +} + +void KexiDBConnectionSet::removeConnectionDataInternal(KexiDB::ConnectionData *data) +{ + QMap<KexiDB::ConnectionData*, QString>::ConstIterator it = d->filenamesForData.find( data ); + const QString filename( it.data() ); + d->filenamesForData.remove(data); + d->dataForFilenames.remove(filename); + d->list.removeRef(data); +} + +bool KexiDBConnectionSet::removeConnectionData(KexiDB::ConnectionData *data) +{ + if (!data) + return false; + QMap<KexiDB::ConnectionData*, QString>::ConstIterator it = d->filenamesForData.find( data ); + if (it == d->filenamesForData.constEnd() || it.data().isEmpty()) + return false; + QFile file( it.data() ); + if (!file.remove()) + return false; + removeConnectionDataInternal(data); + return true; +} + +const KexiDB::ConnectionData::List& KexiDBConnectionSet::list() const +{ + return d->list; +} + +void KexiDBConnectionSet::clear() +{ + d->list.clear(); + d->filenamesForData.clear(); + d->dataForFilenames.clear(); +} + +void KexiDBConnectionSet::load() +{ + clear(); +// QStringList dirs( KGlobal::dirs()->findDirs("data", "kexi/connections") ); +// kexidbg << dirs << endl; + QStringList files( KGlobal::dirs()->findAllResources("data", "kexi/connections/*.kexic") ); +// //also try for capital file extension +// files += KGlobal::dirs()->findAllResources("data", "kexi/connections/*.KEXIC"); +// kexidbg << files << endl; + + foreach(QStringList::ConstIterator, it, files) { + KexiDB::ConnectionData *data = new KexiDB::ConnectionData(); + KexiDBConnShortcutFile shortcutFile( *it ); + if (!shortcutFile.loadConnectionData(*data)) { + delete data; + continue; + } + addConnectionDataInternal(data, *it); + } +} + +QString KexiDBConnectionSet::fileNameForConnectionData(KexiDB::ConnectionData *data) const +{ + if (!data) + return QString::null; + QMap<KexiDB::ConnectionData*, QString>::ConstIterator it = d->filenamesForData.find( data ); + return (it == d->filenamesForData.constEnd()) ? QString::null : it.data(); +} + +KexiDB::ConnectionData* KexiDBConnectionSet::connectionDataForFileName(const QString& fileName) const +{ + return d->dataForFilenames[fileName]; +} diff --git a/kexi/core/kexidbconnectionset.h b/kexi/core/kexidbconnectionset.h new file mode 100644 index 00000000..78d4649a --- /dev/null +++ b/kexi/core/kexidbconnectionset.h @@ -0,0 +1,77 @@ +/* This file is part of the KDE project + Copyright (C) 2003,2005 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KEXIDBCONNSET_H +#define KEXIDBCONNSET_H + +#include <qstring.h> +#include <kexidb/connectiondata.h> + +class KexiDBConnectionSetPrivate; + +/*! Stores information about multiple connection-data items. */ +class KEXICORE_EXPORT KexiDBConnectionSet : public QObject +{ +public: + KexiDBConnectionSet(); + ~KexiDBConnectionSet(); + + /*! Loads connection data set from storage, currently from + .kexic files saved in dirs returned by + KStandardDirs::findDirs("data", "connections") */ + void load(); + + /*! Adds \a data as connection data. + \a data will be owned by a KexiDBConnectionSet object. + If \a filename is not empty, it will be kept for use in saveConnectionData(). + saveConnectionData() is called automatically, if there's no \a filename provided + or the filename is already used, a new unique will be generated. + \return true on successful creating corresponding .kexic file */ + bool addConnectionData(KexiDB::ConnectionData *data, const QString& filename = QString::null); + + /*! Saves changes made to \a oldData to a file which name has been provided by addConnectionData(). + This function does nothing if \a oldData hasn't been added to this set. + \return true on success (data is then copied from \a newData to \a oldData) */ + bool saveConnectionData(KexiDB::ConnectionData *oldData, KexiDB::ConnectionData *newData); + + /*! Removed \a data from this set. + \return true on successful removing of corresponding .kexic file */ + bool removeConnectionData(KexiDB::ConnectionData *data); + + /*! \return the list of connection data items. */ + const KexiDB::ConnectionData::List& list() const; + + /*! \return a filename of a connection data file for \a data. */ + QString fileNameForConnectionData(KexiDB::ConnectionData *data) const; + + /*! \return a connection data for a .kexic shortcut filename. + 0 is returned if the filename does not match. */ + KexiDB::ConnectionData* connectionDataForFileName(const QString& fileName) const; + +private: + /*! Removes all connection data items from this set. */ + void clear(); + void addConnectionDataInternal(KexiDB::ConnectionData *data, const QString& filename); + void removeConnectionDataInternal(KexiDB::ConnectionData *data); + + KexiDBConnectionSetPrivate *d; +}; + +#endif // KEXIDBCONNSET_H + diff --git a/kexi/core/kexidbshortcutfile.cpp b/kexi/core/kexidbshortcutfile.cpp new file mode 100644 index 00000000..4a503d43 --- /dev/null +++ b/kexi/core/kexidbshortcutfile.cpp @@ -0,0 +1,314 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexidbshortcutfile.h" +#include <core/kexiprojectdata.h> +#include <kexidb/connectiondata.h> +#include <kexiutils/utils.h> + +#include <kconfig.h> +#include <kdebug.h> + +#include <qstringlist.h> +#include <qdir.h> + +//! Version of the KexiDBShortcutFile format. +#define KexiDBShortcutFile_version 2 +/* CHANGELOG: + v1: initial version + v2: "encryptedPassword" field added. + For backward compatibility, it is not used if the connection data has been loaded from + a file saved with version 1. In such cases unencrypted "password" field is used. +*/ + +//! @internal +class KexiDBShortcutFile::Private +{ + public: + Private() + : isDatabaseShortcut(true) + { + } + QString fileName; + bool isDatabaseShortcut : 1; +}; + +KexiDBShortcutFile::KexiDBShortcutFile( const QString& fileName ) + : d( new KexiDBShortcutFile::Private() ) +{ + d->fileName = QDir(fileName).absPath(); +} + +KexiDBShortcutFile::~KexiDBShortcutFile() +{ + delete d; +} + +bool KexiDBShortcutFile::loadProjectData(KexiProjectData& data, QString* _groupKey) +{ + KConfig config(d->fileName, true /* readOnly */, false /* local */ ); + config.setGroup("File Information"); + data.formatVersion = config.readNumEntry("version", KexiDBShortcutFile_version); + + QString groupKey; + if (!_groupKey || _groupKey->isEmpty()) { + QStringList groups(config.groupList()); + foreach (QStringList::ConstIterator, it, groups) { + if ((*it).lower()!="file information") { + groupKey = *it; + break; + } + } + if (groupKey.isEmpty()) { + //ERR: "File %1 contains no connection information" + return false; + } + if (_groupKey) + *_groupKey = groupKey; + } + else { + if (!config.hasGroup(*_groupKey)) + return false; + groupKey = *_groupKey; + } + + config.setGroup(groupKey); + QString type( config.readEntry("type", "database").lower() ); + + if (type=="database") { + d->isDatabaseShortcut = true; + } else if (type=="connection") { + d->isDatabaseShortcut = false; + } + else { + //ERR: i18n("No valid "type" field specified for section \"%1\": unknown value \"%2\".").arg(group).arg(type) + return false; + } + +/* kexidbg << "version=" << version + << " using group key=" << groupKey + << " type=" << type + << " caption=" << config.readEntry("caption") + << " name=" << config.readEntry("name") + << " engine=" << config.readEntry("engine") + << " server=" << config.readEntry("server") + << " user=" << config.readEntry("user") + << " password=" << QString().fill('*', config.readEntry("password").length()) + << " comment=" << config.readEntry("comment") + << endl;*/ + + //no filename by default + data.connectionData()->setFileName(QString::null); + + if (d->isDatabaseShortcut) { + data.setCaption( config.readEntry("caption") ); + data.setDescription( config.readEntry("comment") ); + data.connectionData()->description = QString::null; + data.connectionData()->caption = QString::null; /* connection name is not specified... */ + data.setDatabaseName( config.readEntry("name") ); + } + else { + data.setCaption( QString::null ); + data.connectionData()->caption = config.readEntry("caption"); + data.setDescription( QString::null ); + data.connectionData()->description = config.readEntry("comment"); + data.setDatabaseName( QString::null ); /* db name is not specified... */ + } + data.connectionData()->driverName = config.readEntry("engine"); + if (data.connectionData()->driverName.isEmpty()) { + //ERR: "No valid "engine" field specified for %1 section" group + return false; + } + data.connectionData()->hostName = config.readEntry("server"); //empty allowed + data.connectionData()->port = config.readNumEntry("port", 0); + data.connectionData()->useLocalSocketFile = config.readBoolEntry("useLocalSocketFile", false); + data.connectionData()->localSocketFileName = config.readEntry("localSocketFile"); + data.connectionData()->savePassword = config.hasKey("password") || config.hasKey("encryptedPassword"); + if (data.formatVersion >= 2) { + kdDebug() << config.hasKey("encryptedPassword") << endl; + data.connectionData()->password = config.readEntry("encryptedPassword"); + KexiUtils::simpleDecrypt(data.connectionData()->password); + } + if (data.connectionData()->password.isEmpty()) {//no "encryptedPassword", for compatibility + //UNSAFE + data.connectionData()->password = config.readEntry("password"); + } +// data.connectionData()->savePassword = !data.connectionData()->password.isEmpty(); + data.connectionData()->userName = config.readEntry("user"); +/* @todo add "options=", eg. as string list? */ + return true; +} + +bool KexiDBShortcutFile::saveProjectData(const KexiProjectData& data, + bool savePassword, QString* _groupKey, bool overwriteFirstGroup) +{ + KConfig config(d->fileName, false /*rw*/, false /* local */); + config.setGroup("File Information"); + + uint realFormatVersion = data.formatVersion; + if (realFormatVersion == 0) /* 0 means "default version"*/ + realFormatVersion = KexiDBShortcutFile_version; + config.writeEntry("version", realFormatVersion); + + const bool thisIsConnectionData = data.databaseName().isEmpty(); + + //use or find a nonempty group key + QString groupKey; + if (_groupKey && !_groupKey->isEmpty()) { + groupKey = *_groupKey; + } + else { + QString groupPrefix; + const QStringList groups(config.groupList()); + if (overwriteFirstGroup && !groups.isEmpty()) { +// groupKey = groups.first(); //found + foreach (QStringList::ConstIterator, it, groups) { + if ((*it).lower()!="file information") { + groupKey = *it; + break; + } + } + } + + if (groupKey.isEmpty()) { + //find a new unique name + if (thisIsConnectionData) + groupPrefix = "Connection%1"; //do not i18n! + else + groupPrefix = "Database%1"; //do not i18n! + + int number = 1; + while (config.hasGroup(groupPrefix.arg(number))) //a new group key couldn't exist + number++; + groupKey = groupPrefix.arg(number); + } + if (_groupKey) //return this one (generated or found) + *_groupKey = groupKey; + } + + config.deleteGroup(groupKey); + config.setGroup(groupKey); + if (thisIsConnectionData) { + config.writeEntry("type", "connection"); + config.writeEntry("caption", data.constConnectionData()->caption); + if (!data.constConnectionData()->description.isEmpty()) + config.writeEntry("comment", data.constConnectionData()->description); + } + else {//database + config.writeEntry("type", "database"); + config.writeEntry("caption", data.caption()); + config.writeEntry("name", data.databaseName()); + if (!data.description().isEmpty()) + config.writeEntry("comment", data.description()); + } + + config.writeEntry("engine", data.constConnectionData()->driverName); + if (!data.constConnectionData()->hostName.isEmpty()) + config.writeEntry("server", data.constConnectionData()->hostName); + + if (data.constConnectionData()->port!=0) + config.writeEntry("port", data.constConnectionData()->port); + config.writeEntry("useLocalSocketFile", data.constConnectionData()->useLocalSocketFile); + if (!data.constConnectionData()->localSocketFileName.isEmpty()) + config.writeEntry("localSocketFile", data.constConnectionData()->localSocketFileName); + + if (savePassword || data.constConnectionData()->savePassword) { + if (realFormatVersion < 2) { + config.writeEntry("password", data.constConnectionData()->password); + } + else { + QString encryptedPassword = data.constConnectionData()->password; + KexiUtils::simpleCrypt(encryptedPassword); + config.writeEntry("encryptedPassword", encryptedPassword); + encryptedPassword.fill(' '); //for security + } + } + + if (!data.constConnectionData()->userName.isEmpty()) + config.writeEntry("user", data.constConnectionData()->userName); +/* @todo add "options=", eg. as string list? */ + config.sync(); + return true; +} + +QString KexiDBShortcutFile::fileName() const +{ + return d->fileName; +} + +//--------------------------------------------- + +KexiDBConnShortcutFile::KexiDBConnShortcutFile( const QString& fileName ) + : KexiDBShortcutFile( fileName ) +{ +} + +KexiDBConnShortcutFile::~KexiDBConnShortcutFile() +{ +} + +bool KexiDBConnShortcutFile::loadConnectionData(KexiDB::ConnectionData& data, QString* _groupKey) +{ + KexiProjectData pdata(data); + if (!loadProjectData(pdata, _groupKey)) + return false; + data = *pdata.connectionData(); + return true; +} + +bool KexiDBConnShortcutFile::saveConnectionData(const KexiDB::ConnectionData& data, + bool savePassword, QString* groupKey, bool overwriteFirstGroup) +{ + KexiProjectData pdata(data); + return saveProjectData(pdata, savePassword, groupKey, overwriteFirstGroup); +} + +//--------------------------------------------- + +#if 0 +/*! Loads connection data into \a data. */ +bool KexiDBConnSetShortcutFiles::loadConnectionDataSet(KexiDBConnectionSet& set) +{ + set.clear(); +// QStringList dirs( KGlobal::dirs()->findDirs("data", "kexi/connections") ); +// kexidbg << dirs << endl; + QStringList files( KGlobal::dirs()->findAllResources("data", "kexi/connections/*.kexic") ); +// //also try for capital file extension +// files += KGlobal::dirs()->findAllResources("data", "kexi/connections/*.KEXIC"); + kexidbg << files << endl; + + foreach(QStringList::ConstIterator, it, files) { + KexiDB::ConnectionData *data = new KexiDB::ConnectionData(); + KexiDBConnShortcutFile shortcutFile( *it ); + if (!shortcutFile.loadConnectionData(*data)) { + delete data; + continue; + } + set.addConnectionData(data); + } +} + + +/*! Saves a set of connection data \a set to a shortcut files. + Existing files are overwritten with a new data. */ +bool KexiDBConnSetShortcutFiles::saveConnectionDataSet(const KexiDBConnectionSet& set) +{ +} + +#endif diff --git a/kexi/core/kexidbshortcutfile.h b/kexi/core/kexidbshortcutfile.h new file mode 100644 index 00000000..3dfa9c13 --- /dev/null +++ b/kexi/core/kexidbshortcutfile.h @@ -0,0 +1,124 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDBSHORTCUTFILE_H +#define KEXIDBSHORTCUTFILE_H + +#include <qstring.h> + +class KexiProjectData; +namespace KexiDB { class ConnectionData; } + +/*! Loads and saves information for a "shortcut to a connection" file containing + connection information with database name (i.e. ProjectData). + This is implementation for handling .KEXIS files. + See http://www.kexi-project.org/wiki/wikiview/index.php?KexiMimeTypes_DataSaving_Loading +*/ +class KEXICORE_EXPORT KexiDBShortcutFile +{ + public: + /*! Creates a new object for \a fileName. */ + KexiDBShortcutFile( const QString& fileName ); + + ~KexiDBShortcutFile(); + + /*! Loads project data (with connection data) into \a data. + Database name and caption can be set there but these are optional. + \a groupKey, if provided will be set to a group key, + so you can later use it in saveConnectionData(). + \return true on success. */ + bool loadProjectData(KexiProjectData& data, QString* groupKey = 0); + + /*! Saves project data \a data (with connection data) to a shortcut file. + If \a storePassword is true, password will be saved in the file, + even if data.connectionData()->savePassword is false. + Existing data is merged with new data. \a groupKey is reused, if specified. + If \a overwriteFirstGroup is true (the default) first found group will be overwritten + instead of creating of a new unique group. This mode is usable for updating .kexic files + containing single connection data, what's used for storing connections repository. + \return true on success. */ + bool saveProjectData(const KexiProjectData& data, bool savePassword, + QString* groupKey = 0, bool overwriteFirstGroup = true); + + //! \return filename provided on this object's construction. */ + QString fileName() const; + + protected: + class Private; + Private *d; +}; + +/*! Loads and saves information for a "shortcut" file containing + connection information (i.e. KexiDB::ConnectionData). + This is implementation for handling .KEXIC files. + See http://www.kexi-project.org/wiki/wikiview/index.php?KexiMimeTypes_DataSaving_Loading +*/ +class KEXICORE_EXPORT KexiDBConnShortcutFile : protected KexiDBShortcutFile +{ + public: + /*! Creates a new object for \a fileName. */ + KexiDBConnShortcutFile( const QString& fileName ); + + ~KexiDBConnShortcutFile(); + + /*! Loads connection data into \a data. + \a groupKey, if provided will be set to a group key, + so you can later use it in saveConnectionData(). + \return true on success. */ + bool loadConnectionData(KexiDB::ConnectionData& data, QString* groupKey = 0); + + /*! Saves connection data \a data to a shortcut file. + If \a storePassword is true, password will be saved in the file, + even if data.savePassword is false. + Existing data is merged with new data. \a groupKey is reused, if specified. + If \a overwriteFirstGroup is true (the default) first found group will be overwritten + instead of creating of a new unique group. This mode is usable for updating .kexic files + containing single connection data, what's used for storing connections repository. + \return true on success. */ + bool saveConnectionData(const KexiDB::ConnectionData& data, + bool savePassword, QString* groupKey = 0, bool overwriteFirstGroup = true); + + //! \return filename provided on this object's construction. */ + QString fileName() const { return KexiDBShortcutFile::fileName(); } + + protected: +}; + +#if 0 +//! Loads and saves information for a sef of "shortcut to a connection" file containing +//! connection information (i.e. KexiDBConnectionSet). +//! This is implementation for handling .KEXIC files. +//! The set is loaded from files found using +//! KGlobal::dirs()->findAllResources("data", "kexi/connections/*.kexic"). +class KexiDBConnSetShortcutFiles +{ + public: + KexiDBConnSetShortcutFiles(); + + /*! Loads connection data into \a set. The set is cleared before loading. + \retuirn true on successful loading. */ + static bool loadConnectionDataSet(KexiDBConnectionSet& set); + + /*! Saves a set of connection data \a set to a shortcut files. + Existing files are overwritten with a new data. + \retuirn true on successful saving. */ + static bool saveConnectionDataSet(const KexiDBConnectionSet& set); +} +#endif +#endif diff --git a/kexi/core/kexidialogbase.cpp b/kexi/core/kexidialogbase.cpp new file mode 100644 index 00000000..2f94e661 --- /dev/null +++ b/kexi/core/kexidialogbase.cpp @@ -0,0 +1,661 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexidialogbase.h" + +#include "keximainwindow.h" +#include "kexiviewbase.h" +#include "kexicontexthelp_p.h" +#include "kexipart.h" +#include "kexistaticpart.h" +#include "kexipartitem.h" +#include "kexipartinfo.h" +#include "kexiproject.h" + +#include <kexidb/connection.h> +#include <kexidb/utils.h> +#include <kexiutils/utils.h> + +#include <qwidgetstack.h> +#include <qobjectlist.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <kapplication.h> +#include <kiconloader.h> + +KexiDialogBase::KexiDialogBase(KexiMainWindow *parent, const QString &caption) + : KMdiChildView(caption, parent, "KexiDialogBase") + , KexiActionProxy(this, parent) + , m_isRegistered(false) + , m_origCaption(caption) + , m_schemaData(0) + , m_destroying(false) + , m_disableDirtyChanged(false) +// , m_neverSaved(false) +{ + m_supportedViewModes = 0; //will be set by KexiPart + m_openedViewModes = 0; + m_currentViewMode = Kexi::NoViewMode; //no view available yet + m_parentWindow = parent; + m_creatingViewsMode = -1; + + QVBoxLayout *lyr = new QVBoxLayout(this); + m_stack = new QWidgetStack(this, "stack"); + lyr->addWidget(m_stack); + +#ifdef KEXI_NO_CTXT_HELP + m_contextHelpInfo=new KexiContextHelpInfo(); +#endif +// m_instance=parent->instance(); + m_id = -1; + m_item = 0; + + hide(); //will be shown later +} + +KexiDialogBase::~KexiDialogBase() +{ + m_destroying = true; +} + +KexiViewBase *KexiDialogBase::selectedView() const +{ + if (m_destroying) + return 0; +// return static_cast<KexiViewBase*>(m_stack->visibleWidget()); + return static_cast<KexiViewBase*>( m_stack->widget(m_currentViewMode) ); +} + +KexiViewBase *KexiDialogBase::viewForMode(int mode) const +{ + return static_cast<KexiViewBase*>( m_stack->widget(mode) ); +} + +void KexiDialogBase::addView(KexiViewBase *view) +{ + addView(view,0); +} + +void KexiDialogBase::addView(KexiViewBase *view, int mode) +{ + m_stack->addWidget(view, mode); +// addActionProxyChild( view ); + + //set focus proxy inside this view + QWidget *ch = static_cast<QWidget*>(view->child( 0, "QWidget", false )); + if (ch) + view->setFocusProxy(ch); + + m_openedViewModes |= mode; +} + +void KexiDialogBase::removeView(int mode) +{ + KexiViewBase *view = viewForMode(mode); + if (view) + m_stack->removeWidget(view); + + m_openedViewModes |= mode; + m_openedViewModes ^= mode; +} + +QSize KexiDialogBase::minimumSizeHint() const +{ + KexiViewBase *v = selectedView(); + if (!v) + return KMdiChildView::minimumSizeHint(); + return v->minimumSizeHint() + QSize(0, mdiParent() ? mdiParent()->captionHeight() : 0); +} + +QSize KexiDialogBase::sizeHint() const +{ + KexiViewBase *v = selectedView(); + if (!v) + return KMdiChildView::sizeHint(); + return v->preferredSizeHint( v->sizeHint() ); +} + +/* +KInstance *KexiDialogBase::instance() { + return m_instance; +}*/ + +void KexiDialogBase::registerDialog() { + if (m_isRegistered) + return; + m_parentWindow->registerChild(this); + m_isRegistered=true; + if ( m_parentWindow->mdiMode() == KMdi::ToplevelMode ) { + m_parentWindow->addWindow( (KMdiChildView *)this, KMdi::Detach ); + m_parentWindow->detachWindow((KMdiChildView *)this, true); + } + else + m_parentWindow->addWindow((KMdiChildView *)this); +//later show(); +// m_parentWindow->activeWindowChanged(this); +} + +bool KexiDialogBase::isRegistered(){ + return m_isRegistered; +} + +void KexiDialogBase::attachToGUIClient() { + if (!guiClient()) + return; + +} + +void KexiDialogBase::detachFromGUIClient() { + if (!guiClient()) + return; + //TODO +} + +int KexiDialogBase::id() const +{ + return (partItem() && partItem()->identifier()>0) ? partItem()->identifier() : m_id; +} + +void KexiDialogBase::setContextHelp(const QString& caption, const QString& text, const QString& iconName) { +#ifdef KEXI_NO_CTXT_HELP + m_contextHelpInfo->caption=caption; + m_contextHelpInfo->text=text; + m_contextHelpInfo->text=iconName; + updateContextHelp(); +#endif +} + +void KexiDialogBase::closeEvent( QCloseEvent * e ) +{ + m_parentWindow->acceptPropertySetEditing(); + + //let any view send "closing" signal + QObjectList *list = m_stack->queryList( "KexiViewBase", 0, false, false); + KexiViewBase *view; + QObjectListIt it( *list ); + for ( ;(view = static_cast<KexiViewBase*>(it.current()) ) != 0; ++it ) { + bool cancel = false; + emit view->closing(cancel); + if (cancel) { + e->ignore(); + return; + } + } + delete list; + emit closing(); + KMdiChildView::closeEvent(e); +} + +#if 0 +//js removed +bool KexiDialogBase::tryClose(bool dontSaveChanges) +{ + if (!dontSaveChanges && dirty()) { +/*TODO if (KMessageBox::questionYesNo(this, "<b>"+i18n("Do you want save:") + +"<p>"+typeName+" \""+ item->name() + "\"?</b>", + 0, KStdGuiItem::yes(), KStdGuiItem::no(), ???????)==KMessageBox::No) + return false;*/ + //js TODO: save data using saveChanges() + } + close(true); + return true; +} +#endif + +bool KexiDialogBase::dirty() const +{ + //look for "dirty" flag + int m = m_openedViewModes, mode = 1; + while (m>0) { + if (m & 1) { + if (static_cast<KexiViewBase*>(m_stack->widget(mode))->dirty()) + return true; + } + m >>= 1; + mode <<= 1; + } + return false; +/* KexiViewBase *v = m_newlySelectedView ? m_newlySelectedView : selectedView(); + return v ? v->dirty() : false;*/ +} + +void KexiDialogBase::setDirty(bool dirty) +{ + m_disableDirtyChanged = true; + int m = m_openedViewModes, mode = 1; + while (m>0) { + if (m & 1) { + static_cast<KexiViewBase*>(m_stack->widget(mode))->setDirty(dirty); + } + m >>= 1; + mode <<= 1; + } + m_disableDirtyChanged = false; + dirtyChanged(m_viewThatRecentlySetDirtyFlag); //update +} + +QString KexiDialogBase::itemIcon() +{ + if (!m_part || !m_part->info()) { + KexiViewBase *v = selectedView(); + if (v) {//m_stack->visibleWidget() && m_stack->visibleWidget()->inherits("KexiViewBase")) { + return v->m_defaultIconName; + } + return QString::null; + } + return m_part->info()->itemIcon(); +} + +KexiPart::GUIClient* KexiDialogBase::guiClient() const +{ + if (!m_part || m_currentViewMode<1) + return 0; + return m_part->instanceGuiClient(m_currentViewMode); +} + +KexiPart::GUIClient* KexiDialogBase::commonGUIClient() const +{ + if (!m_part) + return 0; + return m_part->instanceGuiClient(0); +} + +bool KexiDialogBase::isDesignModePreloadedForTextModeHackUsed(int newViewMode) const +{ + return newViewMode==Kexi::TextViewMode + && !viewForMode(Kexi::DesignViewMode) + && supportsViewMode(Kexi::DesignViewMode); +} + +tristate KexiDialogBase::switchToViewMode( int newViewMode, QMap<QString,QString>* staticObjectArgs, + bool& proposeOpeningInTextViewModeBecauseOfProblems) +{ + m_parentWindow->acceptPropertySetEditing(); + + const bool designModePreloadedForTextModeHack = isDesignModePreloadedForTextModeHackUsed(newViewMode); + tristate res = true; + if (designModePreloadedForTextModeHack) { + /* A HACK: open design BEFORE text mode: otherwise Query schema becames crazy */ + bool _proposeOpeningInTextViewModeBecauseOfProblems = false; // used because even if opening the view failed, + // text view can be opened + res = switchToViewMode( Kexi::DesignViewMode, staticObjectArgs, _proposeOpeningInTextViewModeBecauseOfProblems); + if ((!res && !_proposeOpeningInTextViewModeBecauseOfProblems) || ~res) + return res; + } + + kdDebug() << "KexiDialogBase::switchToViewMode()" << endl; + bool dontStore = false; + KexiViewBase *view = selectedView(); + + if (m_currentViewMode == newViewMode) + return true; + if (!supportsViewMode(newViewMode)) + return false; + + if (view) { + res = true; + if (!designModePreloadedForTextModeHack) { + res = view->beforeSwitchTo(newViewMode, dontStore); + } + if (~res || !res) + return res; + if (!dontStore && view->dirty()) { + res = m_parentWindow->saveObject(this, i18n("Design has been changed. " + "You must save it before switching to other view.")); + if (~res || !res) + return res; +// KMessageBox::questionYesNo(0, i18n("Design has been changed. You must save it before switching to other view.")) +// ==KMessageBox::No + } + } + + //get view for viewMode + KexiViewBase *newView + = (m_stack->widget(newViewMode) && m_stack->widget(newViewMode)->inherits("KexiViewBase")) + ? static_cast<KexiViewBase*>(m_stack->widget(newViewMode)) : 0; + if (!newView) { + KexiUtils::setWaitCursor(); + //ask the part to create view for the new mode + m_creatingViewsMode = newViewMode; + KexiPart::StaticPart *staticPart = dynamic_cast<KexiPart::StaticPart*>((KexiPart::Part*)m_part); + if (staticPart) + newView = staticPart->createView(m_stack, this, *m_item, newViewMode, staticObjectArgs); + else + newView = m_part->createView(m_stack, this, *m_item, newViewMode, staticObjectArgs); + KexiUtils::removeWaitCursor(); + if (!newView) { + //js TODO error? + kdDebug() << "Switching to mode " << newViewMode << " failed. Previous mode " + << m_currentViewMode << " restored." << endl; + return false; + } + m_creatingViewsMode = -1; + addView(newView, newViewMode); + } + const int prevViewMode = m_currentViewMode; + res = true; + if (designModePreloadedForTextModeHack) { + m_currentViewMode = Kexi::NoViewMode; //SAFE? + } + res = newView->beforeSwitchTo(newViewMode, dontStore); + proposeOpeningInTextViewModeBecauseOfProblems = tempData()->proposeOpeningInTextViewModeBecauseOfProblems; + if (!res) { + removeView(newViewMode); + delete newView; + kdDebug() << "Switching to mode " << newViewMode << " failed. Previous mode " + << m_currentViewMode << " restored." << endl; + return false; + } + m_currentViewMode = newViewMode; + m_newlySelectedView = newView; + if (prevViewMode==Kexi::NoViewMode) + m_newlySelectedView->setDirty(false); + + res = newView->afterSwitchFrom( + designModePreloadedForTextModeHack ? Kexi::NoViewMode : prevViewMode); + proposeOpeningInTextViewModeBecauseOfProblems = tempData()->proposeOpeningInTextViewModeBecauseOfProblems; + if (!res) { + removeView(newViewMode); + delete newView; + kdDebug() << "Switching to mode " << newViewMode << " failed. Previous mode " + << prevViewMode << " restored." << endl; + const Kexi::ObjectStatus status(*this); + setStatus(mainWin()->project()->dbConnection(), + i18n("Switching to other view failed (%1).").arg(Kexi::nameForViewMode(newViewMode)),""); + append( status ); + m_currentViewMode = prevViewMode; + return false; + } + m_newlySelectedView = 0; + if (~res) { + m_currentViewMode = prevViewMode; + return cancelled; + } + if (view) + takeActionProxyChild( view ); //take current proxy child + addActionProxyChild( newView ); //new proxy child + m_stack->raiseWidget( newView ); + newView->propertySetSwitched(); + m_parentWindow->invalidateSharedActions( newView ); + QTimer::singleShot(10, newView, SLOT(setFocus())); //newView->setFocus(); //js ok? +// setFocus(); + return true; +} + +tristate KexiDialogBase::switchToViewMode( int newViewMode ) +{ + bool dummy; + return switchToViewMode( newViewMode, 0, dummy ); +} + +void KexiDialogBase::setFocus() +{ + if (m_stack->visibleWidget()) { + if (m_stack->visibleWidget()->inherits("KexiViewBase")) + static_cast<KexiViewBase*>( m_stack->visibleWidget() )->setFocus(); + else + m_stack->visibleWidget()->setFocus(); + } + else { + KMdiChildView::setFocus(); + } + activate(); +} + +KoProperty::Set* +KexiDialogBase::propertySet() +{ + KexiViewBase *v = selectedView(); + if (!v) + return 0; + return v->propertySet(); +} + +bool KexiDialogBase::eventFilter(QObject *obj, QEvent *e) +{ + if (KMdiChildView::eventFilter(obj, e)) + return true; +/* if (e->type()==QEvent::FocusIn) { + QWidget *w = m_parentWindow->activeWindow(); + w=0; + }*/ + if ((e->type()==QEvent::FocusIn && m_parentWindow->activeWindow()==this) + || e->type()==QEvent::MouseButtonPress) { + if (m_stack->visibleWidget() && KexiUtils::hasParent(m_stack->visibleWidget(), obj)) { + //pass the activation + activate(); + } + } + return false; +} + +void KexiDialogBase::dirtyChanged(KexiViewBase* view) +{ + if (m_disableDirtyChanged) + return; + m_viewThatRecentlySetDirtyFlag = dirty() ? view : 0; +/* if (!dirty()) { + if (caption()!=m_origCaption) + KMdiChildView::setCaption(m_origCaption); + } + else { + if (caption()!=(m_origCaption+"*")) + KMdiChildView::setCaption(m_origCaption+"*"); + }*/ + updateCaption(); + emit dirtyChanged(this); +} + +/*QString KexiDialogBase::caption() const +{ + return m_origCaption; + if (dirty()) + return KMdiChildView::caption()+; + + return KMdiChildView::caption(); +}*/ + +void KexiDialogBase::updateCaption() +{ + if (!m_item || !m_part || !m_origCaption.isEmpty()) + return; +// m_origCaption = c; + QString capt = m_item->name(); + QString fullCapt = capt; + if (m_part) + fullCapt += (" : " + m_part->instanceCaption()); + if (dirty()) { + KMdiChildView::setCaption(fullCapt+"*"); + KMdiChildView::setTabCaption(capt+"*"); + } + else { + KMdiChildView::setCaption(fullCapt); + KMdiChildView::setTabCaption(capt); + } +} + +bool KexiDialogBase::neverSaved() const +{ + return m_item ? m_item->neverSaved() : true; +} + +tristate KexiDialogBase::storeNewData() +{ + if (!neverSaved()) + return false; + KexiViewBase *v = selectedView(); + if (m_schemaData) + return false; //schema must not exist + if (!v) + return false; + //create schema object and assign information + KexiDB::SchemaData sdata(m_part->info()->projectPartID()); + sdata.setName( m_item->name() ); + sdata.setCaption( m_item->caption() ); + sdata.setDescription( m_item->description() ); + + bool cancel = false; + m_schemaData = v->storeNewData(sdata, cancel); + if (cancel) + return cancelled; + if (!m_schemaData) { + setStatus(m_parentWindow->project()->dbConnection(), i18n("Saving object's definition failed."),""); + return false; + } + + if (!part()->info()->isIdStoredInPartDatabase()) { + //this part's ID is not stored within kexi__parts: + KexiDB::TableSchema *ts = m_parentWindow->project()->dbConnection()->tableSchema("kexi__parts"); + kdDebug() << "KexiDialogBase::storeNewData(): schema: " << ts << endl; + if (!ts) + return false; + + //temp. hack: avoid problems with autonumber + // see http://bugs.kde.org/show_bug.cgi?id=89381 + int p_id = part()->info()->projectPartID(); + + if (p_id<0) { + // Find first available custom part ID by taking the greatest + // existing custom ID (if it exists) and adding 1. + p_id = (int)KexiPart::UserObjectType; + tristate success = m_parentWindow->project()->dbConnection()->querySingleNumber( + "SELECT max(p_id) FROM kexi__parts", p_id); + if (!success) { + // Couldn't read part id's from the kexi__parts table + return false; + } else { + // Got a maximum part ID, or there were no parts + p_id = p_id + 1; + p_id = QMAX(p_id, (int)KexiPart::UserObjectType); + } + } + + KexiDB::FieldList *fl = ts->subList("p_id", "p_name", "p_mime", "p_url"); + kexidbg << "KexiDialogBase::storeNewData(): fieldlist: " + << (fl ? fl->debugString() : QString::null) << endl; + if (!fl) + return false; + + kexidbg << part()->info()->ptr()->untranslatedGenericName() << endl; +// QStringList sl = part()->info()->ptr()->propertyNames(); +// for (QStringList::ConstIterator it=sl.constBegin();it!=sl.constEnd();++it) +// kexidbg << *it << " " << part()->info()->ptr()->property(*it).toString() << endl; + if (!m_parentWindow->project()->dbConnection()->insertRecord( + *fl, + QVariant(p_id), + QVariant(part()->info()->ptr()->untranslatedGenericName()), + QVariant(part()->info()->mimeType()), QVariant("http://www.koffice.org/kexi/" /*always ok?*/))) + return false; + + kdDebug() << "KexiDialogBase::storeNewData(): insert success!" << endl; + part()->info()->setProjectPartID( p_id ); + //(int) project()->dbConnection()->lastInsertedAutoIncValue("p_id", "kexi__parts")); + kdDebug() << "KexiDialogBase::storeNewData(): new id is: " + << part()->info()->projectPartID() << endl; + + part()->info()->setIdStoredInPartDatabase(true); + } + + /* Sets 'dirty' flag on every dialog's view. */ + setDirty(false); +// v->setDirty(false); + //new schema data has now ID updated to a unique value + //-assign that to item's identifier + m_item->setIdentifier( m_schemaData->id() ); + m_parentWindow->project()->addStoredItem( part()->info(), m_item ); + + return true; +} + +tristate KexiDialogBase::storeData(bool dontAsk) +{ + if (neverSaved()) + return false; + KexiViewBase *v = selectedView(); + if (!v) + return false; + +#define storeData_ERR \ + setStatus(m_parentWindow->project()->dbConnection(), i18n("Saving object's data failed."),""); + + //save changes using transaction + KexiDB::Transaction transaction = m_parentWindow->project()->dbConnection()->beginTransaction(); + if (transaction.isNull()) { + storeData_ERR; + return false; + } + KexiDB::TransactionGuard tg(transaction); + + const tristate res = v->storeData(dontAsk); + if (~res) //trans. will be cancelled + return res; + if (!res) { + storeData_ERR; + return res; + } + if (!tg.commit()) { + storeData_ERR; + return false; + } + /* Sets 'dirty' flag on every dialog's view. */ + setDirty(false); +// v->setDirty(false); + return true; +} + +void KexiDialogBase::activate() +{ + KexiViewBase *v = selectedView(); + //kdDebug() << "focusWidget(): " << focusWidget()->name() << endl; + if (KexiUtils::hasParent( v, KMdiChildView::focusedChildWidget()))//focusWidget())) + KMdiChildView::activate(); + else {//ah, focused widget is not in this view, move focus: + if (v) + v->setFocus(); + } + if (v) + v->updateActions(true); +//js: not neeed?? m_parentWindow->invalidateSharedActions(this); +} + +void KexiDialogBase::deactivate() +{ + KexiViewBase *v = selectedView(); + if (v) + v->updateActions(false); +} + +void KexiDialogBase::sendDetachedStateToCurrentView() +{ + KexiViewBase *v = selectedView(); + if (v) + v->parentDialogDetached(); +} + +void KexiDialogBase::sendAttachedStateToCurrentView() +{ + KexiViewBase *v = selectedView(); + if (v) + v->parentDialogAttached(); +} + +#include "kexidialogbase.moc" + diff --git a/kexi/core/kexidialogbase.h b/kexi/core/kexidialogbase.h new file mode 100644 index 00000000..43dc1ff4 --- /dev/null +++ b/kexi/core/kexidialogbase.h @@ -0,0 +1,352 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIDIALOGBASE_H +#define KEXIDIALOGBASE_H + +#include "kexipartguiclient.h" +#include "kexiactionproxy.h" +#include "kexi.h" +#include "kexipart.h" + +#include <qguardedptr.h> + +#include <kmdichildview.h> +#include <kxmlguiclient.h> + +class QWidgetStack; +class KexiMainWindow; +class KexiViewBase; +class KActionCollection; +class KexiContextHelpInfo; +namespace KexiPart { + class Part; +} + +namespace KoProperty { + class Set; +} + +//! Privides temporary data shared between KexiDialogBase's views (KexiView's) +/*! Designed for reimplementation, if needed. */ +class KEXICORE_EXPORT KexiDialogTempData : public QObject +{ + public: + KexiDialogTempData(QObject* parent) + : QObject(parent, "KexiDialogTempData") + , proposeOpeningInTextViewModeBecauseOfProblems(false) + {} + /*! Initially false, KexiPart::Part implementation can set this to true + on data loading (e.g. in loadSchemaData()), to indicate that TextView mode + could be used instead of DataView or DesignView, because there are problems + with opening object. + + For example, in KexiQueryPart::loadSchemaData() query statement can be invalid, + and thus could not be displayed in DesignView mode or executed for DataView. + So, this flag is set to true and user is asked for confirmation for switching + to TextView (SQL Editor). + + After switching to TextView, this flag is cleared. + */ + bool proposeOpeningInTextViewModeBecauseOfProblems : 1; +}; + +//! Base class for child window of Kexi's main application window. +/*! This class can contain a number of configurable views, switchable using toggle action. + It also automatically works as a proxy for shared (application-wide) actions. +*/ +class KEXICORE_EXPORT KexiDialogBase : + public KMdiChildView, + public KexiActionProxy, + public Kexi::ObjectStatus +{ + Q_OBJECT + + public: + KexiDialogBase(KexiMainWindow *parent, const QString &caption = QString::null); + virtual ~KexiDialogBase(); + + bool isRegistered(); + + //! \return currently selected view or 0 if there is no current view + KexiViewBase *selectedView() const; + + /*! \return a view for a given \a mode or 0 if there's no such mode available (or opened). + This does not open mode if it's not opened. */ + KexiViewBase *viewForMode(int mode) const; + + //! Adds \a view for the dialog. It will be the _only_ view (of unspecified mode) for the dialog + void addView(KexiViewBase *view); + + /*! \return main (top level) widget inside this dialog. + This widget is used for e.g. determining minimum size hint and size hint. */ +// virtual QWidget* mainWidget() = 0; + + /*! reimplemented: minimum size hint is inherited from currently visible view. */ + virtual QSize minimumSizeHint() const; + + /*! reimplemented: size hint is inherited from currently visible view. */ + virtual QSize sizeHint() const; + + KexiMainWindow *mainWin() const { return m_parentWindow; } + + //js todo: maybe remove this since it's often the same as partItem()->identifier()?: + + /*! This method sets internal identifier for the dialog, but + if there is a part item associated with this dialog (see partItem()), + partItem()->identifier() will be is used as identifier, so this method is noop. + Thus, it's usable only for dialog types when no part item is assigned. */ + void setId(int id) { m_id = id; } + + /*! If there is a part item associated with this dialog (see partItem()), + partItem()->identifier() is returned, otherwise internal dialog's identifier + (previously set by setID()) is returned. */ + int id() const; + + //! \return Kexi part used to create this window + inline KexiPart::Part* part() const { return m_part; } + + //! \return Kexi part item used to create this window + KexiPart::Item *partItem() const { return m_item; } + + //! Kexi dialog's gui COMMON client. + //! It's obtained by querying part object for this dialog. + KexiPart::GUIClient* commonGUIClient() const; + + //! Kexi dialog's gui client for currently selected view. + //! It's obtained by querying part object for this dialog. + KexiPart::GUIClient* guiClient() const; + + /*! Tries to close the dialog. \return true if closing is accepted + (sometimes, user may not want to close the dialog by pressing cancel). + If \a dontSaveChanges if true, changes are not saved even if this dialog is dirty. */ +//js removed bool tryClose(bool dontSaveChanges); + + /*! \return name of icon provided by part that created this dialog. + The name is used by KexiMainWindow to set/reset icon for this dialog. */ + virtual QString itemIcon(); + + /*! \return true if this dialog supports switching to \a mode. + \a mode is one of Kexi::ViewMode enum elements. + The flags are used e.g. for testing by KexiMainWindow, if actions + of switching to given view mode is available. + This member is intialised in KexiPart that creates this KexiDialogBase object. */ + bool supportsViewMode( int mode ) const { return m_supportedViewModes & mode; } + + /*! \return current view mode for this dialog. */ + int currentViewMode() const { return m_currentViewMode; } + + /*! Switches this dialog to \a newViewMode. + \a viewMode is one of Kexi::ViewMode enum elements. + \return true for successful switching + True is returned also if user has cancelled switching + (rarely, but for any reason) - cancelled is returned. + */ + tristate switchToViewMode( int newViewMode ); + + void setContextHelp(const QString& caption, const QString& text, const QString& iconName); + + /*! Internal reimplementation. */ + virtual bool eventFilter(QObject *obj, QEvent *e); + + /*! Used by Main Window + \todo js: PROBABLY REMOVE THESE TWO? + */ + virtual void attachToGUIClient(); + virtual void detachFromGUIClient(); + + /*! True if contents (data) of the dialog is dirty and need to be saved + This may or not be used, depending if changes in the dialog + are saved immediately (e.g. like in datatableview) or saved by hand (by user) + (e.g. like in alter-table dialog). + \return true if at least on "dirty" flag is set for one of the dialog's view. */ + bool dirty() const; + + /*! \return a pointer to view that has recently set dirty flag. + This value is cleared when dirty flag is set to false (i.e. upon saving changes). */ + KexiViewBase* viewThatRecentlySetDirtyFlag() const { return m_viewThatRecentlySetDirtyFlag; } + + /*! \return true, if this dialog's data were never saved. + If it's true we're usually try to ask a user if the dialog's + data should be saved somewhere. After dialog construction, + "neverSaved" flag is set to appropriate value. + KexiPart::Item::neverSaved() is reused. + */ + bool neverSaved() const; + + /*! \return property set provided by a current view, + or NULL if there is no view set (or the view has no set assigned). */ + KoProperty::Set *propertySet(); + + KexiDB::SchemaData* schemaData() const { return m_schemaData; } + /*! Reimpelmented: "*" is added if for 'dirty' dialog's data. */ +// QString caption() const; + + /*! Used by KexiViewBase subclasses. \return temporary data shared between views */ + KexiDialogTempData *tempData() const { return m_tempData; } + +// /*! Used by KexiViewBase subclasses. Sets temporary data shared between views. */ +// void setTempData( KexiDialogTempData* data ) { m_tempData = data; } + + /*! Called primarily by KexiMainWindowImpl to activate dialog. + Selected view (if present) is also informed about activation. */ + void activate(); + + /*! Called primarily by KexiMainWindowImpl to deactivate dialog. + Selected view (if present) is also informed about deactivation. */ + void deactivate(); + + public slots: + virtual void setFocus(); + + void updateCaption(); + + /*! Internal. Called by KexiMainWindowImpl::saveObject(). + Tells this dialog to save changes of the existing object + to the backend. If \a dontAsk is true, no question dialog will + be shown to the user. The default is false. + \sa storeNewData() + \return true on success, false on failure and cancelled when storing has been cancelled. */ + tristate storeData(bool dontAsk = false); + + /*! Internal. Called by KexiMainWindowImpl::saveObject(). + Tells this dialog to create and store data of the new object + to the backend. + Object's schema data has been never stored, + so it is created automatically, using information obtained + form part item. On success, part item's ID is updated to new value, + and m_schemaData is set. \sa schemaData(). + \return true on success, false on failure and cancelled when storing has been cancelled. */ + tristate storeNewData(); + + /*! Reimplemented - we're informing the current view about performed detaching by calling + KexiViewBase::parentDialogDetached(), so the view can react on this event + (by default KexiViewBase::parentDialogDetached() does nothing, you can reimplement it). */ + void sendDetachedStateToCurrentView(); + + /*! W're informing the current view about performed atttaching by calling + KexiViewBase::parentDialogAttached(), so the view can react on this event + (by default KexiViewBase::parentDialogAttached() does nothing, you can reimplement it). */ + void sendAttachedStateToCurrentView(); + + signals: + void updateContextHelp(); + + //! emitted when the window is about to close + void closing(); + + /*! Emited to inform the world that 'dirty' flag changes. + Activated by KexiViewBase::setDirty(). */ + void dirtyChanged(KexiDialogBase*); + + protected slots: + /*! Sets 'dirty' flag on every dialog's view. */ + void setDirty(bool dirty); + + protected: + /*! Used by Part::openInstance(), + like switchToViewMode( int newViewMode ), but passed \a staticObjectArgs. + Only used for parts of class KexiPart::StaticPart. */ + tristate switchToViewMode( int newViewMode, QMap<QString,QString>* staticObjectArgs, + bool& proposeOpeningInTextViewModeBecauseOfProblems); + + void registerDialog(); + + virtual void closeEvent( QCloseEvent * e ); + + //! \internal + void addView(KexiViewBase *view, int mode); + + //! \internal + void removeView(int mode); + + int m_supportedViewModes; + int m_openedViewModes; + int m_currentViewMode; + + inline QWidgetStack * stack() const { return m_stack; } + + //! Used by \a view to inform the dialog about changing state of the "dirty" flag. + void dirtyChanged(KexiViewBase* view); +#if 0 + /*! Loads large string data \a dataString block (e.g. xml form's representation), + indexed with optional \a dataID, from the database backend. + \return true on success + \sa storeDataBlock(). */ + bool loadDataBlock( QString &dataString, const QString& dataID = QString::null); + + /*! Stores (potentially large) string data \a dataString, block (e.g. xml form's representation), + at the database backend. Block will be stored in "kexi__objectdata" table pointed by + this object's id and an optional \a dataID identifier. + If there is already such record in the table, it's simply overwritten. + \return true on success + */ + bool storeDataBlock( const QString &dataString, const QString& dataID = QString::null ); + + /*! Removes (potentially large) string data (e.g. xml form's representation), + pointed by optional \a dataID, from the database backend. + \return true on success. Does not fail if the block doe not exists. + Note that if \a dataID is not specified, all data blocks for this dialog will be removed. + \sa storeDataBlock(). */ + bool removeDataBlock( QString &dataString, const QString& dataID = QString::null); + + /*! @internal + Used by KexiDialogBase::storeDataBlock() and by KexiViewBase::storeDataBlock(). + */ + bool storeDataBlock_internal( const QString &dataString, int o_id, const QString& dataID ); +#endif +// void setError(const QString& message, const QString& details); + + bool isDesignModePreloadedForTextModeHackUsed(int newViewMode) const; + + private: + KexiMainWindow *m_parentWindow; + bool m_isRegistered; +#ifdef KEXI_NO_CTXT_HELP + KexiContextHelpInfo *m_contextHelpInfo; +#endif + int m_id; + QGuardedPtr<KexiPart::Part> m_part; + KexiPart::Item *m_item; + QWidgetStack *m_stack; + QString m_origCaption; //!< helper + KexiDB::SchemaData* m_schemaData; + QGuardedPtr<KexiViewBase> m_newlySelectedView; //!< Used in dirty(), temporary set in switchToViewMode() + //!< during view setup, when a new view is not yet raised. + //! Used in viewThatRecentlySetDirtyFlag(), modified in dirtyChanged(). + QGuardedPtr<KexiViewBase> m_viewThatRecentlySetDirtyFlag; + QGuardedPtr<KexiDialogTempData> m_tempData; //!< temporary data shared between views + + /*! Created view's mode - helper for switchToViewMode(), + KexiViewBase ctor uses that info. >0 values are useful. */ + int m_creatingViewsMode; + + bool m_destroying : 1; //!< true after entering to the dctor + bool m_disableDirtyChanged; //!< used in setDirty(), affects dirtyChanged() + + friend class KexiMainWindow; +// friend class KexiMainWindowImpl; + friend class KexiPart::Part; + friend class KexiInternalPart; + friend class KexiViewBase; +}; + +#endif + diff --git a/kexi/core/kexidragobjects.cpp b/kexi/core/kexidragobjects.cpp new file mode 100644 index 00000000..5cddbca0 --- /dev/null +++ b/kexi/core/kexidragobjects.cpp @@ -0,0 +1,146 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexidragobjects.h" + +#include <qcstring.h> +#include <qdatastream.h> +#include <kdebug.h> + +/// implementation of KexiFieldDrag + +KexiFieldDrag::KexiFieldDrag(const QString& sourceMimeType, const QString& sourceName, + const QString& field, QWidget *parent, const char *name) + : QStoredDrag("kexi/field", parent, name) +{ + QByteArray data; + QDataStream stream1(data,IO_WriteOnly); + stream1 << sourceMimeType << sourceName << field; + setEncodedData(data); +} + +KexiFieldDrag::KexiFieldDrag(const QString& sourceMimeType, const QString& sourceName, + const QStringList& fields, QWidget *parent, const char *name) + : QStoredDrag((fields.count() > 1) ? "kexi/fields" : "kexi/field", parent, name) +{ + QByteArray data; + QDataStream stream1(data,IO_WriteOnly); + if (fields.count() > 1) + stream1 << sourceMimeType << sourceName << fields; + else { + QString field; + if (fields.count() == 1) + field = fields.first(); + else + kexidbg << "KexiFieldDrag::KexiFieldDrag(): fields list is empty!" << endl; + stream1 << sourceMimeType << sourceName << field; + } + setEncodedData(data); +} + +KexiFieldDrag::~KexiFieldDrag() +{ +} + +bool +KexiFieldDrag::canDecodeSingle(QMimeSource *e) +{ + return e->provides("kexi/field"); +} + +bool +KexiFieldDrag::canDecodeMultiple(QMimeSource *e) +{ + return e->provides("kexi/field") || e->provides("kexi/fields"); +} + +bool +KexiFieldDrag::decodeSingle( QDropEvent* e, QString& sourceMimeType, + QString& sourceName, QString& field ) +{ + QByteArray payload( e->data("kexi/field") ); + if (payload.isEmpty()) + return false; + e->accept(); + QDataStream stream1(payload, IO_ReadOnly); + stream1 >> sourceMimeType; + stream1 >> sourceName; + stream1 >> field; +// kdDebug() << "KexiFieldDrag::decode() decoded: " << sourceMimeType<<"/"<<sourceName<<"/"<<field << endl; + return true; +} + +bool +KexiFieldDrag::decodeMultiple( QDropEvent* e, QString& sourceMimeType, + QString& sourceName, QStringList& fields ) +{ + QByteArray payload( e->data("kexi/fields") ); + if (payload.isEmpty()) {//try single + QString field; + bool res = KexiFieldDrag::decodeSingle( e, sourceMimeType, sourceName, field ); + if (!res) + return false; + fields.append(field); + return true; + } + e->accept(); + QDataStream stream1(payload, IO_ReadOnly); + stream1 >> sourceMimeType; + stream1 >> sourceName; + stream1 >> fields; +// kdDebug() << "KexiFieldDrag::decode() decoded: " << sourceMimeType<<"/"<<sourceName<<"/"<<fields << endl; + return true; +} + +/// implementation of KexiDataProviderDrag + +KexiDataProviderDrag::KexiDataProviderDrag(const QString& sourceMimeType, const QString& sourceName, + QWidget *parent, const char *name) + : QStoredDrag("kexi/dataprovider", parent, name) +{ + QByteArray data; + QDataStream stream1(data,IO_WriteOnly); + stream1 << sourceMimeType << sourceName; + setEncodedData(data); +} + + +bool +KexiDataProviderDrag::canDecode(QDragMoveEvent *e) +{ + return e->provides("kexi/dataprovider"); +} + +bool +KexiDataProviderDrag::decode( QDropEvent* e, QString& sourceMimeType, QString& sourceName) +{ + QCString tmp; + QByteArray payload = e->data("kexidataprovider"); + if(payload.size()) + { + e->accept(); + QDataStream stream1(payload, IO_ReadOnly); + stream1 >> sourceMimeType; + stream1 >> sourceName; +// kdDebug() << "KexiDataProviderDrag::decode() decoded: " << sourceMimeType <<"/"<<sourceName<< endl; + return true; + } + return false; +} diff --git a/kexi/core/kexidragobjects.h b/kexi/core/kexidragobjects.h new file mode 100644 index 00000000..9b7a42ec --- /dev/null +++ b/kexi/core/kexidragobjects.h @@ -0,0 +1,82 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 2003 Joseph Wenninger <jowenn@kde.org> + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXI_DRAGOBJECTS_H_ +#define KEXI_DRAGOBJECTS_H_ + +#include <qdragobject.h> + +class QString; +class QStringList; +class QWidget; + +//! Drag object containing information about field(s). +class KEXICORE_EXPORT KexiFieldDrag : public QStoredDrag +{ + public: + /*! Creates drag object for a single field \a field. */ + KexiFieldDrag(const QString& sourceMimeType, const QString& sourceName, + const QString& field, QWidget *parent, const char *name); + + /*! Creates drag object for multiple fields \a fields. + If there's less than two elements in the list, data is set up as for above ctor. */ + KexiFieldDrag(const QString& sourceMimeType, const QString& sourceName, + const QStringList& field, QWidget *parent=0, const char *name=0); + + ~KexiFieldDrag(); + + void addField(const QString& field); + + /*! \return true if event \a e (of class QDragMoveEvent or QDropEvent) + can be decoded as "kexi/field" data */ + static bool canDecodeSingle( QMimeSource* e ); + + /*! \return true if event \a e (of class QDragMoveEvent or QDropEvent) + can be decoded as "kexi/fields" data. If decoding of "kexi/field" + type is supported, decoding of "kexi/fields" is always supported. + */ + static bool canDecodeMultiple( QMimeSource* e ); + + /*! Decodes data of single-field drag ("kexi/field" mime type) coming with event \a e. + Sets \a sourceMimeType, \a sourceName and \a field. + \return true on successful decoding (\a e will be accepted in such case). */ + static bool decodeSingle( QDropEvent* e, QString& sourceMimeType, + QString& sourceName, QString& field ); + + /*! Decodes data of multiple-field drag ("kexi/fields" mime type) coming with event \a e. + Sets \a sourceMimeType, \a sourceName and \a fields. Also works with "kexi/field" data. + \return true on successful decoding (\a e will be accepted in such case). */ + static bool decodeMultiple( QDropEvent* e, QString& sourceMimeType, + QString& sourceName, QStringList& fields ); +}; + +class KEXICORE_EXPORT KexiDataProviderDrag : public QStoredDrag +{ + public: + KexiDataProviderDrag(const QString& sourceMimeType, const QString& sourceName, + QWidget *parent=0, const char *name=0); + ~KexiDataProviderDrag() { }; + + static bool canDecode( QDragMoveEvent* e); + static bool decode( QDropEvent* e, QString& sourceMimeType, QString& sourceName); + +}; + +#endif diff --git a/kexi/core/kexievents.cpp b/kexi/core/kexievents.cpp new file mode 100644 index 00000000..2cafe2d3 --- /dev/null +++ b/kexi/core/kexievents.cpp @@ -0,0 +1,92 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexievents.h" + +Event::Event(QObject *sender, const QCString &signal, + QObject *receiver, const QCString &slot) +{ + m_sender = sender; + m_receiver = receiver; + m_slot = slot; + m_signal = signal; +} + +Event::Event(QObject *sender, const QCString &signal, + const QCString &functionName) +{ + m_sender = sender; + m_signal = signal; + m_slot = functionName; +} + +void +EventList::addEvent(Event *event) +{ + if(event) + append(event); +} + +void +EventList::addEvent(QObject *sender, const QCString &signal, QObject *receiver, const QCString &slot) +{ + Event *ev = new Event(sender, signal, receiver, slot); + append(ev); +} + +void +EventList::addEvent(QObject *sender, const QCString &signal, const QCString &function) +{ + Event *ev = new Event(sender, signal, function); + append(ev); +} + +void +EventList::removeEvent(Event *event) +{ + if(!event) return; + remove(event); + delete event; +} + +EventList* +EventList::allEventsForObject(QObject *widget) +{ + if(!widget) return 0; + + EventList *l = new EventList(); + EventList::ConstIterator endIt = constEnd(); + for(EventList::ConstIterator it = constBegin(); it != endIt; ++it) { + if( ((*it)->sender() == widget) || ( (*it)->receiver() == widget) ) + l->addEvent(*it); + } + + return l; +} + +void +EventList::removeAllEventsForObject(QObject *widget) +{ + EventList::ConstIterator endIt = constEnd(); + for(EventList::ConstIterator it = constBegin(); it != endIt; ++it) { + if( ((*it)->sender() == widget) || ( (*it)->receiver() == widget) ) + removeEvent(*it); + } +} + diff --git a/kexi/core/kexievents.h b/kexi/core/kexievents.h new file mode 100644 index 00000000..145ee68d --- /dev/null +++ b/kexi/core/kexievents.h @@ -0,0 +1,100 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KFORMDESIGNEREVENTS_H +#define KFORMDESIGNEREVENTS_H + +#include <qvaluelist.h> +#include <qguardedptr.h> + +class QDomNode; +class QObject; + +//! A simple class to store events +/*! There are three different types of events (an maybe more in the future): + * signal to slot: sender and receiver are both widgets. + * signal to user function: whenever the signal is emitted, a function in the form script is called. + * signal to action: the signal activates an application action (eg addNewRecord in Kexi) + (other :* global signal to user function: an application global signal (new window opened, etc.) calls a user script function) + + \todo add aliases for slot()?? (eg actionName()) + */ +class KEXICORE_EXPORT Event +{ + public: + Event(QObject *sender, const QCString &signal, + QObject *receiver, const QCString &slot); + Event(QObject *sender, const QCString &signal, + const QCString &functionName); + Event() : m_type(Slot) {;} + ~Event() {;} + + enum { Slot=1000, UserFunction, Action }; //! Event types + int type() {return m_type; } + void setType(int type) { m_type = type; } + + QObject* sender() const { return m_sender; } + QObject* receiver() const { return m_receiver; } + QCString signal() const { return m_signal; } + QCString slot() const { return m_slot; } + + void setSender(QObject *o) { m_sender = o; } + void setReceiver(QObject *o) { m_receiver = o; } + void setSignal(const QCString &s) { m_signal = s; } + void setSlot(const QCString &s) { m_slot = s; } + + protected: + QGuardedPtr<QObject> m_sender; + QCString m_signal; + QGuardedPtr<QObject> m_receiver; + QCString m_slot; + int m_type; +}; + +class KEXICORE_EXPORT EventList : protected QValueList<Event*> +{ + public: + EventList() {;} + ~EventList() {;} + + /*! Adds an event in list. Other overload are available, so that + other classes don't have to use Event class in simple cases. */ + void addEvent(Event *event); + void addEvent(QObject *sender, const QCString &signal, QObject *receiver, const QCString &slot); + void addEvent(QObject *sender, const QCString &signal, const QCString &action); + /*! Removes the Event \a event from the FormScript's list. */ + void removeEvent(Event *event); + + /*! \return A list of events related to widget \a name (ie where Event::sender() + or Event::receiver() == name). */ + EventList* allEventsForObject(QObject *object); + /*! Replace all ocurrences of \a oldname with \a newName inside the list. */ + //void renameWidget(const QCString &oldName, const QCString &newName); + /*! Removes all events related to widget \a name. Called eg when widget is destroyed. */ + void removeAllEventsForObject(QObject *object); + + // make some QValueList function accessible by other classes + QValueListConstIterator<Event*> constBegin() const { return QValueList<Event*>::constBegin(); } + QValueListConstIterator<Event*> constEnd() const { return QValueList<Event*>::constEnd(); } + bool isEmpty() const { return QValueList<Event*>::isEmpty(); } +}; + + +#endif + diff --git a/kexi/core/kexiguimsghandler.cpp b/kexi/core/kexiguimsghandler.cpp new file mode 100644 index 00000000..af5f3a6e --- /dev/null +++ b/kexi/core/kexiguimsghandler.cpp @@ -0,0 +1,176 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexiguimsghandler.h" + +#include "kexi.h" +#include <kexidb/utils.h> +#include <kexiutils/utils.h> + +#include <kmessagebox.h> +#include <kdialogbase.h> + +KexiGUIMessageHandler::KexiGUIMessageHandler(QWidget *parent) +: KexiDB::MessageHandler(parent) +{ +} + +KexiGUIMessageHandler::~KexiGUIMessageHandler() +{ +} + +/*virtual*/ +void +KexiGUIMessageHandler::showErrorMessage(KexiDB::Object *obj, + const QString& msg) +{ + QString _msg(msg); + if (!obj) { + showErrorMessage(_msg); + return; + } + QString details; + KexiDB::getHTMLErrorMesage(obj, _msg, details); + showErrorMessage(_msg, details); +} + +/*virtual*/ +void +KexiGUIMessageHandler::showErrorMessage(const QString &title, const QString &details) +{ + showMessage(Error, title, details); +} + +void +KexiGUIMessageHandler::showSorryMessage(const QString &title, const QString &details) +{ + showMessage(Sorry, title, details); +} + +void KexiGUIMessageHandler::showErrorMessage(const QString &msg, const QString &details, + KexiDB::Object *obj) +{ + QString _msg(msg); + if (!obj) { + showErrorMessage(_msg, details); + return; + } + QString _details(details); + KexiDB::getHTMLErrorMesage(obj, _msg, _details); + showErrorMessage(_msg, _details); +} + +void +KexiGUIMessageHandler::showErrorMessage(Kexi::ObjectStatus *status) +{ + showErrorMessage("", status); +} + +void +KexiGUIMessageHandler::showErrorMessage(const QString &message, Kexi::ObjectStatus *status) +{ + if (status && status->error()) { + QString msg(message); + if (msg.isEmpty() || msg==status->message) { + 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("<br><br>") + status->message); + desc = status->description; + } + } + showErrorMessage(msg, desc, status->dbObject()); + } + else { + showErrorMessage(message); + } + status->clearStatus(); +} + +void +KexiGUIMessageHandler::showMessage(MessageType type, + const QString &title, const QString &details, const QString& dontShowAgainName) +{ + if (!m_enableMessages) + return; + + //'wait' cursor is a nonsense now + KexiUtils::removeWaitCursor(); + + QString msg(title); + if (title.isEmpty()) + msg = i18n("Unknown error"); + msg = "<qt><p>"+msg+"</p>"; + if (!details.isEmpty()) { + switch (type) { + case Error: + KMessageBox::detailedError(m_messageHandlerParentWidget, msg, details); + break; + case Warning: + showWarningContinueMessage(title, details, dontShowAgainName); + break; + default: //Sorry + KMessageBox::detailedSorry(m_messageHandlerParentWidget, msg, details); + } + } + else { + KMessageBox::messageBox(m_messageHandlerParentWidget, + type==Error ? KMessageBox::Error : KMessageBox::Sorry, msg); + } +} + +void KexiGUIMessageHandler::showWarningContinueMessage(const QString &title, const QString &details, + const QString& dontShowAgainName) +{ + if (!KMessageBox::shouldBeShownContinue(dontShowAgainName)) + return; + KDialogBase *dialog = new KDialogBase( + i18n("Warning"), KDialogBase::Yes, KDialogBase::Yes, KDialogBase::No, + m_messageHandlerParentWidget, "warningContinue", true, true, KStdGuiItem::cont() ); + bool checkboxResult = false; + KMessageBox::createKMessageBox(dialog, QMessageBox::Warning, + title + (details.isEmpty() ? QString::null : (QString("\n")+details)), QStringList(), + dontShowAgainName.isEmpty() ? QString::null : i18n("Do not show this message again"), + &checkboxResult, 0); + if (checkboxResult) + KMessageBox::saveDontShowAgainContinue(dontShowAgainName); +} + +int KexiGUIMessageHandler::askQuestion( const QString& message, + KMessageBox::DialogType dlgType, KMessageBox::ButtonCode defaultResult, + const KGuiItem &buttonYes, + const KGuiItem &buttonNo, + const QString &dontShowAskAgainName, + int options ) +{ + Q_UNUSED(defaultResult); + if (KMessageBox::WarningContinueCancel == dlgType) + return KMessageBox::warningContinueCancel(m_messageHandlerParentWidget, + message, QString::null, buttonYes, dontShowAskAgainName, options); + else + return KMessageBox::messageBox(m_messageHandlerParentWidget, + dlgType, message, QString::null, buttonYes, buttonNo, dontShowAskAgainName, options); +} + diff --git a/kexi/core/kexiguimsghandler.h b/kexi/core/kexiguimsghandler.h new file mode 100644 index 00000000..6105f311 --- /dev/null +++ b/kexi/core/kexiguimsghandler.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIGUIMSGHANDLER_H +#define KEXIGUIMSGHANDLER_H + +#include <core/kexi.h> +#include <kexidb/msghandler.h> + +class KEXICORE_EXPORT KexiGUIMessageHandler : public KexiDB::MessageHandler +{ + public: + KexiGUIMessageHandler(QWidget *parent = 0); + virtual ~KexiGUIMessageHandler(); + virtual void showErrorMessage(const QString &title, const QString &details = QString::null); + virtual void showErrorMessage(KexiDB::Object *obj, const QString& msg = QString::null); + + void showErrorMessage(const QString&,const QString&,KexiDB::Object *obj); + void showErrorMessage(Kexi::ObjectStatus *status); + void showErrorMessage(const QString &message, Kexi::ObjectStatus *status); + + /*! Displays a "Sorry" message with \a title text and optional \a details. */ + void showSorryMessage(const QString &title, const QString &details = QString::null); + + /*! Displays a message of a type \a type, with \a title text and optional \a details. + \a dontShowAgainName can be specified to add "Do not show again" option if \a type is Warning. */ + virtual void showMessage(MessageType type, const QString &title, const QString &details, + const QString& dontShowAgainName = QString::null); + + /*! Displays a Warning message with \a title text and optional \a details + with "Continue" button instead "OK". + \a dontShowAgainName can be specified to add "Do not show again" option. */ + virtual void showWarningContinueMessage(const QString &title, const QString &details = QString::null, + const QString& dontShowAgainName = QString::null); + + /*! Interactively asks a question using KMessageBox. + See KexiDB::MessageHandler::askQuestion() for details. */ + virtual int askQuestion( const QString& message, + KMessageBox::DialogType dlgType, KMessageBox::ButtonCode defaultResult, + const KGuiItem &buttonYes=KStdGuiItem::yes(), + const KGuiItem &buttonNo=KStdGuiItem::no(), + const QString &dontShowAskAgainName = QString::null, + int options = KMessageBox::Notify ); +}; + +#endif diff --git a/kexi/core/kexiinternalpart.cpp b/kexi/core/kexiinternalpart.cpp new file mode 100644 index 00000000..fcbb8f17 --- /dev/null +++ b/kexi/core/kexiinternalpart.cpp @@ -0,0 +1,209 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexiinternalpart.h" + +#include "kexidialogbase.h" +#include "kexiviewbase.h" +#include "keximainwindow.h" + +#include <qasciidict.h> +#include <qdialog.h> + +#include <kdebug.h> +#include <klibloader.h> +#include <klocale.h> +#include <ktrader.h> +#include <kparts/componentfactory.h> +#include <kexidb/msghandler.h> + +//! @internal +class KexiInternalPartManager +{ + public: + KexiInternalPartManager() + : m_parts(101, false) + { + m_parts.setAutoDelete(false); + } + + KexiInternalPart* findPart(KexiDB::MessageHandler *msgHdr, const char* partName) + { + KexiInternalPart *part = m_parts[partName]; + if (!part) { + QCString fullname("kexihandler_"); + fullname += QCString(partName).lower(); + part = KParts::ComponentFactory::createInstanceFromLibrary<KexiInternalPart>( + fullname, 0, fullname); + if (!part) { + if (msgHdr) + msgHdr->showErrorMessage(i18n("Could not load \"%1\" plugin.").arg(partName)); + } + else + m_parts.insert(partName, part); + } + return part; + } + + private: + + QAsciiDict<KexiInternalPart> m_parts; +}; + +KexiInternalPartManager internalPartManager; + +//---------------------------------------------- + +KexiInternalPart::KexiInternalPart(QObject *parent, const char *name, const QStringList &) + : QObject(parent, name) + , m_uniqueDialog(true) + , m_cancelled(false) +{ +} + +KexiInternalPart::~KexiInternalPart() +{ +} + +//static +const KexiInternalPart * +KexiInternalPart::part(KexiDB::MessageHandler *msgHdr, const char* partName) +{ + return internalPartManager.findPart(msgHdr, partName); +} + +//static +QWidget* KexiInternalPart::createWidgetInstance(const char* partName, + const char* widgetClass, KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, + QWidget *parent, const char *objName, QMap<QString,QString>* args) +{ + KexiInternalPart *part = internalPartManager.findPart(msgHdr, partName); + if (!part) + return 0; //fatal! + return part->createWidget(widgetClass, mainWin, parent, objName ? objName : partName, args); +} + +KexiDialogBase* KexiInternalPart::findOrCreateKexiDialog( + KexiMainWindow* mainWin, const char *objName) +{ + if (m_uniqueDialog && !m_uniqueWidget.isNull()) + return dynamic_cast<KexiDialogBase*>((QWidget*)m_uniqueWidget); +// KexiDialogBase *dlg = createDialog(mainWin, objName); + KexiDialogBase * dlg = new KexiDialogBase(mainWin, ""); + KexiViewBase *view = createView(mainWin, 0, objName); + if (!view) + return 0; + +// dlg->show(); + + if (m_uniqueDialog) + m_uniqueWidget = dlg; //recall unique! + dlg->addView(view); + dlg->setCaption( view->caption() ); + dlg->setTabCaption( view->caption() ); + dlg->resize(view->sizeHint()); + dlg->setMinimumSize(view->minimumSizeHint().width(),view->minimumSizeHint().height()); + dlg->setId( mainWin->generatePrivateID() ); + dlg->registerDialog(); + return dlg; +} + +//static +KexiDialogBase* KexiInternalPart::createKexiDialogInstance( + const char* partName, + KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, const char *objName) +{ + KexiInternalPart *part = internalPartManager.findPart(msgHdr, partName); + if (!part) { + kdDebug() << "KexiInternalPart::createDialogInstance() !part" << endl; + return 0; //fatal! + } + return part->findOrCreateKexiDialog(mainWin, objName ? objName : partName); +} + +//static +QDialog* KexiInternalPart::createModalDialogInstance(const char* partName, + const char* dialogClass, KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, + const char *objName, QMap<QString,QString>* args) +{ + KexiInternalPart *part = internalPartManager.findPart(msgHdr, partName); + if (!part) { + kdDebug() << "KexiInternalPart::createDialogInstance() !part" << endl; + return 0; //fatal! + } + QWidget *w; + if (part->uniqueDialog() && !part->m_uniqueWidget.isNull()) + w = part->m_uniqueWidget; + else + w = part->createWidget(dialogClass, mainWin, mainWin, objName ? objName : partName, args); + + if (dynamic_cast<QDialog*>(w)) { + if (part->uniqueDialog()) + part->m_uniqueWidget = w; + return dynamic_cast<QDialog*>(w); + } + //sanity + if (! (part->uniqueDialog() && !part->m_uniqueWidget.isNull())) + delete w; + return 0; +} + +//static +bool KexiInternalPart::executeCommand(const char* partName, + KexiMainWindow* mainWin, const char* commandName, QMap<QString,QString>* args) +{ + KexiInternalPart *part = internalPartManager.findPart(0, partName); + if (!part) { + kdDebug() << "KexiInternalPart::createDialogInstance() !part" << endl; + return 0; //fatal! + } + return part->executeCommand(mainWin, commandName, args); +} + +QWidget* KexiInternalPart::createWidget(const char* widgetClass, KexiMainWindow* mainWin, + QWidget * parent, const char * objName, QMap<QString,QString>* args) +{ + Q_UNUSED(widgetClass); + Q_UNUSED(mainWin); + Q_UNUSED(parent); + Q_UNUSED(objName); + Q_UNUSED(args); + return 0; +} + +KexiViewBase* KexiInternalPart::createView(KexiMainWindow* mainWin, QWidget * parent, + const char * objName) +{ + Q_UNUSED(mainWin); + Q_UNUSED(parent); + Q_UNUSED(objName); + return 0; +} + +bool KexiInternalPart::executeCommand(KexiMainWindow* mainWin, const char* commandName, + QMap<QString,QString>* args) +{ + Q_UNUSED(mainWin); + Q_UNUSED(commandName); + Q_UNUSED(args); + return false; +} + +#include "kexiinternalpart.moc" diff --git a/kexi/core/kexiinternalpart.h b/kexi/core/kexiinternalpart.h new file mode 100644 index 00000000..232c3f84 --- /dev/null +++ b/kexi/core/kexiinternalpart.h @@ -0,0 +1,159 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2004-2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIINTERNALPART_H +#define KEXIINTERNALPART_H + +#include <qobject.h> +#include <qguardedptr.h> +#include <qvariant.h> + +class KexiMainWindow; +class KexiDialogBase; +class KexiViewBase; + +namespace KexiDB { + class MessageHandler; +} + +class QWidget; + +/** + * @short A prototype for Kexi Internal Parts (plugins) implementation. + * + * Internal Kexi parts are parts that are not available for users, but loaded + * internally be application when needed. Example of such part is Relations Window. + * The internal part instance is unique and has no explicitly stored data. + * Parts may be able to create widgets or/and dialogs, depending on implementation + * (createWidgetInstance(), createDialogInstance()). + * Parts can have unique flag set for dialogs (true by default) + * - then a dialog created by createDialogInstance() is unique. + */ +class KEXICORE_EXPORT KexiInternalPart : public QObject +{ + Q_OBJECT + + public: + KexiInternalPart(QObject *parent, const char *name, const QStringList &); + virtual ~KexiInternalPart(); + + KexiDialogBase *instance(KexiMainWindow *parent); + + /*! Creates a new widget instance using part \a partName. + \a widgetClass is a pseudo class used in case when the part offers more + than one widget type. + \a msgHdr is a message handler for displaying error messages. + \a args is two-way optional argument: it can contain custom options used + on widget's creation. Depending on implementation, the created widget can write its + state (e.g. result or status information) back to this argument. + Created widget will have assigned \a parent widget and \a objName name. */ + static QWidget* createWidgetInstance(const char* partName, const char* widgetClass, + KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, + QWidget *parent, const char *objName = 0, QMap<QString,QString>* args = 0); + + /*! For convenience. */ + static QWidget* createWidgetInstance(const char* partName, + KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, + QWidget *parent, const char *objName = 0, QMap<QString,QString>* args = 0) + { return createWidgetInstance(partName, 0, msgHdr, mainWin, parent, objName, args); } + + /*! Creates a new dialog instance. If such instance already exists, + and is unique (see uniqueDialog()) it is just returned. + The part knows about destroying its dialog instance, (if it is uinque), + so on another call the dialog will be created again. + \a msgHdr is a message handler for displaying error messages. + The dialog is assigned to \a mainWin as its parent, + and \a objName name is set. */ + static KexiDialogBase* createKexiDialogInstance(const char* partName, + KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, const char *objName = 0); + + /*! Creates a new modal dialog instance (QDialog or a subclass). + If such instance already exists, and is unique (see uniqueDialog()) + it is just returned. + \a dialogClass is a pseudo class used in case when the part offers more + than one dialog type. + \a msgHdr is a message handler for displaying error messages. + \a args is two-way optional argument: it can contain custom options used + on widget's creation. Depending on implementation, the created dialog can write its + state (e.g. result or status information) back to this argument. + The part knows about destroying its dialog instance, (if it is uinque), + so on another call the dialog will be created again. + The dialog is assigned to \a mainWin as its parent, + and \a objName name is set. */ + static QDialog* createModalDialogInstance(const char* partName, + const char* dialogClass, KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, + const char *objName = 0, QMap<QString,QString>* args = 0); + + /*! Adeded For convenience. */ + static QDialog* createModalDialogInstance(const char* partName, + KexiDB::MessageHandler *msgHdr, KexiMainWindow* mainWin, const char *objName = 0, + QMap<QString,QString>* args = 0) + { return createModalDialogInstance(partName, 0, msgHdr, mainWin, objName, args); } + + /*! Executes a command \a commandName (usually nonvisual) using part called \a partName. + The result can be put into the \a args. \return true on successful calling. */ + static bool executeCommand(const char* partName, + KexiMainWindow* mainWin, const char* commandName, QMap<QString,QString>* args = 0); + + /*! \return internal part of a name \a partName. Shouldn't be usable. */ + static const KexiInternalPart* part(KexiDB::MessageHandler *msgHdr, const char* partName); + + /*! \return true if the part can create only one (unique) dialog. */ + inline bool uniqueDialog() const { return m_uniqueDialog; } + + /*! \return true if the part creation has been cancelled (eg. by a user) + so it wasn't an error. Internal part's impelmentation should set it to true when needed. + False by default. */ + inline bool cancelled() const { return m_cancelled; } + + protected: + /*! Used internally */ + KexiDialogBase *findOrCreateKexiDialog(KexiMainWindow* mainWin, + const char *objName); + + /*! Reimplement this if your internal part has to return widgets + or QDialog objects. */ + virtual QWidget *createWidget(const char* widgetClass, KexiMainWindow* mainWin, + QWidget * parent, const char * objName = 0, QMap<QString,QString>* args = 0); + +// //! Reimplement this if your internal part has to return dialogs +// virtual KexiDialogBase *createDialog(KexiMainWindow* /*mainWin*/, +// const char * /*objName*/ =0) +// { return 0; } + + /*! Reimplement this if your internal part has to return a view object. */ + virtual KexiViewBase *createView(KexiMainWindow* mainWin, QWidget * parent, + const char *objName = 0); + + /*! Reimplement this if your internal part has to execute a command \a commandName + (usually nonvisual). Arguments are put into \a args and the result can be put into the \a args. + \return true on successful calling. */ + virtual bool executeCommand(KexiMainWindow* mainWin, const char* commandName, + QMap<QString,QString>* args = 0); + + //! Unique dialog - we're using guarded ptr for the dialog so can know if it has been closed + QGuardedPtr<QWidget> m_uniqueWidget; + + bool m_uniqueDialog : 1; //!< true if createDialogInstance() should return only one dialog + + bool m_cancelled : 1; //!< Used in cancelled() +}; + +#endif diff --git a/kexi/core/keximainwindow.cpp b/kexi/core/keximainwindow.cpp new file mode 100644 index 00000000..bf66e638 --- /dev/null +++ b/kexi/core/keximainwindow.cpp @@ -0,0 +1,36 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "keximainwindow.h" + +#include <kdebug.h> + +KexiMainWindow::KexiMainWindow() + : KMdiMainFrm(0L, "keximainwindow") + , KexiSharedActionHost(this) +{ +} + +KexiMainWindow::~KexiMainWindow() +{ +} + +#include "keximainwindow.moc" + diff --git a/kexi/core/keximainwindow.h b/kexi/core/keximainwindow.h new file mode 100644 index 00000000..ec1db633 --- /dev/null +++ b/kexi/core/keximainwindow.h @@ -0,0 +1,189 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIMAINWINDOW_H +#define KEXIMAINWINDOW_H + +#include <qmap.h> +#include <qintdict.h> + +#include <kmdimainfrm.h> +#include <kexiutils/tristate.h> + +#include "kexisharedactionhost.h" +#include "kexi.h" + +class KexiDialogBase; +class KexiProject; +namespace KexiPart { + class Item; +} + +/** + * @short Kexi's main window interface + * This interface is implemented by KexiMainWindowImpl class. + * KexiMainWindow offers simple features what lowers cross-dependency (and also avoids + * circular dependencies between Kexi modules). + */ +class KEXICORE_EXPORT KexiMainWindow : public KMdiMainFrm, public KexiSharedActionHost +{ + Q_OBJECT + public: + //! Used by printActionForItem() + enum PrintActionType { + PrintItem, + PreviewItem, + PageSetupForItem + }; + + KexiMainWindow(); + virtual ~KexiMainWindow(); + + //! Project data of currently opened project or NULL if no project here yet. + virtual KexiProject *project() = 0; + + /*! Registers dialog \a dlg for watching and adds it to the main window's stack. */ + virtual void registerChild(KexiDialogBase *dlg) = 0; + + virtual QPopupMenu* findPopupMenu(const char *popupName) = 0; + + /*! Generates ID for private "document" like Relations window. + Private IDs are negative numbers (while ID regular part instance's IDs are >0) + Private means that the object is not stored as-is in the project but is somewhat + generated and in most cases there is at most one unique instance document of such type (part). + To generate this ID, just app-wide internal counter is used. */ + virtual int generatePrivateID() = 0; + + /*! \return a list of all actions defined by application. + Not all of them are shared. Don't use plug these actions + in your windows by hand but user methods from KexiViewBase! */ + virtual KActionPtrList allActions() const = 0; + + /*! \return currently active dialog (window) od 0 if there is no active dialog. */ + virtual KexiDialogBase* currentDialog() const = 0; + + /*! \return true if this window is in the User Mode. */ + virtual bool userMode() const = 0; + + signals: + //! Emitted to make sure the project can be close. + //! Connect a slot here and set \a cancel to true to cancel the closing. + void acceptProjectClosingRequested(bool& cancel); + + //! Emitted before closing the project (and destroying all it's data members). + //! You can do you cleanup of your structures here. + void beforeProjectClosing(); + + //! Emitted after closing the project. + void projectClosed(); + + public slots: + /*! Creates new object of type defined by \a info part info. + \a openingCancelled is set to true is opening has been cancelled. + \return true on success. */ + virtual bool newObject( KexiPart::Info *info, bool& openingCancelled ) = 0; + + //! Opens object pointed by \a item in a view \a viewMode + virtual KexiDialogBase * openObject(KexiPart::Item *item, int viewMode, + bool &openingCancelled, QMap<QString,QString>* staticObjectArgs = 0, + QString* errorMessage = 0) = 0; + + //! For convenience + virtual KexiDialogBase * openObject(const QCString& mime, const QString& name, + int viewMode, bool &openingCancelled, QMap<QString,QString>* staticObjectArgs = 0) = 0; + + /*! Closes the object for \a item. + \return true on success (closing can be dealyed though), false on failure and cancelled + if the object has "opening" job assigned. */ + virtual tristate closeObject(KexiPart::Item* item) = 0; + + /*! Called to accept property butter editing. */ + virtual void acceptPropertySetEditing() = 0; + + /*! Received information from active view that \a dlg has switched + its property set, so property editor contents should be reloaded. + If \a force is true, property editor's data is reloaded even + if the currently pointed property set is the same as before. + If \a preservePrevSelection is true and there was a property set + set before call, previously selected item will be preselected + in the editor (if found). */ + virtual void propertySetSwitched(KexiDialogBase *dlg, bool force=false, + bool preservePrevSelection = true, const QCString& propertyToSelect = QCString()) = 0; + + /*! Saves dialog's \a dlg data. If dialog's data is never saved, + user is asked for name and title, before saving (see getNewObjectInfo()). + \return true on successul saving or false on error. + If saving was cancelled by user, cancelled is returned. + \a messageWhenAskingForName is a i18n'ed text that will be visible + within name/caption dialog (see KexiNameDialog), which is popped + up for never saved objects. */ + virtual tristate saveObject( KexiDialogBase *dlg, + const QString& messageWhenAskingForName = QString::null, bool dontAsk = false ) = 0; + + /*! Closes dialog \a dlg. If dialog's data (see KexiDialoBase::dirty()) is unsaved, + used will be asked if saving should be perforemed. + \return true on successull closing or false on closing error. + If closing was cancelled by user, cancelled is returned. */ + virtual tristate closeDialog(KexiDialogBase *dlg) = 0; + + /*! Displays a dialog for entering object's name and title. + Used on new object saving. + \return true on successul closing or cancelled on cancel returned. + It's unlikely to have false returned here. + \a messageWhenAskingForName is a i18n'ed text that will be visible + within name/caption dialog (see KexiNameDialog). + If \a allowOverwriting is true, user will be asked for existing + object's overwriting, else it will be impossible to enter + a name of existing object. + You can check \a allowOverwriting after calling this method. + If it's true, user agreed on overwriting, if it's false, user picked + nonexisting name, so no overwrite will be needed. */ + virtual tristate getNewObjectInfo( KexiPart::Item *partItem, KexiPart::Part *part, + bool& allowOverwriting, const QString& messageWhenAskingForName = QString::null ) = 0; + + /*! Highlights object of mime \a mime and name \a name. + This can be done in the Project Navigator or so. + If a window for the object is opened (in any mode), it should be raised. */ + virtual void highlightObject(const QCString& mime, const QCString& name) = 0; + + //! Shows "print" dialog for \a item. + //! \return true on success. + virtual tristate printItem(KexiPart::Item* item) = 0; + + //! Shows "print preview" dialog. + //! \return true on success. + virtual tristate printPreviewForItem(KexiPart::Item* item) = 0; + + //! Shows "page setup" dialog for \a item. + //! \return true on success and cancelled when the action was cancelled. + virtual tristate showPageSetupForItem(KexiPart::Item* item) = 0; + + /*! Executes custom action for the main window, usually provided by a plugin. + Also used by KexiFormEventAction. */ + virtual tristate executeCustomActionForObject(KexiPart::Item* item, const QString& actionName) = 0; + + protected slots: + virtual void slotObjectRenamed(const KexiPart::Item &item, const QCString& oldName) = 0; + +}; + + +#endif + diff --git a/kexi/core/kexipart.cpp b/kexi/core/kexipart.cpp new file mode 100644 index 00000000..3f97e9c3 --- /dev/null +++ b/kexi/core/kexipart.cpp @@ -0,0 +1,452 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexipart.h" +#include "kexipartinfo.h" +#include "kexipartitem.h" +#include "kexistaticpart.h" +#include "kexidialogbase.h" +#include "kexiviewbase.h" + +#include "kexipartguiclient.h" +#include "keximainwindow.h" +//#include "kexipartdatasource.h" +#include "kexi.h" + +#include <kexidb/connection.h> +#include <kexiutils/identifier.h> +#include <kexiutils/utils.h> + +#include <qwidgetstack.h> + +#include <kiconloader.h> +#include <kdebug.h> +#include <kmessagebox.h> + +namespace KexiPart { +//! @internal +class PartPrivate +{ +public: + PartPrivate() + : instanceActionsInitialized(false) + { + } + + //! Helper, used in Part::openInstance() + tristate askForOpeningInTextMode(KexiDialogBase *dlg, KexiPart::Item &item, + int supportedViewModes, int viewMode) + { + if (viewMode != Kexi::TextViewMode + && supportedViewModes & Kexi::TextViewMode + && dlg->tempData()->proposeOpeningInTextViewModeBecauseOfProblems) + { + //ask + KexiUtils::WaitCursorRemover remover; + //! @todo use message handler for this to enable non-gui apps + QString singleStatusString( dlg->singleStatusString() ); + if (!singleStatusString.isEmpty()) + singleStatusString.prepend(QString("\n\n")+i18n("Details:")+" "); + if (KMessageBox::No==KMessageBox::questionYesNo(0, + ((viewMode == Kexi::DesignViewMode) + ? i18n("Object \"%1\" could not be opened in Design View.").arg(item.name()) + : i18n("Object could not be opened in Data View."))+"\n" + + i18n("Do you want to open it in Text View?") + singleStatusString, 0, + KStdGuiItem::open(), KStdGuiItem::cancel())) + { + // dlg->close(); //this will destroy dlg + return false; + } + return true; + } + return cancelled; + } + + bool instanceActionsInitialized : 1; +}; +} + +//---------------------------------------------------------------- + +using namespace KexiPart; + +Part::Part(QObject *parent, const char *name, const QStringList &) +: QObject(parent, name) +, m_guiClient(0) +, m_registeredPartID(-1) //no registered ID by default +, d(new PartPrivate()) +{ + m_info = 0; + m_supportedViewModes = Kexi::DataViewMode | Kexi::DesignViewMode; + m_supportedUserViewModes = Kexi::DataViewMode; + m_mainWin = 0; + m_newObjectsAreDirty = false; +} + +Part::Part(QObject* parent, StaticInfo *info) +: QObject(parent, "StaticPart") +, m_guiClient(0) +, m_registeredPartID(-1) //no registered ID by default +, d(new PartPrivate()) +{ + m_info = info; + m_supportedViewModes = Kexi::DesignViewMode; + m_supportedUserViewModes = 0; + m_mainWin = 0; + m_newObjectsAreDirty = false; +} + +Part::~Part() +{ + delete d; +} + +void Part::createGUIClients(KexiMainWindow *win) +{ + m_mainWin = win; + if (!m_guiClient) { + //create part's gui client + m_guiClient = new GUIClient(m_mainWin, this, false, "part"); + + //default actions for part's gui client: + KAction *act = new KAction(m_names["instanceCaption"]+"...", info()->createItemIcon(), 0, this, + SLOT(slotCreate()), m_mainWin->actionCollection(), + KexiPart::nameForCreateAction(*info())); + act->plug( m_mainWin->findPopupMenu("insert") ); +// new KAction(m_names["instance"]+"...", info()->itemIcon(), 0, this, +// SLOT(create()), m_guiClient->actionCollection(), (info()->objectName()+"part_create").latin1()); + //let init specific actions for parts +// initPartActions( m_guiClient->actionCollection() ); + m_mainWin->guiFactory()->addClient(m_guiClient); //this client is added permanently + + //create part instance's gui client +// m_instanceGuiClient = new GUIClient(win, this, true); + + //default actions for part instance's gui client: + //NONE + //let init specific actions for part instances + for (int mode = 1; mode <= 0x01000; mode <<= 1) { + if (m_supportedViewModes & mode) { + GUIClient *instanceGuiClient = new GUIClient(m_mainWin, + this, true, Kexi::nameForViewMode(mode).latin1()); + m_instanceGuiClients.insert(mode, instanceGuiClient); +// initInstanceActions( mode, instanceGuiClient->actionCollection() ); + } + } + // also add an instance common for all modes (mode==0) + GUIClient *instanceGuiClient = new GUIClient(m_mainWin, this, true, "allViews"); + m_instanceGuiClients.insert(Kexi::AllViewModes, instanceGuiClient); +// initInstanceActions( Kexi::AllViewModes , instanceGuiClient->actionCollection() ); + +//todo + initPartActions(); +// initActions(); + } +} + +KActionCollection* Part::actionCollectionForMode(int viewMode) const +{ + KXMLGUIClient *cli = m_instanceGuiClients[viewMode]; + return cli ? cli->actionCollection() : 0; +} + +KAction* Part::createSharedAction(int mode, const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name, + const char *subclassName) +{ + GUIClient *instanceGuiClient = m_instanceGuiClients[mode]; + if (!instanceGuiClient) { + kdDebug() << "KexiPart::createSharedAction(): no gui client for mode " << mode << "!" << endl; + return 0; + } + return m_mainWin->createSharedAction(text, pix_name, cut, name, + instanceGuiClient->actionCollection(), subclassName); +} + +KAction* Part::createSharedPartAction(const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name, + const char *subclassName) +{ + if (!m_guiClient) + return 0; + return m_mainWin->createSharedAction(text, pix_name, cut, name, + m_guiClient->actionCollection(), subclassName); +} + +KAction* Part::createSharedToggleAction(int mode, const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name) +{ + return createSharedAction(mode, text, pix_name, cut, name, "KToggleAction"); +} + +KAction* Part::createSharedPartToggleAction(const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name) +{ + return createSharedPartAction(text, pix_name, cut, name, "KToggleAction"); +} + +/*KAction* Part::sharedAction(int mode, const char* name, const char *classname) +{ + GUIClient *instanceGuiClient = m_instanceGuiClients[mode]; + if (!instanceGuiClient) { + kdDebug() << "KexiPart::createSharedAction(): no gui client for mode " << mode << "!" << endl; + return 0; + } + return instanceGuiClient->actionCollection()->action(name, classname); +} + +KAction* Part::sharedPartAction(int mode, const char* name, const char *classname) +{ + if (!m_guiClient) + return 0; + return m_guiClient->actionCollection()->action(name, classname); +}*/ + +void Part::setActionAvailable(const char *action_name, bool avail) +{ + QIntDictIterator<GUIClient> it( m_instanceGuiClients ); + for (;it.current();++it) { + KAction *act = it.current()->actionCollection()->action(action_name); + if (act) { + act->setEnabled(avail); + return; + } + } + + m_mainWin->setActionAvailable(action_name, avail); +} + +KexiDialogBase* Part::openInstance(KexiMainWindow *win, KexiPart::Item &item, int viewMode, + QMap<QString,QString>* staticObjectArgs) +{ + //now it's the time for creating instance actions + if (!d->instanceActionsInitialized) { + initInstanceActions(); + d->instanceActionsInitialized = true; + } + + m_status.clearStatus(); +// KexiDialogBase *dlg = createInstance(win,item,viewMode); +// if (!dlg) +// return 0; +// QString capt = QString("%1 : %2").arg(item.name()).arg(instanceName()); + KexiDialogBase *dlg = new KexiDialogBase(win); + dlg->m_supportedViewModes = m_supportedViewModes; +// dlg->m_neverSaved = item.neverSaved(); +// dlg->m_currentViewMode = viewMode; + dlg->m_part = this; + dlg->m_item = &item; + dlg->updateCaption(); + + KexiDB::SchemaData sdata(m_info->projectPartID()); + sdata.setName( item.name() ); + sdata.setCaption( item.caption() ); + sdata.setDescription( item.description() ); + +/*! @todo js: apply settings for caption displaying method; there can be option for + - displaying item.caption() as caption, if not empty, without instanceName + - displaying the same as above in tabCaption (or not) */ +// dlg->setCaption( capt ); +// dlg->setTabCaption( item.name() ); + dlg->setId(item.identifier()); //not needed, but we did it +//moved down dlg->registerDialog(); + dlg->setIcon( SmallIcon( dlg->itemIcon() ) ); + if (dlg->mdiParent()) + dlg->mdiParent()->setIcon( *dlg->icon() ); +// if (dlg->mainWidget()) +// dlg->mainWidget()->setIcon( *dlg->icon() ); + dlg->stack()->setIcon( *dlg->icon() ); + dlg->m_tempData = createTempData(dlg); + + if (!item.neverSaved()) { + //we have to load schema data for this dialog + dlg->m_schemaData = loadSchemaData(dlg, sdata, viewMode); + if (!dlg->m_schemaData) { + //last chance: + if (false == d->askForOpeningInTextMode(dlg, item, dlg->m_supportedViewModes, viewMode)) { + delete dlg; + return 0; + } + viewMode = Kexi::TextViewMode; + dlg->m_schemaData = loadSchemaData(dlg, sdata, viewMode); + } + if (!dlg->m_schemaData) { + if (!m_status.error()) + m_status = Kexi::ObjectStatus( dlg->mainWin()->project()->dbConnection(), + i18n("Could not load object's definition."), i18n("Object design may be corrupted.")); + m_status.append( + Kexi::ObjectStatus(i18n("You can delete \"%1\" object and create it again.") + .arg(item.name()), QString::null) ); + + dlg->close(); + delete dlg; + return 0; + } + } + + bool switchingFailed = false; + bool dummy; + tristate res = dlg->switchToViewMode( viewMode, staticObjectArgs, dummy ); + if (!res) { + tristate askForOpeningInTextModeRes + = d->askForOpeningInTextMode(dlg, item, dlg->m_supportedViewModes, viewMode); +// if (viewMode==Kexi::DesignViewMode && dlg->isDesignModePreloadedForTextModeHackUsed(Kexi::TextViewMode)) +// askForOpeningInTextModeRes = cancelled; //do not try +// else + if (true == askForOpeningInTextModeRes) { + delete dlg->m_schemaData; //old one + dlg->close(); + delete dlg; + //try in text mode + return openInstance(win, item, Kexi::TextViewMode, staticObjectArgs); + } + else if (false == askForOpeningInTextModeRes) { + delete dlg->m_schemaData; //old one + dlg->close(); + delete dlg; + return 0; + } + //dlg has an error info + switchingFailed = true; + } + if (~res) + switchingFailed = true; + + if (switchingFailed) { + m_status = dlg->status(); + dlg->close(); + delete dlg; + return 0; + } + dlg->registerDialog(); //ok? + dlg->show(); + + if (dlg->mdiParent() && dlg->mdiParent()->state()==KMdiChildFrm::Normal) //only resize dialog if it is in normal state + dlg->resize(dlg->sizeHint()); + + dlg->setMinimumSize(dlg->minimumSizeHint().width(),dlg->minimumSizeHint().height()); + + //dirty only if it's a new object + if (dlg->selectedView()) + dlg->selectedView()->setDirty( m_newObjectsAreDirty ? item.neverSaved() : false ); + + return dlg; +} + +void Part::slotCreate() +{ + emit newObjectRequest( m_info ); +} + +KexiDB::SchemaData* Part::loadSchemaData(KexiDialogBase * /*dlg*/, const KexiDB::SchemaData& sdata, + int /*viewMode*/) +{ + KexiDB::SchemaData *new_schema = new KexiDB::SchemaData(); + *new_schema = sdata; + return new_schema; +} + +bool Part::loadDataBlock( KexiDialogBase *dlg, QString &dataString, const QString& dataID) +{ + if (!dlg->mainWin()->project()->dbConnection()->loadDataBlock( dlg->id(), dataString, dataID )) { + m_status = Kexi::ObjectStatus( dlg->mainWin()->project()->dbConnection(), + i18n("Could not load object's data."), i18n("Data identifier: \"%1\".").arg(dataID) ); + m_status.append( *dlg ); + return false; + } + return true; +} + +void Part::initPartActions() +{ +} + +void Part::initInstanceActions() +{ +} + +bool Part::remove(KexiMainWindow *win, KexiPart::Item &item) +{ + if (!win || !win->project() || !win->project()->dbConnection()) + return false; + KexiDB::Connection *conn = win->project()->dbConnection(); + return conn->removeObject( item.identifier() ); +} + +KexiDialogTempData* Part::createTempData(KexiDialogBase* dialog) +{ + return new KexiDialogTempData(dialog); +} + +QString Part::i18nMessage(const QCString& englishMessage, KexiDialogBase* dlg) const +{ + Q_UNUSED(dlg); + return QString(englishMessage).startsWith(":") ? QString::null : englishMessage; +} + +void Part::setupCustomPropertyPanelTabs(KTabWidget *, KexiMainWindow*) +{ +} + +QCString Part::instanceName() const +{ + // "instanceName" should be already valid identifier but we're using + // KexiUtils::string2Identifier() to be sure translators did it right. + return KexiUtils::string2Identifier(m_names["instanceName"]).lower().latin1(); +} + +QString Part::instanceCaption() const +{ + return m_names["instanceCaption"]; +} + +tristate Part::rename(KexiMainWindow *win, KexiPart::Item &item, const QString& newName) +{ + Q_UNUSED(win); + Q_UNUSED(item); + Q_UNUSED(newName); + return true; +} + +//------------------------------------------------------------------------- + + +GUIClient::GUIClient(KexiMainWindow *win, Part* part, bool partInstanceClient, const char* nameSuffix) + : QObject(part, + (part->info()->objectName() + + (nameSuffix ? QString(":%1").arg(nameSuffix) : QString())).latin1() ) + , KXMLGUIClient(win) +{ + if(!win->project()->data()->userMode()) + setXMLFile(QString::fromLatin1("kexi")+part->info()->objectName() + +"part"+(partInstanceClient?"inst":"")+"ui.rc"); + +// new KAction(part->m_names["new"], part->info()->itemIcon(), 0, this, +// SLOT(create()), actionCollection(), (part->info()->objectName()+"part_create").latin1()); + +// new KAction(i18nInstanceName+"...", part->info()->itemIcon(), 0, this, +// SLOT(create()), actionCollection(), (part->info()->objectName()+"part_create").latin1()); + +// win->guiFactory()->addClient(this); +} + + +#include "kexipart.moc" + diff --git a/kexi/core/kexipart.h b/kexi/core/kexipart.h new file mode 100644 index 00000000..4045c4c6 --- /dev/null +++ b/kexi/core/kexipart.h @@ -0,0 +1,333 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPART_H +#define KEXIPART_H + +#include <qobject.h> +#include <qmap.h> + +#include <kexiutils/tristate.h> +#include "kexi.h" +#include "keximainwindow.h" + +class KActionCollection; +class KexiDialogBase; +class KexiDialogTempData; +class KexiViewBase; +class KexiMainWindowImpl; +class KAction; +class KShortcut; +class KTabWidget; + +namespace KexiPart +{ + class Info; + class Item; + class GUIClient; + class PartPrivate; + class StaticInfo; + +/*! Official (registered) type IDs for objects like table, query, form... */ +enum ObjectTypes { + TableObjectType = KexiDB::TableObjectType, //!< 1, like in KexiDB::ObjectTypes + QueryObjectType = KexiDB::QueryObjectType, //!< 2, like in KexiDB::ObjectTypes + FormObjectType = 3, + ReportObjectType = 4, + ScriptObjectType = 5, + WebObjectType = 6, + MacroObjectType = 7, + LastObjectType = 7, //ALWAYS UPDATE THIS + + UserObjectType = 100 //!< external types +}; + +/** + * The main class for kexi frontend parts like tables, queries, forms and reports + */ +class KEXICORE_EXPORT Part : public QObject +{ + Q_OBJECT + + public: + /*! Constructor. */ + Part(QObject *parent, const char *name, const QStringList &); + /*! Destructor. */ + virtual ~Part(); + +//! @todo make it protected, outside world should use KexiProject + /*! Try to execute the part. Implementations of this \a Part + are able to overwrite this method to offer execution. + \param item The \a KexiPart::Item that should be executed. + \param sender The sender QObject which likes to execute this \a Part or + NULL if there is no sender. The KFormDesigner uses this to pass + the actual widget (e.g. the button that was pressed). + \return true if execution was successfully else false. + */ + virtual bool execute(KexiPart::Item* item, QObject* sender = 0) { + Q_UNUSED(item); + Q_UNUSED(sender); + return false; + } + + /*! \return supported modes for dialogs created by this part, i.e. a combination + of Kexi::ViewMode enum elements. + Set this member in your KexiPart subclass' ctor, if you need to override the default value + that equals Kexi::DataViewMode | Kexi::DesignViewMode, + or Kexi::DesignViewMode in case of Kexi::PartStaticPart object. + This information is used to set supported view modes for every + KexiDialogBase-derived object created by this KexiPart. */ + inline int supportedViewModes() const { return m_supportedViewModes; } + + /*! \return supported modes for dialogs created by this part in "user mode", i.e. a combination + of Kexi::ViewMode enum elements. + Set this member in your KexiPart subclass' ctor, if you need to override the default value + that equals Kexi::DataViewMode. or 0 in case of Kexi::PartStaticPart object. + This information is used to set supported view modes for every + KexiDialogBase-derived object created by this KexiPart. */ + inline int supportedUserViewModes() const { return m_supportedUserViewModes; } + +//! @todo make it protected, outside world should use KexiProject + /*! "Opens" an instance that the part provides, pointed by \a item in a mode \a viewMode. + \a viewMode is one of Kexi::ViewMode enum. + \a staticObjectArgs can be passed for static Kexi Parts. */ + KexiDialogBase* openInstance(KexiMainWindow *win, KexiPart::Item &item, + int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0); + +//! @todo make it protected, outside world should use KexiProject + /*! Removes any stored data pointed by \a item (example: table is dropped for table part). + From now this data is inaccesible, and \a item disappear. + You do not need to remove \a item, or remove object's schema stored in the database, + beacuse this will be done automatically by KexiProject after successful + call of this method. All object's data blocks are also automatically removed from database + (from "kexi__objectdata" table). + For this, a database connection associated with kexi project owned by \a win can be used. + + Database transaction is started by KexiProject before calling this method, + and it will be rolled back if you return false here. + You shouldn't use by hand transactions here. + + Default implementation just removes object from kexi__* system structures + at the database backend using KexiDB::Connection::removeObject(). */ + virtual bool remove(KexiMainWindow *win, KexiPart::Item & item); + + /*! Renames stored data pointed by \a item to \a newName + (example: table name is altered in the database). + For this, a database connection associated with kexi project owned by \a win can be used. + You do not need to change \a item, and change object's schema stored in the database, + beacuse this is automatically handled by KexiProject. + + Database transaction is started by KexiProject before calling this method, + and it will be rolled back if you return false here. + You shouldn't use by hand transactions here. + + Default implementation does nothing and returns true. */ + virtual tristate rename(KexiMainWindow *win, KexiPart::Item &item, const QString& newName); + + /*! Creates and returns a new temporary data for a dialog \a dialog. + This method is called on openInstance() once per dialog. + Reimplement this to return KexiDialogTempData subclass instance. + Default implemention just returns empty KexiDialogTempData object. */ + virtual KexiDialogTempData* createTempData(KexiDialogBase* dialog); + + /*! Creates a new view for mode \a viewMode, \a item and \a parent. The view will be + used inside \a dialog. */ + virtual KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog, + KexiPart::Item &item, int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0) = 0; + + /*! i18n'd instance name usable for displaying in gui as object's name. + The name is valid identifier - contains latin1 lowercase characters only. + @todo move this to Info class when the name could be moved as localized property + to service's .desktop file. */ + QCString instanceName() const; + + /*! i18n'd instance name usable for displaying in gui as object's caption. + @todo move this to Info class when the name could be moved as localized property + to service's .desktop file. */ + QString instanceCaption() const; + + inline Info *info() const { return m_info; } + + /*! \return part's GUI Client, so you can + create part-wide actions using this client. */ + inline GUIClient *guiClient() const { return m_guiClient; } + + /*! \return part's GUI Client, so you can + create instance-wide actions using this client. */ + inline GUIClient *instanceGuiClient(int mode = 0) const + { return m_instanceGuiClients[mode]; } + +#if 0 + /** + * @returns the datasource object of this part + * reeimplement it to make a part work as dataprovider ;) + */ + virtual DataSource *dataSource() { return 0; } +#endif + + /*! \return action collection for mode \a viewMode. */ + KActionCollection* actionCollectionForMode(int viewMode) const; + + const Kexi::ObjectStatus& lastOperationStatus() const { return m_status; } + + /*! \return i18n'd message translated from \a englishMessage. + This method is useful for messages like: + "<p>Table \"%1\" has been modified.</p>", + -- such messages can be accurately translated, + while this could not: "<p>%1 \"%2\" has been modified.</p>". + See implementation of this method in KexiTablePart to see + what strings are needed for translation. + + Default implementation returns generic \a englishMessage. + In special cases, \a englishMessage can start with ":", + to indicate that empty string will be generated if + a part does not offer a message for such \a englishMessage. + This is used e.g. in KexiMainWindowImpl::closeDialog(). + */ + virtual QString i18nMessage(const QCString& englishMessage, + KexiDialogBase* dlg) const; + + signals: + void newObjectRequest( KexiPart::Info *info ); + + protected slots: + void slotCreate(); + + protected: + //! Used by StaticPart + Part(QObject* parent, StaticInfo *info); + +// virtual KexiDialogBase* createInstance(KexiMainWindow *win, const KexiPart::Item &item, int viewMode = Kexi::DataViewMode) = 0; + + //! Creates GUICLients for this part, attached to \a win + //! This method is called from KexiMainWindow + void createGUIClients(KexiMainWindow *win); + +#if 0 + /*! For reimplementation. Create here all part actions (KAction or similar). + "Part action" is an action that is bound to given part, not for dialogs + created with this part, eg. "Open external project" action for Form part. + Default implementation does nothing. + */ + virtual void initPartActions( KActionCollection * ) {}; + + /*! For reimplementation. You should here create all instance actions (KAction or similar) + for \a mode (this method called for every value given by Kexi::ViewMode enum, + and in special cases, in the future - for user-defined part-specific modes). + Actions should be bound to action collection \a col. + "Instance action" is an action that is bound to given dialog instance (created with a part), + for specific view. \a mo; eg. "Filter data" action for DataViewMode of Table part. + By creating actions here, you can ensure that after switching to other view mode (eg. from + Design view to Data view), appropriate actions will be switched/hidden. + \a mode equal Kexi::AllViewModes means that given actions will be available for + all supported views. + Default implementation does nothing. + */ + virtual void initInstanceActions( int mode, KActionCollection *col ) {}; +#endif + + virtual void initPartActions(); + virtual void initInstanceActions(); + + virtual KexiDB::SchemaData* loadSchemaData(KexiDialogBase *dlg, + const KexiDB::SchemaData& sdata, int viewMode); + + bool loadDataBlock( KexiDialogBase *dlg, QString &dataString, const QString& dataID = QString::null); + + /*! Creates shared action for action collection declared + for 'instance actions' of this part. + See KexiSharedActionHost::createSharedAction() for details. + Pass desired KAction subclass with \a subclassName (e.g. "KToggleAction") to have + that subclass allocated instead just KAction (what is the default). */ + KAction* createSharedAction(int mode, const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name, + const char *subclassName = 0); + + /*! Convenience version of above method - creates shared toggle action. */ + KAction* createSharedToggleAction(int mode, const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name); + + /*! Creates shared action for action collection declared + for 'part actions' of this part. + See KexiSharedActionHost::createSharedAction() for details. + Pass desired KAction subclass with \a subclassName (e.g. "KToggleAction") to have + that subclass allocated instead just KAction (what is the default). */ + KAction* createSharedPartAction(const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name, + const char *subclassName = 0); + + /*! Convenience version of above method - creates shared toggle action + for 'part actions' of this part. */ + KAction* createSharedPartToggleAction(const QString &text, + const QString &pix_name, const KShortcut &cut, const char *name); + + void setActionAvailable(const char *action_name, bool avail); + + inline void setInfo(Info *info) { m_info = info; } + + /*! This method can be reimplemented to setup additional tabs + in the property editor panel. Default implementation does nothing. + This method is called whenever current dialog (KexiDialogBase) is switched and + type (mime type) of its contents differs from previous one. + For example, if a user switched from Table Designer to Form Designer, + additional tab containing Form Designer's object tree should be shown. */ + virtual void setupCustomPropertyPanelTabs(KTabWidget *tab, KexiMainWindow* mainWin); + + //! Set of i18n'd action names for, initialised on KexiPart::Part subclass ctor + //! The names are useful because the same action can have other name for each part + //! E.g. "New table" vs "New query" can have different forms for some languages... + QMap<QString,QString> m_names; + + /*! Supported modes for dialogs created by this part. + @see supportedViewModes() */ + int m_supportedViewModes; + + /*! Supported modes for dialogs created by this part in "user mode". + The default is Kexi::DataViewMode. It is altered in classes like KexiSimplePrintingPart. + @see supportedUserViewModes() */ + int m_supportedUserViewModes; + + Info *m_info; + GUIClient *m_guiClient; + QIntDict<GUIClient> m_instanceGuiClients; + KexiMainWindow* m_mainWin; + Kexi::ObjectStatus m_status; + + /*! If you're implementing a new part, set this to value >0 in your ctor + if you have well known (ie registered ID) for your part. + So far, table, query, form, report and script part have defined their IDs + (see KexiPart::ObjectTypes). */ + int m_registeredPartID; + + /*! True if newwly created, unsaved objects are dirty. False by default. + You can change it in your subclass' constructor. */ + bool m_newObjectsAreDirty : 1; + + PartPrivate *d; + + friend class Manager; + friend class ::KexiMainWindow; + friend class ::KexiMainWindowImpl; + friend class GUIClient; +}; + +} + +#endif diff --git a/kexi/core/kexipartdatasource.cpp b/kexi/core/kexipartdatasource.cpp new file mode 100644 index 00000000..f3268594 --- /dev/null +++ b/kexi/core/kexipartdatasource.cpp @@ -0,0 +1,49 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch <lucijan@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexipartdatasource.h" +#include "kexipart.h" + +namespace KexiPart { + +class DataSourcePrivate +{ + public: + DataSourcePrivate() {} + ~DataSourcePrivate() {} + Part *part; +}; + +} + +using namespace KexiPart; + +DataSource::DataSource(Part *part) + : d(new DataSourcePrivate()) +{ + d->part = part; +} + +DataSource::~DataSource() +{ + delete d; +} + +Part* DataSource::part() const { return d->part; } + diff --git a/kexi/core/kexipartdatasource.h b/kexi/core/kexipartdatasource.h new file mode 100644 index 00000000..e7ce9a96 --- /dev/null +++ b/kexi/core/kexipartdatasource.h @@ -0,0 +1,72 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Lucijan Busch <lucijan@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPARTDATASOURCE_H +#define KEXIPARTDATASOURCE_H + +class KexiProject; +namespace KexiDB +{ + class FieldList; + class Cursor; +} + +namespace KexiPart +{ + class DataSourcePrivate; + class Item; + class Part; + +/** + * this class provides a datasource framework for e.g. tables and queries + * using this framework one can query for + * - a list of datasources + * - the fileds in datasources + * - variables (e.g. query variables) + */ +class KEXICORE_EXPORT DataSource +{ + public: + DataSource(Part *part); + virtual ~DataSource(); + + /** + * @returns a list of fileds for the datasource + * @arg id is the document id for the source + */ + virtual KexiDB::FieldList *fields(KexiProject *project, const KexiPart::Item &i)=0; + + /** + * @returns the cursor + */ + virtual KexiDB::Cursor *cursor(KexiProject *project, const KexiPart::Item &i, bool buffer)=0; + + /** + * @returns the part providing this datasource + */ + Part *part() const; + + private: + DataSourcePrivate *d; +}; + +} + +#endif + diff --git a/kexi/core/kexipartguiclient.h b/kexi/core/kexipartguiclient.h new file mode 100644 index 00000000..bb0d16ea --- /dev/null +++ b/kexi/core/kexipartguiclient.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPARTGUICL_H +#define KEXIPARTGUICL_H + +#include "kexipart.h" + +#include <qobject.h> + +#include <kxmlguiclient.h> + +class KexiMainWindow; +class KexiDialogBase; + +namespace KexiPart +{ + +/** @internal A GUI Client used by KexiPart::Part objects within KexiMainWindow +*/ +class GUIClient : public QObject, public KXMLGUIClient +{ + public: + virtual ~GUIClient() {}; + + inline Part *part() { return static_cast<Part*>(QObject::parent()); } + + protected: + /*! Creates a new GUI Client. If \a partInstanceClient is true, the part will be + used as "instance" client, otherwise it will be defined per-view. + \a nameSuffix is used in constructing client's name (only useful for debugging purposes). */ + GUIClient(KexiMainWindow *win, Part* part, bool partInstanceClient, const char* nameSuffix); + + friend class Part; +}; + +} + +#endif + diff --git a/kexi/core/kexipartinfo.cpp b/kexi/core/kexipartinfo.cpp new file mode 100644 index 00000000..22f7cc55 --- /dev/null +++ b/kexi/core/kexipartinfo.cpp @@ -0,0 +1,133 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexipartinfo_p.h" + +#include <kexidb/global.h> + +using namespace KexiPart; + +Info::Private::Private(const KService::Ptr& aPtr) + : ptr(aPtr) + , groupName(aPtr->name()) + , mimeType(aPtr->property("X-Kexi-TypeMime").toCString()) + , itemIcon(aPtr->property("X-Kexi-ItemIcon").toString()) + , objectName(aPtr->property("X-Kexi-TypeName").toString()) + , broken(false) + , idStoredInPartDatabase(false) +{ + QVariant val = ptr->property("X-Kexi-NoObject"); + isVisibleInNavigator = val.isValid() ? (val.toInt() != 1) : true; + +//! @todo (js)..... now it's hardcoded! + if(objectName == "table") + projectPartID = KexiDB::TableObjectType; + else if(objectName == "query") + projectPartID = KexiDB::QueryObjectType; +// else if(objectName == "html") +// m_projectPartID = KexiDB::WebObjectType; + else + projectPartID = -1; //TODO!! +} + +Info::Private::Private() + : projectPartID(-1) //OK? + , broken(false) + , isVisibleInNavigator(false) + , idStoredInPartDatabase(false) +{ +} + +//------------------------------ + +Info::Info(KService::Ptr ptr) + : d(new Private(ptr)) +{ +} + +Info::Info() + : d(new Private()) +{ +} + +Info::~Info() +{ + delete d; +} + +QString Info::groupName() const { return d->groupName; } + +QCString Info::mimeType() const { return d->mimeType; } + +QString Info::itemIcon() const { return d->itemIcon; } + +QString Info::createItemIcon() const { return d->itemIcon+"_newobj"; } + +QString Info::objectName() const { return d->objectName; } + +KService::Ptr Info::ptr() const { return d->ptr; } + +bool Info::isBroken() const { return d->broken; } + +bool Info::isVisibleInNavigator() const { return d->isVisibleInNavigator; } + +int Info::projectPartID() const { return d->projectPartID; } + +void Info::setProjectPartID(int id) { d->projectPartID=id; } + +void Info::setBroken(bool broken, const QString& errorMessage) +{ d->broken = broken; d->errorMessage = errorMessage; } + +QString Info::errorMessage() const { return d->errorMessage; } + +void Info::setIdStoredInPartDatabase(bool set) +{ + d->idStoredInPartDatabase = set; +} + +bool Info::isIdStoredInPartDatabase() const +{ + return d->idStoredInPartDatabase; +} + +bool Info::isDataExportSupported() const +{ + QVariant val = d->ptr ? d->ptr->property("X-Kexi-SupportsDataExport") : QVariant(); + return val.isValid() ? val.toBool() : false; +} + +bool Info::isPrintingSupported() const +{ + QVariant val = d->ptr ? d->ptr->property("X-Kexi-SupportsPrinting") : QVariant(); + return val.isValid() ? val.toBool() : false; +} + +bool Info::isExecuteSupported() const +{ + QVariant val = d->ptr ? d->ptr->property("X-Kexi-SupportsExecution") : QVariant(); + return val.isValid() ? val.toBool() : false; +} + +//-------------- + +QCString KexiPart::nameForCreateAction(const Info& info) +{ + return (info.objectName()+"part_create").latin1(); +} diff --git a/kexi/core/kexipartinfo.h b/kexi/core/kexipartinfo.h new file mode 100644 index 00000000..43cf5b87 --- /dev/null +++ b/kexi/core/kexipartinfo.h @@ -0,0 +1,160 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003,2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPARTINFO_H +#define KEXIPARTINFO_H + +#include "kexipartmanager.h" + +class KexiMainWindowImpl; +class KexiProject; +class KexiDialogBase; + +namespace KexiPart +{ + + class Manager; + class Item; + class Part; + +/** + * @short Information about a Kexi Part (plugin). + */ +class KEXICORE_EXPORT Info +{ + public: + Info(KService::Ptr service); + ~Info(); + + /** + * @return a i18n'ed group name e.g. "Tables" + */ + QString groupName() const; + + /** + * @return the internal mime type of this part + */ + QCString mimeType() const; + +// /** +// * @return the icon for groups +// */ +// inline QString groupIcon() const { return m_groupIcon; } + + /** + * @return the icon for a item + */ + QString itemIcon() const; + + /** + * @return the icon for a item + */ + QString createItemIcon() const; + + /** + * @return the object name associated with this part (e.g. "table") + */ + QString objectName() const; + + /** + * @return the project-part-id + */ + int projectPartID() const; + + /** + * @return the KService::Ptr associated with this part + */ + KService::Ptr ptr() const; + + /** + * @return true if loading was tried but failed + */ + bool isBroken() const; + + /** + * \return true if the part should be visible in the Project Navigator (as a folder). + */ + bool isVisibleInNavigator() const; + + /** + * \return true if the part supports data exporting. + */ + bool isDataExportSupported() const; + + /** + * \return true if the part supports data printing. + */ + bool isPrintingSupported() const; + + /** + * \return true if the part supports execution. This is as + * example the case for the Macro and the Scripting plugins. + */ + bool isExecuteSupported() const; + + protected: + /** + * Used in StaticInfo + */ + Info(); + + friend class Manager; + friend class ::KexiProject; + friend class ::KexiMainWindowImpl; + friend class ::KexiDialogBase; + + /** + * Sets the project-part-id. + */ + void setProjectPartID(int id); + + /** + * Sets the broken flag and error message. + * Most likely to be called by @ref KexiPart::Manager + */ + void setBroken(bool broken, const QString& errorMessage); + + /** + * \return i18n'd error message set by setBroken(). + */ + QString errorMessage() const; + + void setIdStoredInPartDatabase(bool set); + + /** + * \return true if ID of the part is stored in project's database + * false by default. This flag is updated in Manager::checkProject() + * and set to true on first successful execution of KexiDialogBase::storeNewData() + * @internal + */ + bool isIdStoredInPartDatabase() const; + + class Private; + Private *d; +}; + +/*! \return "create" KAction's name for part defined by \a info. + The result is like "tablepart_create". Used in Part::createGUIClients() + and KexiBrowser. */ +KEXICORE_EXPORT QCString nameForCreateAction(const Info& info); + +} + +#endif diff --git a/kexi/core/kexipartinfo_p.h b/kexi/core/kexipartinfo_p.h new file mode 100644 index 00000000..5904ff97 --- /dev/null +++ b/kexi/core/kexipartinfo_p.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPROJECTPARTITEM_P_H +#define KEXIPROJECTPARTITEM_P_H + +#include "kexipartinfo.h" +#include <kservice.h> + +namespace KexiPart +{ +//! @internal +class Info::Private +{ + public: + Private(const KService::Ptr& aPtr); + + //! used in StaticItem class + Private(); + + KService::Ptr ptr; + QString errorMessage; + QString groupName; + QCString mimeType; + QString itemIcon; + QString objectName; + int projectPartID; + bool broken : 1; + bool isVisibleInNavigator : 1; + bool idStoredInPartDatabase : 1; +}; +} + +#endif diff --git a/kexi/core/kexipartitem.cpp b/kexi/core/kexipartitem.cpp new file mode 100644 index 00000000..4ef77783 --- /dev/null +++ b/kexi/core/kexipartitem.cpp @@ -0,0 +1,33 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexipartitem.h" + +using namespace KexiPart; + +Item::Item() + : m_id(0) //- null + , m_neverSaved(false) +{ +} + +Item::~Item() +{ +} + diff --git a/kexi/core/kexipartitem.h b/kexi/core/kexipartitem.h new file mode 100644 index 00000000..46eebf47 --- /dev/null +++ b/kexi/core/kexipartitem.h @@ -0,0 +1,117 @@ +/* This file is part of the KDE project + Copyright (C) 2002, 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPROJECTPARTITEM_H +#define KEXIPROJECTPARTITEM_H + +#include <qobject.h> +#include <qintdict.h> +#include <qptrlist.h> + +namespace KexiDB +{ + class Connection; +} + +namespace KexiPart +{ + +class Info; + +/*! + @short Information about a single object that can be instantiated using Kexi Part + + KexiPart::Item stores: + - identifier ident (low-level name, for example: table name) + - mime type name, eg. "kexi/table" + - caption (visible, i18n'd hight level name, eg. table or query title) +*/ +class KEXICORE_EXPORT Item +{ + public: + + Item(); + ~Item(); + + int identifier() const { return m_id; } + void setIdentifier(int id) { m_id = id; } + + QCString mimeType() const { return m_mime; } + void setMimeType(const QCString &mime) { m_mime = mime; } + + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; } + + QString caption() const { return m_caption; } + void setCaption(const QString &c) { m_caption = c; } + + QString description() const { return m_desc; } + void setDescription(const QString &d) { m_desc = d; } + + /*! \return "neverSaved" flag for this item what mean + that is used when new item is created in-memory-only, + so we need to indicate for KexiProject about that state. + By default this flag is false. + Used by KexiMainWindowImpl::newObject(). */ + bool neverSaved() const { return m_neverSaved; } + + /*! \sa neverSaved(). + Used by KexiMainWindowImpl::newObject(). */ + void setNeverSaved(bool set) { m_neverSaved = set; } + + bool isNull() const { return m_id==0; } + + //! \return caption if not empty, else returns name. + inline QString captionOrName() const { return m_caption.isEmpty() ? m_name : m_caption; } + + private: + QCString m_mime; + QString m_name; + QString m_caption; + QString m_desc; + int m_id; + bool m_neverSaved : 1; +}; + +typedef QIntDict<KexiPart::Item> ItemDict; +typedef QIntDictIterator<KexiPart::Item> ItemDictIterator; +typedef QPtrListIterator<KexiPart::Item> ItemListIterator; + +/*! + @short Part item list with reimplemented compareItems() method. + + Such a list is returend by KexiProject::getSortedItems(KexiPart::ItemList& list, KexiPart::Info *i); + so you can call sort() on the list to sort it by item name. +*/ +class KEXICORE_EXPORT ItemList : public QPtrList<KexiPart::Item> { + public: + ItemList() {} + protected: + virtual int compareItems( QPtrCollection::Item item1, QPtrCollection::Item item2 ) { + return QString::compare( + static_cast<KexiPart::Item*>(item1)->name(), + static_cast<KexiPart::Item*>(item2)->name()); + } +}; + +} + +#endif + diff --git a/kexi/core/kexipartmanager.cpp b/kexi/core/kexipartmanager.cpp new file mode 100644 index 00000000..8525c2d7 --- /dev/null +++ b/kexi/core/kexipartmanager.cpp @@ -0,0 +1,280 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <klibloader.h> +#include <ktrader.h> +#include <kdebug.h> +#include <kconfig.h> +#include <kparts/componentfactory.h> + +#include "kexipartmanager.h" +#include "kexipart.h" +#include "kexipartinfo.h" +#include "kexistaticpart.h" +#include "kexi_version.h" + +#include <kexidb/connection.h> +#include <kexidb/cursor.h> + +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 + m_nextTempProjectPartID = -1; +} + +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<KService> ordered( size*2 ); + int offset = size; //we will insert not described parts from #offset + + //compute order + for(KTrader::OfferList::ConstIterator it(tlist.constBegin()); it != tlist.constEnd(); ++it) + { + KService::Ptr ptr = (*it); + QCString mime = ptr->property("X-Kexi-TypeMime").toCString(); + kdDebug() << "Manager::lookup(): " << mime << endl; +//<TEMP>: disable some parts if needed + if (!Kexi::tempShowForms() && mime=="kexi/form") + continue; + if (!Kexi::tempShowReports() && mime=="kexi/report") + continue; + if (!Kexi::tempShowMacros() && mime=="kexi/macro") + continue; + if (!Kexi::tempShowScripts() && mime=="kexi/script") + continue; +//</TEMP> + 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); + info->setProjectPartID(m_nextTempProjectPartID--); // temp. part id are -1, -2, and so on, + // to avoid duplicates + if (!info->mimeType().isEmpty()) { + m_partsByMime.insert(info->mimeType(), info); + kdDebug() << "Manager::lookup(): inserting info to " << info->mimeType() << endl; + } + m_partlist.append(info); + } + } +} + +Manager::~Manager() +{ +} + +Part * +Manager::part(Info *i) +{ + clearError(); + if(!i) + return 0; + +// kdDebug() << "Manager::part( id = " << i->projectPartID() << " )" << endl; + + if (i->isBroken()) { + setError(i->errorMessage()); + return 0; + } + + Part *p = m_parts[i->projectPartID()]; + + if(!p) { +// kdDebug() << "Manager::part().." << endl; + int error=0; + p = KParts::ComponentFactory::createInstanceFromService<Part>(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, i18n("Error while loading plugin \"%1\"").arg(i->objectName())); + setError(i->errorMessage()); + return 0; + } + if (p->m_registeredPartID>0) { + i->setProjectPartID( p->m_registeredPartID ); + } + + 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<Part> it(m_parts); + for (;i.current();++it) { + i.current()->removeClient(win->guiFactory()); + } +}*/ + +Part * +Manager::partForMimeType(const QString &mimeType) +{ + return mimeType.isEmpty() ? 0 : part(m_partsByMime[mimeType.latin1()]); +} + +Info * +Manager::infoForMimeType(const QString &mimeType) +{ + Info *i = mimeType.isEmpty() ? 0 : m_partsByMime[mimeType.latin1()]; + if (i) + return i; + setError(i18n("No plugin for mime type \"%1\"").arg(mimeType)); + return 0; +} + + +bool +Manager::checkProject(KexiDB::Connection *conn) +{ + clearError(); +// 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 = infoForMimeType(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()); + i->setIdStoredInPartDatabase(true); +// 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; +} + +void Manager::insertStaticPart(StaticPart* part) +{ + if (!part) + return; + part->info()->setProjectPartID(m_nextTempProjectPartID--); // temp. part id are -1, -2, and so on, + m_partlist.append(part->info()); + if (!part->info()->mimeType().isEmpty()) + m_partsByMime.insert(part->info()->mimeType(), part->info()); + m_parts.insert(part->info()->projectPartID(), part); +} + +#include "kexipartmanager.moc" diff --git a/kexi/core/kexipartmanager.h b/kexi/core/kexipartmanager.h new file mode 100644 index 00000000..1756e965 --- /dev/null +++ b/kexi/core/kexipartmanager.h @@ -0,0 +1,141 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPARTMANAGER_H +#define KEXIPARTMANAGER_H + +#include <qobject.h> +#include <qdict.h> +#include <qasciidict.h> +#include <qintdict.h> +#include <qvaluelist.h> +#include <qptrlist.h> + +#include <kservice.h> + +#include <kexidb/object.h> + +//#include "kexipartdatasource.h" + +namespace KexiDB +{ + class Connection; +} + +namespace KexiPart +{ + class Info; + class Part; + class StaticPart; + + struct Missing + { + QString name; + QCString mime; + QString url; + }; + + typedef QAsciiDict<Info> PartInfoDict; + typedef QDictIterator<Info> PartInfoDictIterator; + typedef QValueList<Missing> MissingList; + typedef QPtrList<Info> PartInfoList; + typedef QPtrListIterator<Info> PartInfoListIterator; + typedef QIntDict<Part> PartDict; +// typedef QPtrList<DataSource> DataSourceList; + +/** + * @short KexiPart's manager: looks up and instantiates them + * + * It dlopens them when needed, they aren't dlopened at startup is not necessary. + */ +class KEXICORE_EXPORT Manager : public QObject, public KexiDB::Object +{ + Q_OBJECT + + public: + /** + * creates an empty instance + */ + Manager(QObject *parent = 0); + ~Manager(); + + /** + * queries ktrader and creates a list of available parts + */ + void lookup(); + + /** + * \return a part object for specified mime type. Dlopens a part using KexiPart::Info + * if needed. Return 0 if loading failed. + */ + Part *partForMimeType(const QString& mimeTypt); + + /** + * \return a part object for specified info. Dlopens a part using KexiPart::Info + * if needed. Return 0 if loading failed. + */ + Part *part(Info *); + + /** + * \return the info for a corresponding internal mime + */ + Info *infoForMimeType(const QString& mimeType); + + /** + * checks project's kexi__part table + * and checks if all parts used in a project are available locally + * + * use @ref missingParts() to get a list of missing parts + */ + bool checkProject(KexiDB::Connection *conn); + + /** + * @returns parts metioned in the project meta tables but not available locally + */ + MissingList missingParts() const { return m_missing; } + + + /** + * @returns a list of the available KexiParts in well-defined order + */ + PartInfoList *partInfoList() { return &m_partlist; } + + signals: + void partLoaded(KexiPart::Part*); + + protected: + //! Used by StaticPart + void insertStaticPart(KexiPart::StaticPart* part); + + private: + PartDict m_parts; + PartInfoList m_partlist; + PartInfoDict m_partsByMime; + MissingList m_missing; + int m_nextTempProjectPartID; + bool m_lookupDone : 1; + + friend class StaticPart; +}; + +} + +#endif + diff --git a/kexi/core/kexiproject.cpp b/kexi/core/kexiproject.cpp new file mode 100644 index 00000000..741fb67a --- /dev/null +++ b/kexi/core/kexiproject.cpp @@ -0,0 +1,1023 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <qfile.h> +#include <qapplication.h> +#include <qdom.h> + +#include <kmimetype.h> +#include <kdebug.h> +#include <klocale.h> + +#include <kexiutils/identifier.h> + +#include <kexidb/connection.h> +#include <kexidb/cursor.h> +#include <kexidb/driver.h> +#include <kexidb/drivermanager.h> +#include <kexidb/utils.h> +#include <kexidb/parser/parser.h> +#include <kexidb/msghandler.h> +#include <kexidb/dbproperties.h> +#include <kexiutils/utils.h> + +#include "kexiproject.h" +#include "kexipartmanager.h" +#include "kexipartitem.h" +#include "kexipartinfo.h" +#include "kexipart.h" +#include "kexidialogbase.h" +#include "kexi.h" +#include "keximainwindow.h" +#include "kexiblobbuffer.h" +#include "kexiguimsghandler.h" + +#include <assert.h> + +class KexiProject::Private +{ + public: + Private() + : data(0) + , itemDictsCache(199) + , unstoredItems(199) + , tempPartItemID_Counter(-1) + , sqlParser(0) + , versionMajor(0) + , versionMinor(0) + { + itemDictsCache.setAutoDelete(true); + unstoredItems.setAutoDelete(true); + } + ~Private() { + delete data; + data=0; + delete sqlParser; + } + + QGuardedPtr<KexiDB::Connection> connection; + QGuardedPtr<KexiProjectData> data; + + QString error_title; + + //! a cache for item() method, indexed by project part's ids + QIntDict<KexiPart::ItemDict> itemDictsCache; + + QPtrDict<KexiPart::Item> unstoredItems; + int tempPartItemID_Counter; //!< helper for getting unique + //!< temporary identifiers for unstored items + KexiDB::Parser* sqlParser; + + int versionMajor; + int versionMinor; +}; + +//--------------------------- + +/* + Helper for setting temporary error title. +class KexiProject::ErrorTitle +{ + public: + ErrorTitle(KexiProject* p, const QString& msg = QString::null) + : prj(p) + , prev_err_title(p->m_error_title) + { + p->m_error_title = msg; + } + ~ErrorTitle() + { + prj->m_error_title = prev_err_title; + } + KexiProject* prj; + QString prev_err_title; +};*/ + +KexiProject::KexiProject(KexiProjectData *pdata, KexiDB::MessageHandler* handler) + : QObject(), Object(handler) + , d(new Private()) +{ + d->data = pdata; +//! @todo partmanager is outside project, so can be initialised just once: + Kexi::partManager().lookup(); +} + +KexiProject::KexiProject(KexiProjectData *pdata, KexiDB::MessageHandler* handler, + KexiDB::Connection* conn) + : QObject(), Object(handler) + , d(new Private()) +{ + d->data = pdata; + if (d->data->connectionData() == d->connection->data()) + d->connection = conn; + else + kdWarning() << "KexiProject::KexiProject(): passed connection's data (" + << conn->data()->serverInfoString() << ") is not compatible with project's conn. data (" + << d->data->connectionData()->serverInfoString() << ")" << endl; +//! @todo partmanager is outside project, so can be initialised just once: + Kexi::partManager().lookup(); +} + +KexiProject::~KexiProject() +{ + closeConnection(); + delete d; +} + +KexiDB::Connection *KexiProject::dbConnection() const +{ + return d->connection; +} + +KexiProjectData* KexiProject::data() const +{ + return d->data; +} + +int KexiProject::versionMajor() const +{ + return d->versionMajor; +} + +int KexiProject::versionMinor() const +{ + return d->versionMinor; +} + +tristate +KexiProject::open(bool &incompatibleWithKexi) +{ + return openInternal(&incompatibleWithKexi); +} + +tristate +KexiProject::open() +{ + return openInternal(0); +} + +tristate +KexiProject::openInternal(bool *incompatibleWithKexi) +{ + if (incompatibleWithKexi) + *incompatibleWithKexi = false; + kdDebug() << "KexiProject::open(): " << d->data->databaseName() <<" "<< d->data->connectionData()->driverName << endl; + KexiDB::MessageTitle et(this, + i18n("Could not open project \"%1\".").arg(d->data->databaseName())); + + if (!createConnection()) { + kdDebug() << "KexiProject::open(): !createConnection()" << endl; + return false; + } + bool cancel = false; + KexiGUIMessageHandler msgHandler; + if (!d->connection->useDatabase(d->data->databaseName(), true, &cancel, &msgHandler)) + { + if (cancel) { + return cancelled; + } + kdDebug() << "KexiProject::open(): !d->connection->useDatabase() " + << d->data->databaseName() <<" "<< d->data->connectionData()->driverName << endl; + + if (d->connection->errorNum() == ERR_NO_DB_PROPERTY) { +//<temp> +//! @todo this is temporary workaround as we have no import driver for SQLite + if (/*supported?*/ !d->data->connectionData()->driverName.lower().startsWith("sqlite")) { +//</temp> + if (incompatibleWithKexi) + *incompatibleWithKexi = true; + } + else + setError(d->connection); + closeConnection(); + return false; + } + + setError(d->connection); + closeConnection(); + return false; + } + + if (!initProject()) + return false; + + return createInternalStructures(/*insideTransaction*/true); +} + +tristate +KexiProject::create(bool forceOverwrite) +{ + KexiDB::MessageTitle et(this, + i18n("Could not create project \"%1\".").arg(d->data->databaseName())); + + if (!createConnection()) + return false; + if (!checkWritable()) + return false; + if (d->connection->databaseExists( d->data->databaseName() )) { + if (!forceOverwrite) + return cancelled; + if (!d->connection->dropDatabase( d->data->databaseName() )) { + setError(d->connection); + closeConnection(); + return false; + } + kdDebug() << "--- DB '" << d->data->databaseName() << "' dropped ---"<< endl; + } + if (!d->connection->createDatabase( d->data->databaseName() )) { + setError(d->connection); + closeConnection(); + return false; + } + kdDebug() << "--- DB '" << d->data->databaseName() << "' created ---"<< endl; + // and now: open + if (!d->connection->useDatabase(d->data->databaseName())) + { + kdDebug() << "--- DB '" << d->data->databaseName() << "' USE ERROR ---"<< endl; + setError(d->connection); + closeConnection(); + return false; + } + kdDebug() << "--- DB '" << d->data->databaseName() << "' used ---"<< endl; + + //<add some data> + KexiDB::Transaction trans = d->connection->beginTransaction(); + if (trans.isNull()) + return false; + + if (!createInternalStructures(/*!insideTransaction*/false)) + return false; + + //add some metadata +//! @todo put more props. todo - creator, created date, etc. (also to KexiProjectData) + KexiDB::DatabaseProperties &props = d->connection->databaseProperties(); + if (!props.setValue("kexiproject_major_ver", d->versionMajor) + || !props.setCaption("kexiproject_major_ver", i18n("Project major version")) + || !props.setValue("kexiproject_minor_ver", d->versionMinor) + || !props.setCaption("kexiproject_minor_ver", i18n("Project minor version")) + || !props.setValue("project_caption", d->data->caption()) + || !props.setCaption("project_caption", i18n("Project caption")) + || !props.setValue("project_desc", d->data->description()) + || !props.setCaption("project_desc", i18n("Project description")) ) + return false; + +/* KexiDB::TableSchema *t_db = d->connection->tableSchema("kexi__db"); + //caption: + if (!t_db) + return false; + + if (!KexiDB::replaceRow(*d->connection, t_db, "db_property", "project_caption", + "db_value", QVariant( d->data->caption() ), KexiDB::Field::Text) + || !KexiDB::replaceRow(*d->connection, t_db, "db_property", "project_desc", + "db_value", QVariant( d->data->description() ), KexiDB::Field::Text) ) + return false; +*/ + if (trans.active() && !d->connection->commitTransaction(trans)) + return false; + //</add some data> + + return initProject(); +} + +bool KexiProject::createInternalStructures(bool insideTransaction) +{ + KexiDB::TransactionGuard tg; + if (insideTransaction) { + tg.setTransaction( d->connection->beginTransaction() ); + if (tg.transaction().isNull()) + return false; + } + + //Get information about kexiproject version. + //kexiproject version is a version of data layer above kexidb layer. + KexiDB::DatabaseProperties &props = d->connection->databaseProperties(); + bool ok; + int storedMajorVersion = props.value("kexiproject_major_ver").toInt(&ok); + if (!ok) + storedMajorVersion = 0; + int storedMinorVersion = props.value("kexiproject_minor_ver").toInt(&ok); + if (!ok) + storedMinorVersion = 1; + + bool containsKexi__blobsTable = d->connection->drv_containsTable("kexi__blobs"); + int dummy; + bool contains_o_folder_id = containsKexi__blobsTable && true == d->connection->querySingleNumber( + "SELECT COUNT(o_folder_id) FROM kexi__blobs", dummy, 0, false/*addLimitTo1*/); + bool add_folder_id_column = false; + +//! @todo what about read-only db access? + if (storedMajorVersion<=0) { + d->versionMajor = KEXIPROJECT_VERSION_MAJOR; + d->versionMinor = KEXIPROJECT_VERSION_MINOR; + //For compatibility for projects created before Kexi 1.0 beta 1: + //1. no kexiproject_major_ver and kexiproject_minor_ver -> add them + if (!d->connection->isReadOnly()) { + if (!props.setValue("kexiproject_major_ver", d->versionMajor) + || !props.setCaption("kexiproject_major_ver", i18n("Project major version")) + || !props.setValue("kexiproject_minor_ver", d->versionMinor) + || !props.setCaption("kexiproject_minor_ver", i18n("Project minor version")) ) { + return false; + } + } + + if (containsKexi__blobsTable) { +//! @todo what to do for readonly connections? Should we alter kexi__blobs in memory? + if (!d->connection->isReadOnly()) { + if (!contains_o_folder_id) { + add_folder_id_column = true; + } + } + } + } + if (storedMajorVersion!=d->versionMajor || storedMajorVersion!=d->versionMinor) { + //! @todo version differs: should we change something? + d->versionMajor = storedMajorVersion; + d->versionMinor = storedMinorVersion; + } + + KexiDB::InternalTableSchema *t_blobs = new KexiDB::InternalTableSchema("kexi__blobs"); + t_blobs->addField( new KexiDB::Field("o_id", KexiDB::Field::Integer, + KexiDB::Field::PrimaryKey | KexiDB::Field::AutoInc, KexiDB::Field::Unsigned) ) + .addField( new KexiDB::Field("o_data", KexiDB::Field::BLOB) ) + .addField( new KexiDB::Field("o_name", KexiDB::Field::Text ) ) + .addField( new KexiDB::Field("o_caption", KexiDB::Field::Text ) ) + .addField( new KexiDB::Field("o_mime", KexiDB::Field::Text, KexiDB::Field::NotNull) ) + .addField( new KexiDB::Field("o_folder_id", + KexiDB::Field::Integer, 0, KexiDB::Field::Unsigned) //references kexi__gallery_folders.f_id + //If null, the BLOB only points to virtual "All" folder + //WILL BE USED in Kexi >=2.0 + ); + + //*** create global BLOB container, if not present + if (containsKexi__blobsTable) { + //! just insert this schema + d->connection->insertInternalTableSchema(t_blobs); + if (add_folder_id_column && !d->connection->isReadOnly()) { + // 2. "kexi__blobs" table contains no "o_folder_id" column -> add it + // (by copying table to avoid data loss) + KexiDB::TableSchema *kexi__blobsCopy = new KexiDB::TableSchema( *t_blobs ); + kexi__blobsCopy->setName("kexi__blobs__copy"); + if (!d->connection->drv_createTable( *kexi__blobsCopy )) { + delete kexi__blobsCopy; + delete t_blobs; + return false; + } + // 2.1 copy data (insert 0's into o_folder_id column) + if (!d->connection->executeSQL( + QString::fromLatin1("INSERT INTO kexi__blobs (o_data, o_name, o_caption, o_mime, o_folder_id) " + "SELECT o_data, o_name, o_caption, o_mime, 0 FROM kexi__blobs") ) + // 2.2 remove the original kexi__blobs + || !d->connection->executeSQL(QString::fromLatin1("DROP TABLE kexi__blobs")) //lowlevel + // 2.3 rename the copy back into kexi__blobs + || !d->connection->drv_alterTableName(*kexi__blobsCopy, "kexi__blobs") + ) + { + //(no need to drop the copy, ROLLBACK will drop it) + delete kexi__blobsCopy; + delete t_blobs; + return false; + } + delete kexi__blobsCopy; //not needed - physically renamed to kexi_blobs + } + } + else { +// if (!d->connection->createTable( t_blobs, false/*!replaceExisting*/ )) { + if (!d->connection->isReadOnly()) { + if (!d->connection->createTable( t_blobs, true/*replaceExisting*/ )) { + delete t_blobs; + return false; + } + } + } + + //Store default part infos. + //Infos for other parts (forms, reports...) are created on demand in KexiDialogBase::storeNewData() + KexiDB::InternalTableSchema *t_parts = new KexiDB::InternalTableSchema("kexi__parts"); //newKexiDBSystemTableSchema("kexi__parts"); + t_parts->addField( + new KexiDB::Field("p_id", KexiDB::Field::Integer, KexiDB::Field::PrimaryKey | KexiDB::Field::AutoInc, KexiDB::Field::Unsigned) + ) + .addField( new KexiDB::Field("p_name", KexiDB::Field::Text) ) + .addField( new KexiDB::Field("p_mime", KexiDB::Field::Text ) ) + .addField( new KexiDB::Field("p_url", KexiDB::Field::Text ) ); + + bool containsKexi__partsTable = d->connection->drv_containsTable("kexi__parts"); + bool partsTableOk = true; + if (containsKexi__partsTable) { + //! just insert this schema + d->connection->insertInternalTableSchema(t_parts); + } + else { + if (!d->connection->isReadOnly()) { + partsTableOk = d->connection->createTable( t_parts, true/*replaceExisting*/ ); + + KexiDB::FieldList *fl = t_parts->subList("p_id", "p_name", "p_mime", "p_url"); + if (partsTableOk) + partsTableOk = d->connection->insertRecord(*fl, QVariant(1), QVariant("Tables"), + QVariant("kexi/table"), QVariant("http://koffice.org/kexi/")); + + if (partsTableOk) + partsTableOk = d->connection->insertRecord(*fl, QVariant(2), QVariant("Queries"), + QVariant("kexi/query"), QVariant("http://koffice.org/kexi/")); + } + } + + if (!partsTableOk) { + delete t_parts; + return false; + } + + if (insideTransaction) { + if (tg.transaction().active() && !tg.commit()) + return false; + } + return true; +} + +bool +KexiProject::createConnection() +{ + if (d->connection) + return true; + + clearError(); +// closeConnection();//for sanity + KexiDB::MessageTitle et(this); + + KexiDB::Driver *driver = Kexi::driverManager().driver(d->data->connectionData()->driverName); + if(!driver) { + setError(&Kexi::driverManager()); + return false; + } + + int connectionOptions = 0; + if (d->data->isReadOnly()) + connectionOptions |= KexiDB::Driver::ReadOnlyConnection; + d->connection = driver->createConnection(*d->data->connectionData(), connectionOptions); + if (!d->connection) + { + kdDebug() << "KexiProject::open(): uuups failed " << driver->errorMsg() << endl; + setError(driver); + return false; + } + + if (!d->connection->connect()) + { + setError(d->connection); + kdDebug() << "KexiProject::createConnection(): error connecting: " << (d->connection ? d->connection->errorMsg() : QString::null) << endl; + closeConnection(); + return false; + } + + //re-init BLOB buffer +//! @todo won't work for subsequent connection + KexiBLOBBuffer::setConnection(d->connection); + return true; +} + + +bool +KexiProject::closeConnection() +{ + if (!d->connection) + return true; + + if (!d->connection->disconnect()) { + setError(d->connection); + return false; + } + + delete d->connection; //this will also clear connection for BLOB buffer + d->connection = 0; + return true; +} + +bool +KexiProject::initProject() +{ +// emit dbAvailable(); + kdDebug() << "KexiProject::open(): checking project parts..." << endl; + + if (!Kexi::partManager().checkProject(d->connection)) { + setError(Kexi::partManager().error() ? (KexiDB::Object*)&Kexi::partManager() : (KexiDB::Connection*)d->connection); + return false; + } + +// !@todo put more props. todo - creator, created date, etc. (also to KexiProjectData) + KexiDB::DatabaseProperties &props = d->connection->databaseProperties(); + QString str( props.value("project_caption").toString() ); + if (!str.isEmpty()) + d->data->setCaption( str ); + str = props.value("project_desc").toString(); + if (!str.isEmpty()) + d->data->setDescription( str ); +/* KexiDB::RowData data; + QString sql = "select db_value from kexi__db where db_property='%1'"; + if (d->connection->querySingleRecord( sql.arg("project_caption"), data ) && !data[0].toString().isEmpty()) + d->data->setCaption(data[0].toString()); + if (d->connection->querySingleRecord( sql.arg("project_desc"), data) && !data[0].toString().isEmpty()) + d->data->setDescription(data[0].toString());*/ + + return true; +} + +bool +KexiProject::isConnected() +{ + if(d->connection && d->connection->isDatabaseUsed()) + return true; + + return false; +} + +KexiPart::ItemDict* +KexiProject::items(KexiPart::Info *i) +{ + kdDebug() << "KexiProject::items()" << endl; + if(!i || !isConnected()) + return 0; + + //trying in cache... + KexiPart::ItemDict *dict = d->itemDictsCache[ i->projectPartID() ]; + if (dict) + return dict; + //retrieve: + KexiDB::Cursor *cursor = d->connection->executeQuery( + "SELECT o_id, o_name, o_caption FROM kexi__objects WHERE o_type = " + + QString::number(i->projectPartID()));//, KexiDB::Cursor::Buffered); +// kdDebug() << "KexiProject::items(): cursor handle is:" << cursor << endl; + if(!cursor) + return 0; + + dict = new KexiPart::ItemDict(1009); + dict->setAutoDelete(true); + + for(cursor->moveFirst(); !cursor->eof(); cursor->moveNext()) + { + KexiPart::Item *it = new KexiPart::Item(); + bool ok; + int ident = cursor->value(0).toInt(&ok); + QString objName( cursor->value(1).toString() ); + + if ( ok && (ident>0) && !d->connection->isInternalTableSchema(objName) + && KexiUtils::isIdentifier(objName) ) + { + it->setIdentifier(ident); + it->setMimeType(i->mimeType()); //js: may be not null??? + it->setName(objName); + it->setCaption(cursor->value(2).toString()); + } + dict->insert(it->identifier(), it); +// kdDebug() << "KexiProject::items(): ITEM ADDED == "<<objName <<" id="<<ident<<endl; + } + + d->connection->deleteCursor(cursor); +// kdDebug() << "KexiProject::items(): end with count " << dict->count() << endl; + d->itemDictsCache.insert( i->projectPartID(), dict ); + return dict; +} + +KexiPart::ItemDict* +KexiProject::itemsForMimeType(const QCString &mimeType) +{ + KexiPart::Info *info = Kexi::partManager().infoForMimeType(mimeType); + return items(info); +} + +void +KexiProject::getSortedItems(KexiPart::ItemList& list, KexiPart::Info *i) +{ + list.clear(); + KexiPart::ItemDict* dict = items(i); + if (!dict) + return; + for (KexiPart::ItemDictIterator it(*dict); it.current(); ++it) + list.append(it.current()); +} + +void +KexiProject::getSortedItemsForMimeType(KexiPart::ItemList& list, const QCString &mimeType) +{ + KexiPart::Info *info = Kexi::partManager().infoForMimeType(mimeType); + getSortedItems(list, info); +} + +void +KexiProject::addStoredItem(KexiPart::Info *info, KexiPart::Item *item) +{ + if (!info || !item) + return; + KexiPart::ItemDict *dict = items(info); + item->setNeverSaved( false ); + d->unstoredItems.take(item); //no longer unstored + dict->insert( item->identifier(), item ); + //let's update e.g. navigator + emit newItemStored(*item); +} + +KexiPart::Item* +KexiProject::itemForMimeType(const QCString &mimeType, const QString &name) +{ + KexiPart::ItemDict *dict = itemsForMimeType(mimeType); + if (!dict) + return 0; + const QString l_name = name.lower(); + for (KexiPart::ItemDictIterator it( *dict ); it.current(); ++it) { + if (it.current()->name().lower()==l_name) + return it.current(); + } + return 0; +} + +KexiPart::Item* +KexiProject::item(KexiPart::Info *i, const QString &name) +{ + KexiPart::ItemDict *dict = items(i); + if (!dict) + return 0; + const QString l_name = name.lower(); + for (KexiPart::ItemDictIterator it( *dict ); it.current(); ++it) { + if (it.current()->name().lower()==l_name) + return it.current(); + } + return 0; +} + +KexiPart::Item* +KexiProject::item(int identifier) +{ + KexiPart::ItemDict *dict; + for (QIntDictIterator<KexiPart::ItemDict> it(d->itemDictsCache); (dict = it.current()); ++it) { + KexiPart::Item *item = dict->find(identifier); + if (item) + return item; + } + return 0; +} + +/*void KexiProject::clearMsg() +{ + clearError(); +// d->error_title=QString::null; +} + +void KexiProject::setError(int code, const QString &msg ) +{ + Object::setError(code, msg); + if (Object::error()) + ERRMSG(d->error_title, this); +// emit error(d->error_title, this); +} + + +void KexiProject::setError( const QString &msg ) +{ + Object::setError(msg); + if (Object::error()) + ERRMSG(d->error_title, this); +// emit error(d->error_title, this); +} + +void KexiProject::setError( KexiDB::Object *obj ) +{ + if (!obj) + return; + Object::setError(obj); + if (Object::error()) + ERRMSG(d->error_title, obj); +// emit error(d->error_title, obj); +} + +void KexiProject::setError(const QString &msg, const QString &desc) +{ + Object::setError(msg); //ok? + ERRMSG(msg, desc); //not KexiDB-related +// emit error(msg, desc); //not KexiDB-related +} +*/ + +KexiPart::Part *KexiProject::findPartFor(KexiPart::Item& item) +{ + clearError(); + KexiDB::MessageTitle et(this); + KexiPart::Part *part = Kexi::partManager().partForMimeType(item.mimeType()); + if (!part) + setError(&Kexi::partManager()); + return part; +} + +KexiDialogBase* KexiProject::openObject(KexiMainWindow *wnd, KexiPart::Item& item, + int viewMode, QMap<QString,QString>* staticObjectArgs) +{ + clearError(); + if (viewMode!=Kexi::DataViewMode && data()->userMode()) + return 0; + + KexiDB::MessageTitle et(this); + KexiPart::Part *part = findPartFor(item); + if (!part) + return 0; + KexiDialogBase *dlg = part->openInstance(wnd, item, viewMode, staticObjectArgs); + if (!dlg) { + if (part->lastOperationStatus().error()) + setError(i18n("Opening object \"%1\" failed.").arg(item.name())+"<br>" + +part->lastOperationStatus().message, + part->lastOperationStatus().description); + return 0; + } + return dlg; +} + +KexiDialogBase* KexiProject::openObject(KexiMainWindow *wnd, const QCString &mimeType, + const QString& name, int viewMode) +{ + KexiPart::Item *it = itemForMimeType(mimeType, name); + return it ? openObject(wnd, *it, viewMode) : 0; +} + +bool KexiProject::checkWritable() +{ + if (!d->connection->isReadOnly()) + return true; + setError(i18n("This project is opened as read only.")); + return false; +} + +bool KexiProject::removeObject(KexiMainWindow *wnd, KexiPart::Item& item) +{ + clearError(); + if (data()->userMode()) + return false; + + KexiDB::MessageTitle et(this); + if (!checkWritable()) + return false; + KexiPart::Part *part = findPartFor(item); + if (!part) + return false; + if (!item.neverSaved() && !part->remove(wnd, item)) { + //js TODO check for errors + return false; + } + if (!item.neverSaved()) { + KexiDB::TransactionGuard tg( *d->connection ); + if (!tg.transaction().active()) { + setError(d->connection); + return false; + } + if (!d->connection->removeObject( item.identifier() )) { + setError(d->connection); + return false; + } + if (!tg.commit()) { + setError(d->connection); + return false; + } + } + emit itemRemoved(item); + + //now: remove this item from cache + if (part->info()) { + KexiPart::ItemDict *dict = d->itemDictsCache[ part->info()->projectPartID() ]; + if (!(dict && dict->remove( item.identifier() ))) + d->unstoredItems.remove(&item);//remove temp. + } + return true; +} + +bool KexiProject::renameObject( KexiMainWindow *wnd, KexiPart::Item& item, const QString& _newName ) +{ + clearError(); + if (data()->userMode()) + return 0; + + KexiUtils::WaitCursor wait; + QString newName = _newName.stripWhiteSpace(); + { + KexiDB::MessageTitle et(this); + if (newName.isEmpty()) { + setError( i18n("Could not set empty name for this object.") ); + return false; + } + if (this->itemForMimeType(item.mimeType(), newName)!=0) { + setError( i18n("Could not use this name. Object with name \"%1\" already exists.") + .arg(newName) ); + return false; + } + } + + KexiDB::MessageTitle et(this, + i18n("Could not rename object \"%1\".").arg(item.name()) ); + if (!checkWritable()) + return false; + KexiPart::Part *part = findPartFor(item); + if (!part) + return false; + KexiDB::TransactionGuard tg( *d->connection ); + if (!tg.transaction().active()) { + setError(d->connection); + return false; + } + if (!part->rename(wnd, item, newName)) { + setError(part->lastOperationStatus().message, part->lastOperationStatus().description); + return false; + } + if (!d->connection->executeSQL( "update kexi__objects set o_name=" + + d->connection->driver()->valueToSQL( KexiDB::Field::Text, newName ) + + " where o_id=" + QString::number(item.identifier()) )) { + setError(d->connection); + return false; + } + if (!tg.commit()) { + setError(d->connection); + return false; + } + QCString oldName( item.name().latin1() ); + item.setName( newName ); + emit itemRenamed(item, oldName); + return true; +} + +KexiPart::Item* KexiProject::createPartItem(KexiPart::Info *info, const QString& suggestedCaption) +{ + clearError(); + if (data()->userMode()) + return 0; + + KexiDB::MessageTitle et(this); + KexiPart::Part *part = Kexi::partManager().part(info); + if (!part) { + setError(&Kexi::partManager()); + return 0; + } + + KexiPart::ItemDict *dict = items(info); + + //find new, unique default name for this item + int n; + QString new_name; + QString base_name; + if (suggestedCaption.isEmpty()) { + n = 1; + base_name = part->instanceName(); + } + else { + n = 0; //means: try not to add 'n' + base_name = KexiUtils::string2Identifier(suggestedCaption).lower(); + } + base_name = KexiUtils::string2Identifier(base_name).lower(); + KexiPart::ItemDictIterator it(*dict); + QPtrDictIterator<KexiPart::Item> itUnstored(d->unstoredItems); + do { + new_name = base_name; + if (n>=1) + new_name += QString::number(n); + for (it.toFirst(); it.current(); ++it) { + if (it.current()->name().lower()==new_name) + break; + } + if ( it.current() ) { + n++; + continue; //stored exists! + } + for (itUnstored.toFirst(); itUnstored.current(); ++itUnstored) { + if (itUnstored.current()->name().lower()==new_name) + break; + } + if ( !itUnstored.current() ) + break; //unstored doesn't exist + n++; + } while (n<1000/*sanity*/); + + if (n>=1000) + return 0; + + QString new_caption( suggestedCaption.isEmpty() ? part->instanceCaption() : suggestedCaption); + if (n>=1) + new_caption += QString::number(n); + + KexiPart::Item *item = new KexiPart::Item(); + item->setIdentifier( --d->tempPartItemID_Counter );//temporary + item->setMimeType(info->mimeType()); + item->setName(new_name); + item->setCaption(new_caption); + item->setNeverSaved(true); + d->unstoredItems.insert(item, item); + return item; +} + +KexiPart::Item* KexiProject::createPartItem(KexiPart::Part *part, const QString& suggestedCaption) +{ + return createPartItem(part->info(), suggestedCaption); +} + +void KexiProject::deleteUnstoredItem(KexiPart::Item *item) +{ + if (!item) + return; + d->unstoredItems.remove(item); +} + +KexiDB::Parser* KexiProject::sqlParser() +{ + if (!d->sqlParser) { + if (!d->connection) + return 0; + d->sqlParser = new KexiDB::Parser(d->connection); + } + return d->sqlParser; +} + +static const QString warningNoUndo = i18n("Warning: entire project's data will be removed."); + +/*static*/ +KexiProject* +KexiProject::createBlankProject(bool &cancelled, KexiProjectData* data, + KexiDB::MessageHandler* handler) +{ + cancelled = false; + KexiProject *prj = new KexiProject( new KexiProjectData(*data), handler ); + + tristate res = prj->create(false); + if (~res) { +//! @todo move to KexiMessageHandler + if (KMessageBox::Yes != KMessageBox::warningYesNo(0, "<qt>"+i18n( + "The project %1 already exists.\n" + "Do you want to replace it with a new, blank one?") + .arg(prj->data()->infoString())+"\n"+warningNoUndo+"</qt>", + QString::null, KGuiItem(i18n("Replace")), KStdGuiItem::cancel() )) +//todo add serverInfoString() for server-based prj + { + delete prj; + cancelled = true; + return 0; + } + res = prj->create(true/*overwrite*/); + } + if (res != true) { + delete prj; + return 0; + } + kdDebug() << "KexiProject::createBlankProject(): new project created --- " << endl; +//todo? Kexi::recentProjects().addProjectData( data ); + + return prj; +} + +/*static*/ +tristate KexiProject::dropProject(KexiProjectData* data, + KexiDB::MessageHandler* handler, bool dontAsk) +{ + if (!dontAsk && KMessageBox::Yes != KMessageBox::warningYesNo(0, + i18n("Do you want to drop the project \"%1\"?").arg(data->objectName())+"\n"+warningNoUndo )) + return cancelled; + + KexiProject prj( new KexiProjectData(*data), handler ); + if (!prj.open()) + return false; + + if (prj.dbConnection()->isReadOnly()) { + handler->showErrorMessage( + i18n("Could not drop this project. Database connection for this project has been opened as read only.")); + return false; + } + + return prj.dbConnection()->dropDatabase(); +} + +/*void KexiProject::reloadPartItem( KexiDialogBase* dialog ) +{ + if (!dialog) + return; + + KexiPart::Item* item = dialog->partItem(); + + if (dialog || !d->connection->setQuerySchemaObsolete( queryName )) + return; + KexiPart::Info *partInfo = Kexi::partManager().infoForMimeType("kexi/query"); + if (!partInfo) + return; //err? + item(partInfo, queryName); + if (!item) + return; //err? + emit itemSetO + +}*/ + +#include "kexiproject.moc" diff --git a/kexi/core/kexiproject.h b/kexi/core/kexiproject.h new file mode 100644 index 00000000..1128ffe4 --- /dev/null +++ b/kexi/core/kexiproject.h @@ -0,0 +1,334 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPROJECT_H +#define KEXIPROJECT_H + +#include <qobject.h> +#include <qintdict.h> +#include <qptrdict.h> +#include <qguardedptr.h> + +#include <kexiutils/tristate.h> +#include <kexidb/object.h> +#include "kexiprojectdata.h" +#include "kexipartitem.h" +#include "kexi.h" + +/*! KexiProject implementation version. + It is altered after every change: + - major number is increased after KexiProject storage format change, + - minor is increased after adding binary-incompatible change. + Use KexiProject::versionMajor() and KexiProject::versionMinor() to get real project's version. +*/ + +#define KEXIPROJECT_VERSION_MAJOR 1 +#define KEXIPROJECT_VERSION_MINOR 0 + +namespace KexiDB +{ + class DriverManager; + class Driver; + class Connection; + class Parser; +} + +namespace KexiPart +{ + class Part; + class Info; +} + +class KexiMainWindow; +class KexiDialogBase; + +/** + * @short A project's main controller. + * It also contains connection data, + * current file state, etc. + */ +class KEXICORE_EXPORT KexiProject : public QObject, public KexiDB::Object +{ + Q_OBJECT + + public: + /*! Constructor 1. Creates a new object using \a pdata. + \a pdata which will be then owned by KexiProject object. + \a handler can be provided to receive error messages during + entire KexiProject object's lifetime. */ + KexiProject(KexiProjectData* pdata, KexiDB::MessageHandler* handler = 0); + + /*! Constructor 2. Like above but sets predefined connections \a conn. + The connection should be created using the same connection data + as pdata->connectionData(). The connection will become owned by created KexiProject + object, so do not destroy it. */ + KexiProject(KexiProjectData *pdata, KexiDB::MessageHandler* handler, + KexiDB::Connection* conn); + +// KexiProject(KexiDB::ConnectionData *cdata); + + ~KexiProject(); + + /*! \return major version of KexiProject object. + This information is retrieved from database when existing project is opened. */ + int versionMajor() const; + + /*! \return minor version of KexiProject object. + @see versionMajor() */ + int versionMinor() const; + + /*! Opens existing project using project data. + \return true on success */ + tristate open(); + + /*! Like open(). + \return true on success. + Additional \a incompatibleWithKexi, is set to false on failure when + connection for the project was successfully started bu the project + is probably not compatible with Kexi - no valid "kexidb_major_ver" + value in "kexi__db" table. + This is often the case for native server-based databases. + If so, Kexi application can propose importing the database + or linking it to parent project (the latter isn't yet implemented). + For other types of errors the variable is set to true. */ + tristate open(bool &incompatibleWithKexi); + + /*! Creates new, empty project using project data. + If \a forceOverwrite is true, existing database project is silently overwritten. + Connection is created (accessible then with KexiProject::dbConnection()). + + Since KexiProject inherits KexiDB::Object, it is possible to get error message + and other information on error. + + \return true on success, false on failure, and cancelled when database exists + but \a forceOverwrite is false. */ + tristate create(bool forceOverwrite = false); + + /*! \return true if there was error during last operation on the object. */ + bool error() const { return KexiDB::Object::error(); } + + /** + * @return true if a we are connected to a database + */ + bool isConnected(); + + /** + * @return all items of a type \a i in this project + */ + KexiPart::ItemDict* items(KexiPart::Info *i); + + /** + * @return all items of a type \a mime in this project + * It is a convenience function. + */ + KexiPart::ItemDict* itemsForMimeType(const QCString &mimeType); + + /** + * Puts a list of items of a type \a i in this project into \a list. + * You can then sort this list using ItemList::sort(). + */ + void getSortedItems(KexiPart::ItemList& list, KexiPart::Info *i); + + /** + * Puts a sorted list of items of a type \a mimeType in this project into \a list. + * You can then sort this list using ItemList::sort(). + */ + void getSortedItemsForMimeType(KexiPart::ItemList& list, const QCString &mimeType); + + /** + * @return item of type \a mime and name \a name + */ + KexiPart::Item* itemForMimeType(const QCString &mimeType, const QString &name); + + /** + * @return item of type \a i and name \a name + */ + KexiPart::Item* item(KexiPart::Info *i, const QString &name); + + /** + * @return item for \a identifier + */ + KexiPart::Item* item(int identifier); + + /** + * @return the database connection associated with this project + */ + KexiDB::Connection *dbConnection() const; + + /** + * @return the project's data + */ + KexiProjectData *data() const; + + /*! Opens object pointed by \a item in a view \a viewMode. + \a staticObjectArgs can be passed for static object + (only works when part for this item is of type KexiPart::StaticPart) */ + KexiDialogBase* openObject(KexiMainWindow *wnd, KexiPart::Item& item, + int viewMode = Kexi::DataViewMode, QMap<QString,QString>* staticObjectArgs = 0); + + //! For convenience + KexiDialogBase* openObject(KexiMainWindow *wnd, const QCString &mimeType, + const QString& name, int viewMode = Kexi::DataViewMode); + + /*! Remove a part instance pointed by \a item. + \return true on success. */ + bool removeObject(KexiMainWindow *wnd, KexiPart::Item& item); + + /*! Renames a part instance pointed by \a item to a new name \a newName. + \return true on success. */ + bool renameObject(KexiMainWindow *wnd, KexiPart::Item& item, const QString& newName); + + /*! Creates part item for given part \a info. + Newly item will not be saved to the backend but stored in memory only + (owned by project), and marked as "neverSaved" (see KexiPart::Item::neverSaved()). + The item will have assigned a new unique caption like e.g. "Table15", + and unique name like "table15", but no specific identifier + (because id will be assigned on creation at the backend side). + + If \a suggestedCaption is not empty, it will be set as a caption + (with number suffix, to avoid duplicated, e.g. "employees7" + for "employees" sugested name). Name will be then built based + on this caption using KexiUtils::string2Identifier(). + + This method is used before creating new object. + \return newly created part item or NULL on any error. */ + KexiPart::Item* createPartItem(KexiPart::Info *info, + const QString& suggestedCaption = QString::null ); + + //! Added for convenience. + KexiPart::Item* createPartItem(KexiPart::Part *part, + const QString& suggestedCaption = QString::null); + + /*! Adds item \a item after it is succesfully stored as an instance of part + pointed by \a info. Also clears 'neverSaved' flag if \a item. + Used by KexiDialogBase::storeNewData(). + @internal */ + void addStoredItem(KexiPart::Info *info, KexiPart::Item *item); + + /*! removes \a item from internal dictionaries. The item is destroyed + after successful removal. + Used to delete an unstored part item previusly created with createPartItem(). */ + void deleteUnstoredItem(KexiPart::Item *item); + +#if 0 //remove? + /*! Creates object using data provided by \a dlg dialog. + Dialog's \a item (KexiDialog::partItem()) must not be stored + (KexiPart::Item::neverStored()==false) and created + by KexiProject::createPartItem(). + Identifier of the item will be updated to a final value + (stored in the backend), because previously there was temporary one set. + \return true for successfully created object or false on any error. */ + bool createObject(KexiDialogBase *dlg); +#endif + + KexiDB::Parser* sqlParser(); + + /*! Shows dialog for creating new blank project, + ans creates one. Dialog is not shown if option for automatic creation + is checked or Kexi::startupHandler().projectData() was provided from command line. + \a cancelled is set to true if creation has been cancelled (e.g. user answered + no when asked for database overwriting, etc. + \return true if database was created, false on error or when cancel was pressed */ + static KexiProject* createBlankProject(bool &cancelled, KexiProjectData* data, + KexiDB::MessageHandler* handler = 0); + + /*! Drops project described by \a data. \return true on success. + Use with care: Any KexiProject objects allocated for this project will become invalid! */ + static tristate dropProject(KexiProjectData* data, + KexiDB::MessageHandler* handler, bool dontAsk = false); + + /*! @see KexiDB::Connection::setQuerySchemaObsolete( const QString& queryName ) */ +// void setQuerySchemaObsolete( const QString& queryName ); + +// /** used to emit objectCreated() signal */ +// void emitObjectCreated(const QCString &mime, const QCString& name) { emit objectCreated(mime, name); } +// void emitTableCreated(KexiDB::TableSchema& schema) { emit tableCreated(schema); } + + protected: + /*! Creates connection using project data. + The connection will be readonly if data()->isReadOnly(). + \return true on success, otherwise false and appropriate error is set. */ + bool createConnection(); + + bool closeConnection(); + + bool initProject(); + + //! Used in open() and open(bool&). + tristate openInternal(bool *incompatibleWithKexi); + + /*! Kexi itself can define a number of internal database objects (mostly data structures), + usually tables for it's own purposes. + Even while at KexiDB library level, such "system" tables, like "kexi__objects", "kexi__objectdata" + are created automatically on database project creation, this is not enough: there are objects + needed specifically for Kexi but not for other applications utilizing KexiDB library. + Example table created here for now is "kexi__blobs". + + This method is called on create() and open(): creates necessary objects + if they are not yet existing. This especially allows to create to create these objects + (on open) within a project made with previous Kexi version not supporting + all currently defined structurtes. We're trying to be here as much backward compatible as possible. + For this purpose, here's the complete list of currently created objects: + - "kexi__blobs" - a table containing BLOBs data that can be accessed globally at Kexi projects + from within any database-aware view (table views, forms, reports, etc.) + + @param insideTransaction Embed entire creation process inside a transaction + + \return true on successful object's creation. Objects are created only once, they are not overwritten. + */ + bool createInternalStructures(bool insideTransaction); + + /*! \return Kexi part for \a item. */ + KexiPart::Part *findPartFor(KexiPart::Item& item); + + signals: + /** signal emitted on error */ + void error(const QString &title, KexiDB::Object *obj); + + /** signal emitted on error (not KexiDB-related) */ + void error(const QString &msg, const QString &desc); + + /** New \a item has been stored. */ + void newItemStored(KexiPart::Item& item); + + /** instance pointed by \a item is removed */ + void itemRemoved(const KexiPart::Item &item); + + /** instance pointed by \a item is renamed */ + void itemRenamed(const KexiPart::Item &item, const QCString& oldName); + +// /** new table \a schema created */ +// void tableCreated(KexiDB::TableSchema& schema); +// /** New object of mimetype \a mime and \a name has been created. */ +// void objectCreated(const QCString &mime, const QCString& name); + + protected: + /*! Checks whether the project's connection is read-only. + If so, error message is set and false is returned. */ + bool checkWritable(); + + class Private; + Private *d; + + friend class KexiMainWindowImpl; +}; + + +#endif diff --git a/kexi/core/kexiprojectconnectiondata.cpp b/kexi/core/kexiprojectconnectiondata.cpp new file mode 100644 index 00000000..6a73342f --- /dev/null +++ b/kexi/core/kexiprojectconnectiondata.cpp @@ -0,0 +1,152 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <sys/types.h> +#include <unistd.h> + +#include <qdom.h> +#include <qdir.h> +#include <qfile.h> +#include <qregexp.h> + +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kio/netaccess.h> +#include <kurl.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include <kexidb/connectiondata.h> +#include <kexidb/drivermanager.h> +#include "kexiprojectconnectiondata.h" + +KexiProjectConnectionData::KexiProjectConnectionData(): KexiDB::ConnectionData() +{ +} + +KexiProjectConnectionData::KexiProjectConnectionData(const QString& driverName, const QString& databaseName, const QString &host, + unsigned short int rport, const QString& user, const QString &pass, const QString& file):KexiDB::ConnectionData() +{ + m_driverName=driverName; + m_databaseName=databaseName; + hostName=host; + port=rport; + userName=user; + password=pass; + setFileName(file); +} + +KexiProjectConnectionData::KexiProjectConnectionData(const QString &driverName, const QString &fileName) + : KexiDB::ConnectionData() +{ + m_driverName=driverName; + setFileName(fileName); +} + +const QString & +KexiProjectConnectionData::generateTmpName() +{ + return QString::null; +} + +KexiProjectConnectionData* +KexiProjectConnectionData::loadInfo(QDomElement &rootElement) +{ + QDomElement engineElement = rootElement.namedItem("engine").toElement(); + QDomElement hostElement = rootElement.namedItem("host").toElement(); + QDomElement portElement = rootElement.namedItem("port").toElement(); + QDomElement nameElement = rootElement.namedItem("name").toElement(); + QDomElement userElement = rootElement.namedItem("user").toElement(); + QDomElement passElement = rootElement.namedItem("password").toElement(); + QDomElement persElement = rootElement.namedItem("persistant").toElement(); + QDomElement encodingElement = rootElement.namedItem("encoding").toElement(); + + KexiProjectConnectionData *tmp=new KexiProjectConnectionData( + engineElement.text(), nameElement.text(),hostElement.text(),portElement.text().toInt(), + userElement.text(),passElement.text(),""); + + return tmp; +} + +void KexiProjectConnectionData::setDriverName(const QString &driverName) { + m_driverName=driverName; +} + +void KexiProjectConnectionData::setDatabaseName(const QString &databaseName) { + m_databaseName=databaseName; +} + +QString KexiProjectConnectionData::driverName() const { + return m_driverName; +} + +QString KexiProjectConnectionData::databaseName() const { + return m_databaseName; +} + + +void +KexiProjectConnectionData::writeInfo(QDomDocument &domDoc) +{ + QDomElement connectionElement = domDoc.createElement("KexiDBConnection"); + domDoc.documentElement().appendChild(connectionElement); + +//DB ENGINE + QDomElement engineElement = domDoc.createElement("engine"); + connectionElement.appendChild(engineElement); + + QDomText tEngine = domDoc.createTextNode(m_driverName); + engineElement.appendChild(tEngine); + +//HOST + QDomElement hostElement = domDoc.createElement("host"); + connectionElement.appendChild(hostElement); + + QDomText tHost = domDoc.createTextNode(hostName); + hostElement.appendChild(tHost); + +//DATABASE NAME + QDomElement nameElement = domDoc.createElement("name"); + connectionElement.appendChild(nameElement); + + QDomText tName = domDoc.createTextNode(m_databaseName); + nameElement.appendChild(tName); + +//USER + QDomElement userElement = domDoc.createElement("user"); + connectionElement.appendChild(userElement); + + QDomText tUser = domDoc.createTextNode(userName); + userElement.appendChild(tUser); + +//PASSWORD STUFF + QDomElement passElement = domDoc.createElement("password"); + connectionElement.appendChild(passElement); + + QDomText tPass=domDoc.createTextNode(password); + passElement.appendChild(tPass); + +} + + + +KexiProjectConnectionData::~KexiProjectConnectionData() +{ +} diff --git a/kexi/core/kexiprojectconnectiondata.h b/kexi/core/kexiprojectconnectiondata.h new file mode 100644 index 00000000..04ff40ff --- /dev/null +++ b/kexi/core/kexiprojectconnectiondata.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPROJECTCONNECTIONDATA_H +#define KEXIPROJECTCONNECTIONDATA_H + +#include <kexidb/connectiondata.h> + +class QDomElement; +class QDomDocument; +class KoStore; +/** + * This class aims to provide + * methods to store/load database settings + * especially for file based engines. Extends KexiDB::ConnectionData with + * additional information (selected driver name and database name) + * that allows fully-automatic reconnect eg. on next application startup. + */ + +class KEXICORE_EXPORT KexiProjectConnectionData:public KexiDB::ConnectionData +{ + public: + + KexiProjectConnectionData(); + + KexiProjectConnectionData(const QString& driverName, const QString& databaseName, const QString &hostName, unsigned short int port, + const QString& userName, const QString &password, const QString& fileName); + + /** + * connect to a embedded database + */ + KexiProjectConnectionData(const QString &driverName, const QString &fileName=QString::null); + + ~KexiProjectConnectionData(); + + static const QString &generateTmpName(); + + static KexiProjectConnectionData* loadInfo(QDomElement &e); + void writeInfo(QDomDocument &doc); + + void setDriverName(const QString &driverName); + void setDatabaseName(const QString &databaseName); + + QString driverName() const; + QString databaseName() const; + + private: + QString m_driverName; + QString m_databaseName; + +}; + +#endif diff --git a/kexi/core/kexiprojectdata.cpp b/kexi/core/kexiprojectdata.cpp new file mode 100644 index 00000000..68966a11 --- /dev/null +++ b/kexi/core/kexiprojectdata.cpp @@ -0,0 +1,176 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <sys/types.h> +#include <unistd.h> + +#include <qdom.h> +#include <qdir.h> +#include <qfile.h> +#include <qregexp.h> + +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kio/netaccess.h> +#include <kurl.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include <kexidb/drivermanager.h> +#include "kexiprojectdata.h" + + +//! @internal +class KexiProjectDataPrivate +{ +public: + KexiProjectDataPrivate() + : userMode(false) + , readOnly(false) + {} + + KexiDB::ConnectionData connData; + QDateTime lastOpened; + bool userMode : 1; + bool readOnly : 1; +}; + +//--------------------------------------- + +KexiProjectData::KexiProjectData() + : QObject(0, "KexiProjectData") + , KexiDB::SchemaData() + , formatVersion(0) + , d( new KexiProjectDataPrivate() ) +{ +} + +KexiProjectData::KexiProjectData( + const KexiDB::ConnectionData &cdata, const QString& dbname, const QString& caption ) + : QObject(0, "KexiProjectData") + , KexiDB::SchemaData() + , formatVersion(0) + , d( new KexiProjectDataPrivate() ) +{ + d->connData = cdata; + setDatabaseName(dbname); + setCaption(caption); +} + +KexiProjectData::KexiProjectData( const KexiProjectData& pdata ) + : QObject(0, "KexiProjectData"), KexiDB::SchemaData() + , d( 0 ) +{ + *this = pdata; + autoopenObjects = pdata.autoopenObjects; +/* + d->connData = *pdata.connectionData(); + setDatabaseName(pdata.databaseName()); + setCaption(pdata.caption());*/ +} + +KexiProjectData::~KexiProjectData() +{ + delete d; +} + +KexiProjectData& KexiProjectData::operator=(const KexiProjectData& pdata) +{ + delete d; //this is old + static_cast<KexiDB::SchemaData&>(*this) = static_cast<const KexiDB::SchemaData&>(pdata); + //deep copy + d = new KexiProjectDataPrivate(); + *d = *pdata.d; +// d->connData = *pdata.constConnectionData(); +// setDatabaseName(pdata.databaseName()); +// setCaption(pdata.caption()); +// setDescription(pdata.description()); + return *this; +} + +KexiDB::ConnectionData* KexiProjectData::connectionData() +{ + return &d->connData; +} + +const KexiDB::ConnectionData* KexiProjectData::constConnectionData() const +{ + return &d->connData; +} + +QString KexiProjectData::databaseName() const +{ + return KexiDB::SchemaData::name(); +} + +void KexiProjectData::setDatabaseName(const QString& dbName) +{ + KexiDB::SchemaData::setName(dbName); +} + +bool KexiProjectData::userMode() const +{ + return d->userMode; +} + +QDateTime KexiProjectData::lastOpened() const +{ + return d->lastOpened; +} + +void KexiProjectData::setLastOpened(const QDateTime& lastOpened) +{ + d->lastOpened=lastOpened; + +} +QString KexiProjectData::description() const +{ + return KexiDB::SchemaData::description(); +} + +void KexiProjectData::setDescription(const QString& desc) +{ + return KexiDB::SchemaData::setDescription(desc); +} + +QString KexiProjectData::infoString(bool nobr) const +{ + if (constConnectionData()->fileName().isEmpty()) { + //server-based + return QString(nobr ? "<nobr>" : "") + QString("\"%1\"").arg(databaseName()) + (nobr ? "</nobr>" : "") + + (nobr ? " <nobr>" : " ") + i18n("database connection", "(connection %1)") + .arg(constConnectionData()->serverInfoString()) + (nobr ? "</nobr>" : ""); + } + //file-based + return QString(nobr ? "<nobr>" : "") + + QString("\"%1\"").arg(constConnectionData()->fileName()) + (nobr ? "</nobr>" : ""); +} + +void KexiProjectData::setReadOnly(bool set) +{ + d->readOnly = set; +} + +bool KexiProjectData::isReadOnly() const +{ + return d->readOnly; +} + diff --git a/kexi/core/kexiprojectdata.h b/kexi/core/kexiprojectdata.h new file mode 100644 index 00000000..b4fc99d3 --- /dev/null +++ b/kexi/core/kexiprojectdata.h @@ -0,0 +1,108 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@gmx.at> + Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIPROJECTDATA_H +#define KEXIPROJECTDATA_H + +#include <kexidb/connectiondata.h> +#include <kexidb/schemadata.h> + +#include <qdatetime.h> + +class KexiProjectDataPrivate; + +/** @short Kexi project core data member + + Contains: + - project name + - database name + - connection data + - date and time of last opening +*/ +class KEXICORE_EXPORT KexiProjectData : public QObject, public KexiDB::SchemaData +{ + public: + typedef QPtrList<KexiProjectData> List; + typedef QMap<QCString,QString> ObjectInfo; + + KexiProjectData(); + + KexiProjectData( const KexiDB::ConnectionData &cdata, + const QString& dbname = QString::null, const QString& caption = QString::null ); + + /*! Constructs a copy of \a pdata */ + KexiProjectData( const KexiProjectData& pdata ); + + ~KexiProjectData(); + + KexiProjectData& operator=(const KexiProjectData& pdata); + + /*! \return true if there is the User Mode set in internal + project settings. */ + bool userMode() const; + + KexiDB::ConnectionData* connectionData(); + + const KexiDB::ConnectionData* constConnectionData() const; + + /*! \return database name. + In fact, this is the same as KexiDB::SchemaData::name() */ + QString databaseName() const; + void setDatabaseName(const QString& dbName); + + /*! \return user-visible string better describing the project than just databaseName(). + For server-based projects returns i18n'd string: + "<project name>" (connection: user\@server:port). + For file-based projects returns project's filename. + If \a nobr is true, \<nobr\> tags are added around '(connection: user\@server:port)' + (useful for displaying in message boxes). */ + QString infoString(bool nobr = true) const; + + QDateTime lastOpened() const; + void setLastOpened(const QDateTime& lastOpened); + + QString description() const; + void setDescription(const QString& desc); + + /*! If \a set is true, sets readonly flag for this data, so any connection opened for the project will + be readonly. Change this flag before using this data in KexiProject instance, + otherwise you will need to reopen the project. */ + void setReadOnly(bool set); + + /*! \return readonly flag. False by default. + @see setReadOnly() */ + bool isReadOnly() const; + + /*! objects to open on startup (come from command line "-open" option) + It's public for convenience */ + QValueList<ObjectInfo> autoopenObjects; + + /*! @internal + Format version used when saving the data to a shortcut file. + This is set to 0 by default what means KexiDBShortcutFile_version should be used on saving. + If KexiDBShortcutFile was used to create this KexiProjectData object, + the version information is be retrieved from the file. */ + uint formatVersion; + + private: + KexiProjectDataPrivate *d; +}; + +#endif diff --git a/kexi/core/kexiprojectset.cpp b/kexi/core/kexiprojectset.cpp new file mode 100644 index 00000000..51e804dd --- /dev/null +++ b/kexi/core/kexiprojectset.cpp @@ -0,0 +1,112 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "kexiprojectset.h" +#include "kexi.h" + +#include <kexidb/driver.h> +#include <kexidb/connection.h> +#include <kexidb/msghandler.h> + +#include <kdebug.h> + +//#define ERRMSG(a1, a2) +// { if (m_msgHandler) m_msgHandler->showErrorMessage(a1, a2); } + +//! @internal +class KexiProjectSetPrivate +{ +public: + KexiProjectSetPrivate() + { +// list.setAutoDelete(true); + } + KexiProjectData::List list; +// KexiDB::MessageHandler* msgHandler; +}; + +KexiProjectSet::KexiProjectSet(KexiDB::MessageHandler* handler) +: KexiDB::Object(handler) +, d(new KexiProjectSetPrivate()) +{ +} + +KexiProjectSet::KexiProjectSet(KexiDB::ConnectionData &conndata, + KexiDB::MessageHandler* handler) +: KexiDB::Object(handler) +, 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*/); +// kexidbg << dbnames.count() << endl; + if (conn->error()) { + setError(conn); + delete conn; + return; + } + delete conn; + conn = 0; + 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<KexiProjectData> it( d->list ); + for (;it.current();++it) { + if (it.current()->databaseName().lower()==_dbName) + return it.current(); + } + return 0; +} diff --git a/kexi/core/kexiprojectset.h b/kexi/core/kexiprojectset.h new file mode 100644 index 00000000..af251722 --- /dev/null +++ b/kexi/core/kexiprojectset.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2005 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KEXIPROJECTSET_H +#define KEXIPROJECTSET_H + +#include <kexidb/connectiondata.h> +#include <kexidb/object.h> + +#include "kexiprojectdata.h" + +class KexiProjectSetPrivate; +namespace KexiDB { + class MessageHandler; +} + +/*! @short Stores information about multiple kexi project-data items */ +class KEXICORE_EXPORT KexiProjectSet : public KexiDB::Object +{ + public: + + /*! Creates empty project set. Use addProjectData to add a project data. + \a handler can be provided to receive error messages. */ + KexiProjectSet(KexiDB::MessageHandler* handler = 0); + + /*! Creates project set filled with all projects found using \a conndata. + There may be error during project list retrieving - use appropriate + KexiDB::Object::error(), and similar methods to get error message. + \a handler can be provided to receive error messages. */ + KexiProjectSet(KexiDB::ConnectionData &conndata, + KexiDB::MessageHandler* handler = 0); + + ~KexiProjectSet(); + + /*! Adds \a data as project data. + \a data will be owned by this object. */ + void addProjectData(KexiProjectData *data); + + //! \return list object + KexiProjectData::List list() const; + + //! Case insensitive lookup. + //! \return project data for databased \a dbName or NULL if not found + KexiProjectData* findProject(const QString &dbName) const; + + private: + KexiProjectSetPrivate *d; +}; + +#endif // KEXINEWDBCONNDIALOG_H + diff --git a/kexi/core/kexisearchandreplaceiface.cpp b/kexi/core/kexisearchandreplaceiface.cpp new file mode 100644 index 00000000..0c533d93 --- /dev/null +++ b/kexi/core/kexisearchandreplaceiface.cpp @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexisearchandreplaceiface.h" + +KexiSearchAndReplaceViewInterface::KexiSearchAndReplaceViewInterface() +{ +} + +KexiSearchAndReplaceViewInterface::~KexiSearchAndReplaceViewInterface() +{ +} + +//----------------------------------------------------------------------- + +KexiSearchAndReplaceViewInterface::Options::Options() + : columnNumber(AllColumns) + , textMatching(MatchAnyPartOfField) + , searchDirection(DefaultSearchDirection) + , caseSensitive(false) + , wholeWordsOnly(false) + , promptOnReplace(true) +{ +} + diff --git a/kexi/core/kexisearchandreplaceiface.h b/kexi/core/kexisearchandreplaceiface.h new file mode 100644 index 00000000..f17d8358 --- /dev/null +++ b/kexi/core/kexisearchandreplaceiface.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KexiSearchAndReplaceViewInterface_H +#define KexiSearchAndReplaceViewInterface_H + +#include <kexiutils/tristate.h> +#include <qstring.h> +class QVariant; +class QStringList; + +//! @short An interface used by Kexi views (KexiViewBase) supporting search/replace features +class KEXICORE_EXPORT KexiSearchAndReplaceViewInterface +{ + public: + KexiSearchAndReplaceViewInterface(); + virtual ~KexiSearchAndReplaceViewInterface(); + + //! @short Specifies options for find and replace operations. + /*! A GUI for setting these options is provided by KexiFindDialog class. */ + class KEXICORE_EXPORT Options { + public: + Options(); + + //! Special values for columnNumber. + enum SpecialLookInValue { + AllColumns = -1, //!< "all columns" (the default) + CurrentColumn = -2 //!< "current column" + }; + //! Column number to look in, AllColumns means "all columns" (the default) + //! and CurrentColumn means "current column". + int columnNumber; + + //! Specifies possible options for text matching + enum TextMatching { + MatchAnyPartOfField = 0, //!< Matched text can be any part of field (the default) + MatchWholeField = 1, //!< Matched text must be the whole field + MatchStartOfField = 2 //!< Matched text must be at the start of field + }; + + //! Specifies possible options for text matching + TextMatching textMatching; + + //! Specifies search direction + enum SearchDirection { + SearchUp = 0, //!< Search up (previous) from the current position + SearchDown = 1, //!< Search down (next) from the current position (the default) + SearchAllRows = 2, //!< Search from the first to the last row + DefaultSearchDirection = SearchDown //! Used to mark the default + }; + + //! Specifies search direction + SearchDirection searchDirection; + + //! True for searching is case-sensitive (false by default) + bool caseSensitive : 1; + + //! True for searching for whole words only (false by default) + bool wholeWordsOnly : 1; + + //! True if question should be displayed before every replacement made (true by default) + bool promptOnReplace : 1; + }; + + /*! Sets up data for find/replace dialog, based on view's data model. + \a columnNames should contain column name, \a columnCaptions should contain column captions, + and \a currentColumnName should beset to current column's name. + Implementation should set up values and return true if find/replace dialog should be filled. */ + virtual bool setupFindAndReplace(QStringList& columnNames, QStringList& columnCaptions, + QString& currentColumnName) = 0; + + /*! Finds \a valueToFind within the view. + \a options are used to control the process. Selection is moved to found value. + \return true if value has been found, false if value has not been found, + and cancelled if there is nothing to find or there is no data to search in. + If \a next is true, "find next" is performed, else "find previous" is performed. */ + virtual tristate find(const QVariant& valueToFind, + const KexiSearchAndReplaceViewInterface::Options& options, bool next) = 0; + + /*! Finds \a valueToFind within the view and replaces with \a replacement + \a options are used to control the process. + \return true if value has been found and replaced, false if value + has not been found and replaced, and cancelled if there is nothing + to find or there is no data to search in or the data is read only. + If \a replaceAll is true, all found values are replaced. */ + virtual tristate findNextAndReplace(const QVariant& valueToFind, const QVariant& replacement, + const KexiSearchAndReplaceViewInterface::Options& options, bool replaceAll) = 0; +}; + +#endif diff --git a/kexi/core/kexisharedactionhost.cpp b/kexi/core/kexisharedactionhost.cpp new file mode 100644 index 00000000..11ab94db --- /dev/null +++ b/kexi/core/kexisharedactionhost.cpp @@ -0,0 +1,291 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexisharedactionhost.h" +#include "kexisharedactionhost_p.h" +#include "kexiactionproxy.h" +#include "kexidialogbase.h" + +#include <kexiutils/utils.h> + +#include <kiconloader.h> +#include <kguiitem.h> +#include <kdebug.h> + +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) +{ +/*test if (qstrcmp(action_name, "tablepart_toggle_pkey")==0) { + kdDebug() << "tablepart_toggle_pkey" << endl; + }*/ + if (!d) + return; //sanity + 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) +{ + if (!d) + return; + bool insideDialogBase = o && (o->inherits("KexiDialogBase") || 0!=KexiUtils::findParent<KexiDialogBase>(o, "KexiDialogBase")); + + KexiActionProxy *p = o ? d->actionProxies[ o ] : 0; + 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<KAction> 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; + if (dynamic_cast<KMdiMainFrm*>(d->mainWin)) { + fw = dynamic_cast<KMdiMainFrm*>(d->mainWin)->activeWindow(); + } + else { + QWidget *aw = qApp->activeWindow(); + if (!aw) + aw = d->mainWin; + fw = aw->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; +} + +KActionPtrList KexiSharedActionHost::sharedActions() const +{ + return d->sharedActions; +} + +/*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<QWidget> 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() ) + ); +} + +KAction* KexiSharedActionHost::createSharedAction(const KGuiItem& guiItem, const KShortcut &cut, + const char *name, KActionCollection* col) +{ + return createSharedActionInternal( + new KAction(guiItem, cut, 0/*receiver*/, 0/*slot*/, + col ? col : d->mainWin->actionCollection(), name)); +} + +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/kexisharedactionhost.h b/kexi/core/kexisharedactionhost.h new file mode 100644 index 00000000..abb960bf --- /dev/null +++ b/kexi/core/kexisharedactionhost.h @@ -0,0 +1,165 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXISHAREDACTIONHOST_H +#define KEXISHAREDACTIONHOST_H + +#include <qguardedptr.h> +#include <qasciidict.h> +#include <qobject.h> +#include <qpair.h> + +#include <kstdaction.h> +#include <kaction.h> + +class KShortcut; +class KGuiItem; +class KMainWindow; +class KexiActionProxy; +class KexiSharedActionHostPrivate; + +namespace KexiPart { + class Part; +} + +//! Acts as application-wide host that offers shared actions. +/*! + You can inherit this class together with KMainWindow (or any KMainWindow). + Call setAsDefaultHost() to make the host default for all shared actions that have + not explicitly specified host. + + For example how all this is done, see KexiMainWindow implementation. + + \sa KexiActionProxy, KexiMainWindow +*/ + +class KEXICORE_EXPORT KexiSharedActionHost +{ + public: + + /*! Constructs host for main window \a mainWin. */ + KexiSharedActionHost(KMainWindow* mainWin); + + virtual ~KexiSharedActionHost(); + + /*! \return true if \a w can accept shared actions. + This method is used by focusWindow() to look up widgets hierarchy + for widget that can accept shared actions. + Default implementation always returns false. + You can reimplement it e.g. like in KexiMainWindowImpl::acceptsSharedActions(): + \code + return o->inherits("KexiDialogBase") || o->inherits("KexiViewBase"); + \endcode + */ + virtual bool acceptsSharedActions(QObject *o); + + /*! \return window widget that is currently focused (using QWidget::focusWidget()) + and matches acceptsSharedActions(). If focused widget does not match, + it's parent, grandparent, and so on is checked. If all this fails, + or no widget has focus, NULL is returned. + Also works if currently focused window is detached (as in KMDI). + */ + QWidget* focusWindow(); + + /*! Sets this host as default shared actions host. */ + void setAsDefaultHost(); + + /*! \return default shared actions host, used when no host + is explicitly specified for shared actions. + There can be exactly one deault shared actions host. */ + static KexiSharedActionHost& defaultHost(); + + /*! \return shared actions list. */ + KActionPtrList sharedActions() const; + + /*! PROTOTYPE, DO NOT USE YET */ + void setActionVolatile( KAction *a, bool set ); + + + protected: + + /*! Invalidates all shared actions declared using createSharedAction(). + Any shared action will be enabled if \a o (typically: a child window or a dock window) + has this action plugged _and_ it is available (i.e. enabled). + Otherwise the action is disabled. + + If \a o is not KexiDialogBase or its child, + actions are only invalidated if these come from mainwindow's KActionCollection + (thus part-actions are untouched when the focus is e.g. in the Property Editor. + + Call this method when it is known that some actions need invalidation + (e.g. when new window is activated). See how it is used in KexiMainWindowImpl. + */ + virtual void invalidateSharedActions(QObject *o); + + void setActionAvailable(const char *action_name, bool avail); + + /*! Plugs shared actions proxy \a proxy for this host. */ + void plugActionProxy(KexiActionProxy *proxy); + + /*! Updates availability of action \a action_name for object \a obj. + Object is mainly the window. */ + void updateActionAvailable(const char *action_name, bool avail, QObject *obj); + + /*! \return main window for which this host is defined. */ + KMainWindow* mainWindow() const; + + /*! Creates shared action using \a text, \a pix_name pixmap, shortcut \a cut, + optional \a name. You can pass your own action collection as \a col. + If \a col action collection is null, main window's action will be used. + Pass desired KAction subclass with \a subclassName (e.g. "KToggleAction") to have + that subclass allocated instead just KAction (what is the default). + Created action's data is owned by the main window. */ + KAction* createSharedAction(const QString &text, const QString &pix_name, + const KShortcut &cut, const char *name, KActionCollection* col = 0, + const char *subclassName = 0); + + /*! Like above - creates shared action, but from standard action identified by \a id. + Action's data is owned by the main window. */ + KAction* createSharedAction( KStdAction::StdAction id, const char *name, + KActionCollection* col = 0); + + /*! Creates shared action with name \a name and shortcut \a cut + by copying all properties of \a guiItem. + If \a col action collection is null, main window's action will be used. */ + KAction* createSharedAction(const KGuiItem& guiItem, const KShortcut &cut, const char *name, + KActionCollection* col = 0); + + /*! \return action proxy for object \a o, or NULL if this object has + no plugged shared actions. */ + KexiActionProxy* actionProxyFor(QObject *o) const; + + /*! Like actionProxyFor(), but takes the proxy from the host completely. + This is called by KExiActionProxy on its destruction. */ + KexiActionProxy* takeActionProxyFor(QObject *o); + + private: + /*! Helper function for createSharedAction(). */ + KAction* createSharedActionInternal( KAction *action ); + + KexiSharedActionHostPrivate *d; + + friend class KexiActionProxy; + friend class KexiPart::Part; + friend class KexiViewBase; + friend class KexiDialogBase; +}; + +#endif + diff --git a/kexi/core/kexisharedactionhost_p.h b/kexi/core/kexisharedactionhost_p.h new file mode 100644 index 00000000..f56db586 --- /dev/null +++ b/kexi/core/kexisharedactionhost_p.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXISHAREDACTIONHOST_P_H +#define KEXISHAREDACTIONHOST_P_H + +#include <qobject.h> +#include <qptrdict.h> +#include <qsignalmapper.h> + +#include <kmainwindow.h> + +#include "kexiactionproxy.h" + +class KexiSharedActionHost; + +class KexiVolatileActionData +{ + public: + KexiVolatileActionData() { plugged=false; } +// KAction *kaction; + bool plugged : 1; +}; + +//! @internal +class KEXICORE_EXPORT KexiSharedActionHostPrivate : public QObject +{ + Q_OBJECT + + public: + KexiSharedActionHostPrivate(KexiSharedActionHost *h); + + public slots: + void slotAction(const QString& act_id); + + public: + QPtrDict<KexiActionProxy> actionProxies; + KMainWindow *mainWin; + KActionPtrList sharedActions; + QSignalMapper actionMapper; + QPtrDict<KexiVolatileActionData> volatileActions; + QAsciiDict<QWidget> enablers; + + KexiSharedActionHost *host; +}; + +#endif + diff --git a/kexi/core/kexistartupdata.cpp b/kexi/core/kexistartupdata.cpp new file mode 100644 index 00000000..301ebdbd --- /dev/null +++ b/kexi/core/kexistartupdata.cpp @@ -0,0 +1,84 @@ +/* This file is part of the KDE project + Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexistartupdata.h" +#include "kexi.h" + +#include <kexidb/driver.h> +#include <kexidb/drivermanager.h> + +#include <qfileinfo.h> +#include <qcstring.h> + +KexiStartupData::KexiStartupData() + : m_projectData(0) + , m_action(KexiStartupData::DoNothing) + , m_forcedUserMode(false) + , m_forcedDesignMode(false) + , m_isProjectNavigatorVisible(false) +// , m_createDB(false) +// , m_dropDB(false) +// , m_alsoOpenDB(false) +{ +} + +KexiStartupData::~KexiStartupData() +{ +} + +KexiProjectData *KexiStartupData::projectData() const +{ + return m_projectData; +} + +KexiStartupData::Action KexiStartupData::action() const +{ + return m_action; +} + +bool KexiStartupData::forcedUserMode() const +{ + return m_forcedUserMode; +} + +bool KexiStartupData::forcedDesignMode() const +{ + return m_forcedDesignMode; +} + +bool KexiStartupData::isProjectNavigatorVisible() const +{ + if (m_forcedUserMode && !m_forcedDesignMode) + return m_isProjectNavigatorVisible; + return true; +} + +KexiStartupData::Import KexiStartupData::importActionData() const +{ + return m_importActionData; +} + +KexiStartupData::Import::Import() +{ +} + +KexiStartupData::Import::operator bool() const +{ + return !fileName.isEmpty() && !mimeType.isEmpty(); +} diff --git a/kexi/core/kexistartupdata.h b/kexi/core/kexistartupdata.h new file mode 100644 index 00000000..71999bcb --- /dev/null +++ b/kexi/core/kexistartupdata.h @@ -0,0 +1,90 @@ +/* This file is part of the KDE project + Copyright (C) 2004-2007 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXI_STARTUPDATA_H +#define KEXI_STARTUPDATA_H + +#include <qstring.h> + +class KexiProjectData; + +//! Startup data used for storing results of startup operations in Kexi. +//! @see KexiStartupHandler +class KEXICORE_EXPORT KexiStartupData +{ + public: + typedef enum Action { + DoNothing, + CreateBlankProject, + CreateFromTemplate, + OpenProject, + ImportProject, + Exit + }; + + /*! Data required to perform import action. + It is set by KexiStartupHandler::detectActionForFile() + if a need for project/data importing has been detected. */ + class KEXICORE_EXPORT Import + { + public: + Import(); + operator bool() const; + QString fileName; + QString mimeType; + }; + + KexiStartupData(); + virtual ~KexiStartupData(); + + virtual bool init() { return true; }; + + Action action() const; + + //! \return project data of a project that should be opened (for action()==OpenProject) + KexiProjectData *projectData() const; + + //! \return import action's data needed to perform import (for action()==ImportProject) + KexiStartupData::Import importActionData() const; + + /*! \return true is the Design Mode is forced for this project. + Used on startup (by --design-mode comman line switch). */ + bool forcedDesignMode() const; + + /*! \return true is the User Mode is forced for this project. + Used on startup (by --user-mode comman line switch). + By default this is false. */ + bool forcedUserMode() const; + + /*! \return true if the Project Navigator should be visible even if User Mode is on. */ + bool isProjectNavigatorVisible() const; + + protected: + KexiProjectData *m_projectData; + Action m_action; + KexiStartupData::Import m_importActionData; + bool m_forcedUserMode : 1; + bool m_forcedDesignMode : 1; + bool m_isProjectNavigatorVisible : 1; + bool m_createDB : 1; + bool m_dropDB : 1; + bool m_alsoOpenDB : 1; +}; + +#endif diff --git a/kexi/core/kexistaticpart.cpp b/kexi/core/kexistaticpart.cpp new file mode 100644 index 00000000..c7e323e1 --- /dev/null +++ b/kexi/core/kexistaticpart.cpp @@ -0,0 +1,63 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexistaticpart.h" +#include "kexipartinfo_p.h" +#include "kexipartitem.h" +#include "kexi.h" + +using namespace KexiPart; + +//------------------------------ + +StaticInfo::StaticInfo(const QCString& mimeType, const QString& itemIcon, const QString& objectName) + : Info() +{ + d->mimeType = mimeType; + d->itemIcon = itemIcon; + d->objectName = objectName; +} + +StaticInfo::~StaticInfo() +{ +} + +//------------------------------ + +StaticPart::StaticPart(const QCString& mimeType, const QString& itemIcon, const QString& objectName) + : Part(&Kexi::partManager(), new StaticInfo(mimeType, itemIcon, objectName)) +{ + Kexi::partManager().insertStaticPart(this); +} + +StaticPart::~StaticPart() +{ +} + +KexiViewBase* StaticPart::createView(QWidget *parent, KexiDialogBase* dialog, + KexiPart::Item &item, int viewMode) +{ + Q_UNUSED(parent); + Q_UNUSED(dialog); + Q_UNUSED(item); + Q_UNUSED(viewMode); + //unused + return 0; +} diff --git a/kexi/core/kexistaticpart.h b/kexi/core/kexistaticpart.h new file mode 100644 index 00000000..fe127fe4 --- /dev/null +++ b/kexi/core/kexistaticpart.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> + Copyright (C) 2003,2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXISTATICPART_H +#define KEXISTATICPART_H + +#include "kexipart.h" +#include "kexipartinfo.h" + +namespace KexiPart +{ + +/** + * @short Information about a static Kexi Part (plugin). + */ +class KEXICORE_EXPORT StaticInfo : public Info +{ + public: + StaticInfo(const QCString& mimeType, const QString& itemIcon, const QString& objectName); + ~StaticInfo(); + + protected: +}; + +/** + * @short Static Kexi Part (plugin). + */ +class KEXICORE_EXPORT StaticPart : public Part +{ + public: + StaticPart(const QCString& mimeType, const QString& itemIcon, const QString& objectName); + virtual ~StaticPart(); + + /*! Creates a new view for mode \a viewMode, \a item and \a parent. The view will be + used inside \a dialog. \a args arguments can be passed. */ + virtual KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog, + KexiPart::Item &item, int viewMode, QMap<QString,QString>* args) = 0; + + protected: + //! unused by static parts + KexiViewBase* createView(QWidget *parent, KexiDialogBase* dialog, + KexiPart::Item &item, int viewMode = Kexi::DataViewMode); +}; + +} + +#endif diff --git a/kexi/core/kexitabledesignerinterface.cpp b/kexi/core/kexitabledesignerinterface.cpp new file mode 100644 index 00000000..9d6e1dc0 --- /dev/null +++ b/kexi/core/kexitabledesignerinterface.cpp @@ -0,0 +1,28 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexitabledesignerinterface.h" + +KexiTableDesignerInterface::KexiTableDesignerInterface() +{ +} + +KexiTableDesignerInterface::~KexiTableDesignerInterface() +{ +} diff --git a/kexi/core/kexitabledesignerinterface.h b/kexi/core/kexitabledesignerinterface.h new file mode 100644 index 00000000..60d7d0c7 --- /dev/null +++ b/kexi/core/kexitabledesignerinterface.h @@ -0,0 +1,104 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl> + + 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXITABLEDESIGNERINTERFACE_H +#define KEXITABLEDESIGNERINTERFACE_H + +#include <koproperty/property.h> +#include <kexiutils/tristate.h> + +namespace KoProperty { + class Set; +} + +//! Interface for main Table Designer's commands +/*! This interface has been specified to enable invoking Table Designer's commands + at application's level. This is used in the "altertable" test suite, available in + kexi/tests/altertable Kexi source code directory. + KexiTableDesignerInterface is implemented by KexiTableDesignerView, so it's enough + to use dynamic_cast: + \code + KexiDialogBase *dlg = KexiMainWindowImpl::self()->currentDialog(); + if (dlg) { + KexiTableDesignerInterface* designerIface + = dynamic_cast<KexiTableDesignerInterface*>( dlg->selectedView() ); + if (designerIface) { + //for example, delete row #3 + designerIface->deleteRow( 3, true ); + } + } + \endcode + Methods of KexiTableDesignerInterface are also used by classes of KexiTableDesignerCommands + namespace (KCommand derivatives) for implementing commands execution and unexecution. + + All the methods contain addCommand argument. Set if to true to get the command added + to the undo/redo buffer, what will look like real user's action. This is also needed + to poperly generate arguments for commiting the "alter table" operation. +*/ +class KEXICORE_EXPORT KexiTableDesignerInterface +{ + public: + KexiTableDesignerInterface(); + + virtual ~KexiTableDesignerInterface(); + + /*! Clears field information entered for row. + This is performed by removing values from caption and data type columns. */ + virtual void clearRow(int row, bool addCommand = false) = 0; + + /*! Inserts a new field with \a caption for \a row. + Property set is also created. + Existing field will be overwritten, so use insertEmptyRow() + is you want to move subsequent fields down. */ + virtual void insertField(int row, const QString& caption, bool addCommand = false) = 0; + + /*! Inserts a new \a field for \a row. + Property set is also created. \a set will be deeply-copied into the new set. + Existing field will be overwritten, so use insertEmptyRow() + is you want to move subsequent fields down. */ + virtual void insertField(int row, KoProperty::Set& set, bool addCommand = false) = 0; + + /*! Inserts a new empty row at position \a row. */ + virtual void insertEmptyRow( int row, bool addCommand = false ) = 0; + + /*! Deletes \a row from the table view. Property set is also deleted. + All the subsequent fields are moved up. */ + virtual void deleteRow( int row, bool addCommand = false ) = 0; + + /*! Changes property \a propertyName to \a newValue for a field pointed by \a fieldUID. + If \a listData is not NULL and not empty, a deep copy of it is passed to Property::setListData(). + If \a listData \a nlist if not NULL but empty, Property::setListData(0) is called. */ + virtual void changeFieldPropertyForRow( int fieldUID, const QCString& propertyName, + const QVariant& newValue, KoProperty::Property::ListData* const listData = 0, + bool addCommand = false ) = 0; + + /*! Creates temporary table for the current design and returns debug string for it. */ + virtual QString debugStringForCurrentTableSchema(tristate& result) = 0; + + /*! Simulates execution of alter table, and puts debug into \a debugTarget. + A case when debugTarget is not 0 is true for the alter table test suite. */ + virtual tristate simulateAlterTableExecution(QString *debugTarget) = 0; + + /*! Real execution of the Alter Table. For debugging of the real alter table. + \return true on success, false on failure and cancelled if user has cancelled + execution. */ + virtual tristate executeRealAlterTable() = 0; +}; + +#endif diff --git a/kexi/core/kexitemplateloader.cpp b/kexi/core/kexitemplateloader.cpp new file mode 100644 index 00000000..489d5f51 --- /dev/null +++ b/kexi/core/kexitemplateloader.cpp @@ -0,0 +1,112 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexitemplateloader.h" + +#include <kstandarddirs.h> +#include <kglobal.h> +#include <klocale.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kapplication.h> + +#include <qdir.h> + +//static +KexiTemplateInfo::List KexiTemplateLoader::loadListInfo() +{ + KexiTemplateInfo::List list; + const QString subdir = QString(kapp->instanceName()) + "/templates"; + QString lang( KGlobal::locale()->language() ); + QStringList dirs( kapp->dirs()->findDirs("data", subdir) ); + while (true) { + foreach( QStringList::ConstIterator, it, dirs) { + QDir dir((*it)+lang); + if (!dir.exists()) + continue; + if (!dir.isReadable()) { + kdWarning() << "KexiTemplateLoader::loadListInfo() \"" << dir.absPath() << "\" not readable!" << endl; + continue; + } + const QStringList templateDirs( dir.entryList(QDir::Dirs, QDir::Name) ); + const QString absDirPath( dir.absPath() + '/' ); + foreach(QStringList::ConstIterator, templateIt, templateDirs) { + if ((*templateIt)=="." || (*templateIt=="..")) + continue; + KexiTemplateInfo info = KexiTemplateLoader::loadInfo( absDirPath + *templateIt ); + if (!info.name.isEmpty()) + list.append( info ); + } + } + if (lang != "en" && list.isEmpty()) //not found for current locale, try "en" + lang = "en"; + else + break; + } + return list; +} + +//static +KexiTemplateInfo KexiTemplateLoader::loadInfo(const QString& directory) +{ + QDir dir(directory); + if (!dir.isReadable()) { + kdWarning() << "KexiTemplateLoader::loadInfo() \"" + << directory << "\" not readable!" << endl; + return KexiTemplateInfo(); + } + if (!QFileInfo(directory+"/info.txt").isReadable()) + return KexiTemplateInfo(); + KConfig infoTxt(directory+"/info.txt", true/*readonly*/, false/*local*/); + KexiTemplateInfo info; + info.name = infoTxt.readEntry("Name"); + if (info.name.isEmpty()) { + kdWarning() << "KexiTemplateLoader::loadInfo() \"" << (directory+"/info.txt") << "\" contains no \"name\" field" << endl; + return KexiTemplateInfo(); + } + const QStringList templateFiles( dir.entryList("*.kexi", QDir::Files|QDir::Readable, QDir::Name) ); + if (templateFiles.isEmpty()) { + kdWarning() << "KexiTemplateLoader::loadInfo() no readable .kexi template file found in \"" << directory << "\"" << endl; + return KexiTemplateInfo(); + } + info.filename = directory+"/"+templateFiles.first(); + info.description = infoTxt.readEntry("Description"); + const QString iconFileName( infoTxt.readEntry("Icon") ); + if (!iconFileName.isEmpty()) + info.icon = QPixmap(directory+'/'+iconFileName); + if (info.icon.isNull()) + info.icon = DesktopIcon("kexiproject_sqlite"); //default + QStringList autoopenObjectsString = infoTxt.readListEntry("AutoOpenObjects"); + foreach( QStringList::ConstIterator, it, autoopenObjectsString) { + KexiProjectData::ObjectInfo autoopenObject; + QStringList autoopenObjectNameSplitted( QStringList::split(':', *it) ); + if (autoopenObjectNameSplitted.count()>1) { + autoopenObject["type"] = autoopenObjectNameSplitted[0]; + autoopenObject["name"] = autoopenObjectNameSplitted[1]; + } + else { + autoopenObject["type"] = "table"; + autoopenObject["name"] = autoopenObjectNameSplitted[0]; + } + autoopenObject["action"] = "open"; + info.autoopenObjects.append( autoopenObject ); + } + return info; +} diff --git a/kexi/core/kexitemplateloader.h b/kexi/core/kexitemplateloader.h new file mode 100644 index 00000000..131eda31 --- /dev/null +++ b/kexi/core/kexitemplateloader.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXI_TEMPLLOADER_H +#define KEXI_TEMPLLOADER_H + +#include <qpixmap.h> +#include "kexiprojectdata.h" + +//! A structure providing information about single kexi database template file +struct KEXICORE_EXPORT KexiTemplateInfo +{ + typedef QValueList<KexiTemplateInfo> List; + + QString filename, name, description; + QPixmap icon; + QValueList<KexiProjectData::ObjectInfo> autoopenObjects; +}; + +//! Handles retrieving information about templates +class KEXICORE_EXPORT KexiTemplateLoader +{ + public: + static KexiTemplateInfo::List loadListInfo(); + static KexiTemplateInfo loadInfo(const QString& directory); +}; + +#endif diff --git a/kexi/core/kexitextmsghandler.cpp b/kexi/core/kexitextmsghandler.cpp new file mode 100644 index 00000000..a75c7eb3 --- /dev/null +++ b/kexi/core/kexitextmsghandler.cpp @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexitextmsghandler.h" + +#include "kexi.h" +#include <kexidb/utils.h> +#include <kexiutils/utils.h> + +KexiTextMessageHandler::KexiTextMessageHandler(QString &messageTarget, QString &detailsTarget) + : KexiGUIMessageHandler(0) + , m_messageTarget(&messageTarget) + , m_detailsTarget(&detailsTarget) +{ + *m_messageTarget = QString::null; + *m_detailsTarget = QString::null; +} + +KexiTextMessageHandler::~KexiTextMessageHandler() +{ +} + +void +KexiTextMessageHandler::showMessage(MessageType type, + const QString &title, const QString &details) +{ + Q_UNUSED(type); + if (!m_enableMessages) + return; + + //'wait' cursor is a nonsense now + KexiUtils::removeWaitCursor(); + + QString msg(title); + if (title.isEmpty()) + msg = i18n("Unknown error"); + msg = "<qt><p>"+msg+"</p>"; + *m_messageTarget = msg; + *m_detailsTarget = details; +} + diff --git a/kexi/core/kexitextmsghandler.h b/kexi/core/kexitextmsghandler.h new file mode 100644 index 00000000..74463cc7 --- /dev/null +++ b/kexi/core/kexitextmsghandler.h @@ -0,0 +1,37 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXITXTMSGHANDLER_H +#define KEXITXTMSGHANDLER_H + +#include "kexiguimsghandler.h" + +//! @short A wrapper that redirects messages to a string variables instead of displaying them. +//! See KexiTextMessageHandler constructor for details. +class KEXICORE_EXPORT KexiTextMessageHandler : public KexiGUIMessageHandler +{ + public: + KexiTextMessageHandler(QString &messageTarget, QString &detailsTarget); + virtual ~KexiTextMessageHandler(); + virtual void showMessage(MessageType type, const QString &title, const QString &details); + + QString *m_messageTarget, *m_detailsTarget; +}; + +#endif diff --git a/kexi/core/kexiuseraction.cpp b/kexi/core/kexiuseraction.cpp new file mode 100644 index 00000000..eb521de5 --- /dev/null +++ b/kexi/core/kexiuseraction.cpp @@ -0,0 +1,108 @@ +#include <kmessagebox.h> +#include <kdebug.h> +#include <kshortcut.h> + +#include <kexidb/cursor.h> + +#include "kexipart.h" +#include "kexipartmanager.h" + +#include "kexiproject.h" +#include "keximainwindow.h" +#include "kexiuseraction.h" + +KexiUserAction::KexiUserAction(KexiMainWindow *win, KActionCollection *parent, const QString &name, const QString &text, const QString &pixmap) + : KAction(text, pixmap, KShortcut::null(), this, SLOT(execute()), parent, name.latin1()) +{ + m_win = win; + m_method = 0; + connect(this, SIGNAL(activated()), this, SLOT(execute())); +} + +void +KexiUserAction::setMethod(int method, Arguments args) +{ + m_method = method; + m_args = args; +} + +void +KexiUserAction::execute() +{ + kdDebug() << "KexiUserAction::execute(): " << KexiUserActionMethod::methodName(m_method) << endl; + + switch(m_method) + { + case OpenObject: //open a project object + { + //get partinfo + KexiPart::Info *i = Kexi::partManager().infoForMimeType(m_args[0].toString().latin1()); + if (!i) { + KMessageBox::error(m_win, i18n("Specified part does not exist")); + return; + } + + Kexi::partManager().part(i); //load part if doesn't exists + KexiPart::Item *item = m_win->project()->item(i, m_args[1].toString()); + bool openingCancelled; + if(!m_win->openObject(item, Kexi::DataViewMode, openingCancelled) && !openingCancelled) { + KMessageBox::error(m_win, i18n("Specified document could not be opened.")); + return; + } + if (openingCancelled) + return; + break; + } + default: + break; + } +} + +KexiUserAction * +KexiUserAction::fromCurrentRecord(KexiMainWindow *context, KActionCollection *parent, KexiDB::Cursor *c) +{ + if(!c || c->bof() || c->eof()) + return 0; + + KexiUserAction *a = new KexiUserAction(context, parent, c->value(1).toString(), c->value(2).toString(), c->value(3).toString()); + QString args = c->value(5).toString(); + bool quote = false; + + Arguments arg; + QString tmp; + const int len = args.length(); + for(int i=0; i < len; i++) + { + if(args[i] == '"') // if current char is quoted unqote or other way round + { + quote = !quote; + } + else if(args[i] == ',' && !quote) //if item end add tmp to argumentstack and strip quotes if nessesery + { + if(tmp.left(1)=="\"" && tmp.right(1)=="\"") + tmp = tmp.mid(1, tmp.length()-2); + + arg.append(QVariant(tmp)); + tmp = ""; + } + else //else simply add char to tmp + { + tmp += args[i]; + } + } + + if(tmp.left(1)=="\"" && tmp.right(1)=="\"") + tmp = tmp.mid(1, tmp.length()-2); + + arg.append(QVariant(tmp)); + + a->setMethod(c->value(4).toInt(), arg); + return a; +} + +KexiUserAction::~KexiUserAction() +{ +} + +#include "kexiuseraction.moc" + diff --git a/kexi/core/kexiuseraction.h b/kexi/core/kexiuseraction.h new file mode 100644 index 00000000..822220f0 --- /dev/null +++ b/kexi/core/kexiuseraction.h @@ -0,0 +1,81 @@ +#ifndef KEXIUSERACTION_H +#define KEXIUSERACTION_H + +#include <kaction.h> + +#include "kexiuseractionmethod.h" + +namespace KexiDB +{ + class Cursor; +} +class KexiMainWindow; +typedef QValueVector<QVariant> Arguments; + +/*! action that can be defined by a user for a special scope e.g. main, form ... + the actions can have some predefined \ref Methods which are described in \ref KexiUserActionMethod + e.g. OpenObject, ExecuteScript ... those methods take different arguments also described in \ref KexiUserActionMethod +*/ +class KEXICORE_EXPORT KexiUserAction : public KAction +{ + Q_OBJECT + + public: + /*! bytecode of available methods */ + enum Methods + { + MethodNone = 0, + OpenObject = 1, + CloseObject = 2, + DeleteObject = 3, + ExecuteScript = 4, + ExitKexi = 5, + + LastMethod = 6 //use the last integer here... so we can stop iteration + }; + + /*! argument types */ + enum ArgTypes + { + String = 0, + Integer = 1, + Bool = 2, + KexiPart = 3, + KexiItem = 4 + }; + + /*! constructs an action + \note methods are associated using setMethod() + */ + KexiUserAction(KexiMainWindow *context, KActionCollection *parent, const QString &name, const QString &text, const QString &pixmap); + ~KexiUserAction(); + + /*! sets execution information associated with this action this will mostly look like + \code + KexiUserAction *action = new KexiUserAction(...); + Arguments arg; + arg.append(QVariant("kexi/form")); + arg.append(QVariant("main")); + action->setMethod(KexiUserAction::OpenAction, arg); + \endcode + */ + void setMethod(int method, Arguments args); + + /*! creates a KexiUserAction from current record in \a c + mostly needed for creation from kexi__useractions table */ + static KexiUserAction *fromCurrentRecord(KexiMainWindow *context, KActionCollection *parent, KexiDB::Cursor *c); + + protected slots: + /*! actually executes the associated method + \note KexiUserAction automatically connects KAction::activated() to KexiUserAction::execute() + */ + void execute(); + + private: + KexiMainWindow *m_win; + int m_method; + Arguments m_args; +}; + +#endif + diff --git a/kexi/core/kexiuseractionmethod.cpp b/kexi/core/kexiuseractionmethod.cpp new file mode 100644 index 00000000..a2ec2914 --- /dev/null +++ b/kexi/core/kexiuseractionmethod.cpp @@ -0,0 +1,32 @@ +#include <klocale.h> + +#include "kexiuseraction.h" +#include "kexiuseractionmethod.h" + +KexiUserActionMethod::KexiUserActionMethod(int method, ArgTypes types, ArgNames names) +{ + m_method = method; + m_types = types; + m_names = names; +} + +QString +KexiUserActionMethod::methodName(int method) +{ + switch(method) + { + case KexiUserAction::OpenObject: + return i18n("Open Object"); + case KexiUserAction::CloseObject: + return i18n("Close Object"); + case KexiUserAction::DeleteObject: + return i18n("Delete Object"); + case KexiUserAction::ExecuteScript: + return i18n("Execute Script"); + case KexiUserAction::ExitKexi: + return i18n("Exit Main Application"); + default: + return QString::null; + } +} + diff --git a/kexi/core/kexiuseractionmethod.h b/kexi/core/kexiuseractionmethod.h new file mode 100644 index 00000000..5bfae22c --- /dev/null +++ b/kexi/core/kexiuseractionmethod.h @@ -0,0 +1,42 @@ +#ifndef KEXIUSERACTIONMETHOD_H +#define KEXIUSERACTIONMETHOD_H + +#include <qvaluevector.h> +#include <qstring.h> +#include <qvariant.h> + +typedef QValueVector<int> ArgTypes; +typedef QValueVector<QString> ArgNames; + +/*! describes a UserActionCommand */ +class KEXICORE_EXPORT KexiUserActionMethod +{ + public: + /*! constructs a UserActionCommand describtion */ + KexiUserActionMethod(int method, ArgTypes types, ArgNames names); + + /*! \return method id of this method */ + int method() { return m_method; } + + /*! \return argument type information of this method */ + ArgTypes types() { return m_types; } + + /*! \return i18n argument names of this method */ + ArgNames names() { return m_names; } + + + + /*! \return i18n method name for \a method */ + static QString methodName(int method); + + /*! \return an i18n string for \a type */ + static QString typeName(int type); + + private: + int m_method; + ArgTypes m_types; + ArgNames m_names; +}; + +#endif + diff --git a/kexi/core/kexiviewbase.cpp b/kexi/core/kexiviewbase.cpp new file mode 100644 index 00000000..1696f502 --- /dev/null +++ b/kexi/core/kexiviewbase.cpp @@ -0,0 +1,328 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "kexiviewbase.h" + +#include "keximainwindow.h" +#include "kexidialogbase.h" +#include "kexiproject.h" +#include <koproperty/set.h> + +#include <kexidb/connection.h> +#include <kexidb/utils.h> +#include <kexiutils/utils.h> + +#include <kdebug.h> + +KexiViewBase::KexiViewBase(KexiMainWindow *mainWin, QWidget *parent, const char *name) + : QWidget(parent, name) + , KexiActionProxy(this, mainWin) + , m_mainWin(mainWin) + , m_viewWidget(0) + , m_parentView(0) + , m_newlyAssignedID(-1) + , m_viewMode(0) //unknown! + , m_dirty(false) +{ + QWidget *wi=this; + while ((wi = wi->parentWidget()) && !wi->inherits("KexiDialogBase")) + ; + m_dialog = (wi && wi->inherits("KexiDialogBase")) ? static_cast<KexiDialogBase*>(wi) : 0; + if (m_dialog) { + //init view mode number for this view (obtained from dialog where this view is created) + if (m_dialog->supportsViewMode(m_dialog->m_creatingViewsMode)) + m_viewMode = m_dialog->m_creatingViewsMode; + } + + installEventFilter(this); +} + +KexiViewBase::~KexiViewBase() +{ +} + +KexiPart::Part* KexiViewBase::part() const +{ + return m_dialog ? m_dialog->part() : 0; +} + +tristate KexiViewBase::beforeSwitchTo(int /* mode */, bool & /*dontStore*/) +{ + return true; +} + +tristate KexiViewBase::afterSwitchFrom(int /* mode */) +{ + return true; +} + +QSize KexiViewBase::preferredSizeHint(const QSize& otherSize) +{ + KexiDialogBase* dlg = parentDialog(); + if (dlg && dlg->mdiParent()) { + QRect r = dlg->mdiParent()->mdiAreaContentsRect(); + return otherSize.boundedTo( QSize( + r.width() - 10, + r.height() - dlg->mdiParent()->captionHeight() - dlg->pos().y() - 10 + ) ); + } + return otherSize; +} + +void KexiViewBase::closeEvent( QCloseEvent * e ) +{ + bool cancel = false; + emit closing(cancel); + if (cancel) { + e->ignore(); + return; + } + QWidget::closeEvent(e); +} + +KoProperty::Set *KexiViewBase::propertySet() +{ + return 0; +} + +void KexiViewBase::propertySetSwitched() +{ + if (parentDialog()) + m_mainWin->propertySetSwitched( parentDialog(), false ); +} + +void KexiViewBase::propertySetReloaded(bool preservePrevSelection, const QCString& propertyToSelect) +{ + if (parentDialog()) + m_mainWin->propertySetSwitched( parentDialog(), true, preservePrevSelection, propertyToSelect ); +} + +void KexiViewBase::setDirty(bool set) +{ +/* if (m_dirty == set) {//no change here + if (m_dialog) { + // however, it's a change from dialog perspective + if (m_dialog->dirty()!=set) + m_dialog->dirtyChanged(); + } + return; + }*/ + const bool changed = (m_dirty != set); + m_dirty = set; + m_dirty = dirty(); +// if (m_dirty!=set)//eventually didn't change +// return; + if (m_parentView) { + m_parentView->setDirty(m_dirty); + } + else { + if (changed && m_dialog) + m_dialog->dirtyChanged(this); + } +} + +/*bool KexiViewBase::saveData() +{ + //TODO.... + + //finally: + setDirty(false); + return true; +}*/ + +KexiDB::SchemaData* KexiViewBase::storeNewData(const KexiDB::SchemaData& sdata, bool & /*cancel*/) +{ + KexiDB::SchemaData *new_schema = new KexiDB::SchemaData(); + *new_schema = sdata; + + if (!m_mainWin->project()->dbConnection() + ->storeObjectSchemaData( *new_schema, true )) + { + delete new_schema; + new_schema=0; + } + m_newlyAssignedID = new_schema->id(); + return new_schema; +} + +tristate KexiViewBase::storeData(bool dontAsk) +{ + Q_UNUSED(dontAsk); + if (!m_dialog || !m_dialog->schemaData()) + return false; + if (!m_mainWin->project()->dbConnection() + ->storeObjectSchemaData( *m_dialog->schemaData(), false /*existing object*/ )) + { + return false; + } + setDirty(false); + return true; +} + +bool KexiViewBase::loadDataBlock( QString &dataString, const QString& dataID, bool canBeEmpty ) +{ + if (!m_dialog) + return false; + const tristate res = m_mainWin->project()->dbConnection()->loadDataBlock(m_dialog->id(), dataString, dataID); + if (canBeEmpty && ~res) { + dataString = QString::null; + return true; + } + return res == true; +} + +bool KexiViewBase::storeDataBlock( const QString &dataString, const QString &dataID ) +{ + if (!m_dialog) + return false; + int effectiveID; + if (m_newlyAssignedID>0) {//ID not yet stored within dialog, but we've got ID here + effectiveID = m_newlyAssignedID; + m_newlyAssignedID = -1; + } + else + effectiveID = m_dialog->id(); + + return effectiveID>0 + && m_mainWin->project()->dbConnection()->storeDataBlock(effectiveID, dataString, dataID); +} + +bool KexiViewBase::removeDataBlock( const QString& dataID ) +{ + if (!m_dialog) + return false; + return m_mainWin->project()->dbConnection()->removeDataBlock(m_dialog->id(), dataID); +} + +bool KexiViewBase::eventFilter( QObject *o, QEvent *e ) +{ + if (e->type()==QEvent::FocusIn || e->type()==QEvent::FocusOut) {// && o->inherits("QWidget")) { +// //hp==true if currently focused widget is a child of this table view +// const bool hp = Kexi::hasParent( static_cast<QWidget*>(o), focusWidget()); +// kexidbg << "KexiViewBase::eventFilter(): " << o->name() << " " << e->type() << endl; + if (KexiUtils::hasParent( this, static_cast<QWidget*>(o))) { + if (e->type()==QEvent::FocusOut && focusWidget() && !KexiUtils::hasParent( this, focusWidget())) { + //focus out: when currently focused widget is not a parent of this view + emit focus(false); + } else if (e->type()==QEvent::FocusIn) { + emit focus(true); + } + if (e->type()==QEvent::FocusOut) { // && focusWidget() && Kexi::hasParent( this, focusWidget())) { // && focusWidget()->inherits("KexiViewBase")) { +// kdDebug() << focusWidget()->className() << " " << focusWidget()->name()<< endl; +// kdDebug() << o->className() << " " << o->name()<< endl; + KexiViewBase *v = KexiUtils::findParent<KexiViewBase>(o, "KexiViewBase") ; +// QWidget *www=v->focusWidget(); + if (v) { + while (v->m_parentView) + v = v->m_parentView; + if (KexiUtils::hasParent( this, static_cast<QWidget*>(v->focusWidget()) )) + v->m_lastFocusedChildBeforeFocusOut = static_cast<QWidget*>(v->focusWidget()); +// v->m_lastFocusedChildBeforeFocusOut = static_cast<QWidget*>(o); //focusWidget(); + } + } + + if (e->type()==QEvent::FocusIn && m_actionProxyParent) { + m_actionProxyParent->m_focusedChild = this; + } +// m_mainWin->invalidateSharedActions(this); + } + } + return false; +} + +void KexiViewBase::setViewWidget(QWidget* w, bool focusProxy) +{ + if (m_viewWidget == w) + return; + if (m_viewWidget) { + m_viewWidget->removeEventFilter(this); + } + m_viewWidget = w; + if (m_viewWidget) { + m_viewWidget->installEventFilter(this); + if (focusProxy) + setFocusProxy(m_viewWidget); //js: ok? + } +} + +void KexiViewBase::addChildView( KexiViewBase* childView ) +{ + m_children.append( childView ); + addActionProxyChild( childView ); + childView->m_parentView = this; +// if (m_parentView) +// childView->installEventFilter(m_parentView); + childView->installEventFilter(this); + +} + +void KexiViewBase::setFocus() +{ + if (!m_lastFocusedChildBeforeFocusOut.isNull()) { +// kdDebug() << "FOCUS: " << m_lastFocusedChildBeforeFocusOut->className() << " " << m_lastFocusedChildBeforeFocusOut->name()<< endl; + QWidget *w = m_lastFocusedChildBeforeFocusOut; + m_lastFocusedChildBeforeFocusOut = 0; + w->setFocus(); + } + else { + if (hasFocus()) + setFocusInternal(); + else + setFocusInternal(); + } + m_mainWin->invalidateSharedActions(this); +} + +KAction* KexiViewBase::sharedAction( const char *action_name ) +{ + if (part()) { + KActionCollection *ac; + if ( (ac = part()->actionCollectionForMode( viewMode() )) ) { + KAction* a = ac->action( action_name ); + if (a) + return a; + } + } + return KexiActionProxy::sharedAction(action_name); +} + +void KexiViewBase::setAvailable(const char* action_name, bool set) +{ + if (part()) { + KActionCollection *ac; + KAction* a; + if ( (ac = part()->actionCollectionForMode( viewMode() )) && (a = ac->action( action_name )) ) { + a->setEnabled(set); +//why? return; + } + } + KexiActionProxy::setAvailable(action_name, set); +} + +void KexiViewBase::updateActions(bool activated) +{ + //do nothing here + //do the same for children :) + for (QPtrListIterator<KexiViewBase> it(m_children); it.current(); ++it) { + it.current()->updateActions(activated); + } +} + +#include "kexiviewbase.moc" + diff --git a/kexi/core/kexiviewbase.h b/kexi/core/kexiviewbase.h new file mode 100644 index 00000000..18cd056d --- /dev/null +++ b/kexi/core/kexiviewbase.h @@ -0,0 +1,280 @@ +/* This file is part of the KDE project + Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIVIEWBASE_H +#define KEXIVIEWBASE_H + +#include <qwidget.h> + +#include "kexiactionproxy.h" + +class KexiMainWindow; +class KexiDialogBase; + +namespace KoProperty { + class Set; +} + +namespace KexiDB { + class SchemaData; +} + +//! Base class for single view embeddable of in KexiDialogBase. +/*! This class automatically works as a proxy for shared (application-wide) actions. + KexiViewBase has 'dirty' flag to indicate that view's data has changed. + This flag's state is reused by KexiDialogBase object that contain the view. + KexiViewBase objects can be also nested, using addChildView(): any actions and 'dirty' flag + are transmited to parent view in this case. + + KexiViewBase objects are usually allocated within KexiDialogBase objects by implementing + KexiPart::createView() method. See query or table part code for examples. + + KexiViewBase object can be also allocated without attaching it KexiDialogBase, + especially withinn dock window. see KexiMainWindowImpl::initNavigator() to see example + how KexiBrowser does this. +*/ +class KEXICORE_EXPORT KexiViewBase : public QWidget, public KexiActionProxy +{ + Q_OBJECT + + public: + KexiViewBase(KexiMainWindow *mainWin, QWidget *parent, const char *name = 0); + virtual ~KexiViewBase(); + + //! \return kexi main window that contain this view + inline KexiMainWindow *mainWin() const { return m_mainWin; } + + //! \return parent KexiDialogBase that contains this view, or 0 if no dialog contain this view + KexiDialogBase* parentDialog() const { return m_dialog; } + + /*! Added for convenience. + \return KexiPart object that was used to create this view (with a dialog) + or 0 if this view is not created using KexiPart. \sa parentDialog() */ + KexiPart::Part* part() const; + + /*! \return preferred size hint, that can be used to resize the view. + It is computed using maximum of (a) \a otherSize and (b) current KMDI dock area's size, + so the view won't exceed this maximum size. The method is used e.g. in KexiDialogBase::sizeHint(). + If you reimplement this method, do not forget to return value of + yoursize.boundedTo( KexiViewBase::preferredSizeHint(otherSize) ). */ + virtual QSize preferredSizeHint(const QSize& otherSize); + + virtual bool eventFilter( QObject *o, QEvent *e ); + + void addChildView( KexiViewBase* childView ); + + /*! True if contents (data) of the view is dirty and need to be saved + This may or not be used, depending if changes in the dialog + are saved immediately (e.g. like in datatableview) or saved by hand (by user) + (e.g. like in alter-table dialog). + "Dirty" flag is reused by KexiDialogBase::dirty(). + Default implementation just uses internal m_dirty flag, that is false by default. + Reimplement this if you e.g. want reuse other "dirty" + flag from internal structures that may be changed. */ + virtual bool dirty() const { return m_dirty; } + + /*! \return the view mode for this view. */ + int viewMode() const { return m_viewMode; } + + /*! Reimpelmented from KexiActionProxy. + \return shared action with name \a action_name for this view. + If there's no such action declared in Kexi Part (part()), + global shared action is returned (if exists). */ + virtual KAction* sharedAction( const char *action_name ); + + /*! Enables or disables shared action declared in Kexi Part (part()). + If there's no such action, global shared action is enabled or disabled (if exists). */ + virtual void setAvailable(const char* action_name, bool set); + + public slots: + virtual void setFocus(); + + /*! Call this in your view's implementation whenever current property set + (returned by propertySet()) is switched to other, + so property editor contents need to be completely replaced. */ + virtual void propertySetSwitched(); + + /*! Sets dirty flag on or off. It the flag changes, + dirty(bool) signal is emitted by parent dialog (KexiDialog), + to inform the world about that. If this view has a parent view, setDirty() + is called also on parent view. + Always use this function to update 'dirty' flag information. */ + void setDirty(bool set); + + /*! Equal to setDirty(true). */ + void setDirty() { setDirty(true); } + + signals: + //! emitted when the view is about to close + void closing(bool& cancel); + + void focus(bool in); + + protected: + /*! called by KexiDialogBase::switchToViewMode() right before dialog is switched to new mode + By default does nothing. Reimplement this if you need to do something + before switching to this view. + \return true if you accept or false if a error occupied and view shouldn't change + If there is no error but switching should be just cancelled + (probably after showing some info messages), you need to return cancelled. + Set \a dontStore to true (it's false by default) if you want to avoid data storing + by storeData() or storeNewData(). */ + virtual tristate beforeSwitchTo(int mode, bool &dontStore); + + /*! called by KexiDialogBase::switchToViewMode() right after dialog is switched to new mode + By default does nothing. Reimplement this if you need to do something + after switching to this view. + \return true if you accept or false if a error occupied and view shouldn't change + If there is no error but switching should be just cancelled + (probably after showing some info messages), you need to return cancelled. */ + virtual tristate afterSwitchFrom(int mode); + + virtual void closeEvent( QCloseEvent * e ); + + /*! \return a property set for this view. For reimplementation. By default returns NULL. */ + virtual KoProperty::Set *propertySet(); + + /*! Call this in your view's implementation whenever current property set + is changed that few properties are now visible and/or few other are invisible, + so property editor operating on this property set should be completely reloaded. + If \a preservePrevSelection is true and there was a property set + assigned before call, previously selected item will be preselected + in the editor (if found). */ + void propertySetReloaded(bool preservePrevSelection = false, const QCString& propertyToSelect = QCString()); + + /*! Tells this dialog to create and store data of the new object + pointed by \a sdata on the backend. + Called by KexiDialogBase::storeNewData(). + Default implementation: + - makes a deep copy of \a sdata + - stores object schema data \a sdata in 'kexi__objects' internal table + using Connection::storeObjectSchemaData(). + Reimpelment this for your needs. + Requirements: + - deep copy of \a sdata should be made + - schema data should be created at the backend + (by calling KexiViewBase::storeNewData(const KexiDB::SchemaData& sdata)), + or using Connection::storeObjectSchemaData() or more specialized + method. For example, KexiAlterTableDialog + uses Connection::createTable(TableSchema) for this + (tableschema is SchemaData subclass) to store more information than + just a schem adata. You should use such subclasses if needed. + Should return newly created schema data object on success. + In this case, do not store schema object yourself (make deep copy if needed). */ + virtual KexiDB::SchemaData* storeNewData(const KexiDB::SchemaData& sdata, bool &cancel); + + /*! Loads large string data \a dataString block (e.g. xml form's representation), + indexed with optional \a dataID, from the database backend. + If \a canBeEmpty is true and there is no data block for dataID, true is returned + and \a dataString is set to null string. The default is false. + \return true on success + \sa storeDataBlock(). */ + bool loadDataBlock( QString &dataString, const QString& dataID = QString::null, bool canBeEmpty = false ); + + /*! Tells this view to store data changes on the backend. + Called by KexiDialogBase::storeData(). + Default implementation: + - makes a deep copy of \a sdata + - stores object schema data \a sdata in 'kexi__objects' internal table + using Connection::storeObjectSchemaData(). + If \a dontAsk is true, no question dialog will + be shown to the user. The default is false. + + Reimpelment this for your needs. Should return true on success + or cancelled when the task should be cancelled. + \sa storeNewData() */ + virtual tristate storeData(bool dontAsk = false); + + /*! Stores (potentially large) string data \a dataString, block (e.g. xml form's representation), + at the database backend. Block will be stored in "kexi__objectdata" table pointed by + this object's id and an optional \a dataID identifier. + + If dialog's id is not available (KexiDialogBase::id()), + then ID that was just created in storeNewData() is used + (see description of m_newlyAssignedID member). + If there is already such record in the table, it's simply overwritten. + \return true on success + */ + bool storeDataBlock( const QString &dataString, const QString &dataID = QString::null ); + + /*! Removes (potentially large) string data (e.g. xml form's representation), + pointed by optional \a dataID, from the database backend. + \return true on success. Does not fail if the block doe not exists. + Note that if \a dataID is not specified, all data blocks for this view will be removed. + \sa storeDataBlock(). */ + bool removeDataBlock( const QString& dataID = QString::null); + + void setViewWidget(QWidget* w, bool focusProxy = false); + + /*! Updates actions (e.g. availability). Reimplement it, if needed (you must + call superclass impelmentation at the end!). + This implementation does nothing for this view but calls updateActions() + for every child-view of this view. + called by KexiDialogBase on dialog's activation (\a activated is true) + or deactivation. */ + virtual void updateActions(bool activated); + + virtual void setFocusInternal() { QWidget::setFocus(); } + + /*! Allows to react on parent dialog's detaching (only for KMDI's ChildFrame mode) + - it is called by KexiDialogBase::youAreDetached(). + Default implementation does nothing. + Implement it if you want to perform some appropriate actions. */ + virtual void parentDialogDetached() {}; + + /*! Allows to react on parent dialog's attaching (only for KMDI's ChildFrame mode) + - it is called by KexiDialogBase::youAreAttached(). + Default implementation does nothing. + Implement it if you want to perform some appropriate actions. */ + virtual void parentDialogAttached() {}; + + QString m_defaultIconName; + + KexiMainWindow *m_mainWin; + + KexiDialogBase *m_dialog; + + QWidget *m_viewWidget; + + KexiViewBase *m_parentView; + + QGuardedPtr<QWidget> m_lastFocusedChildBeforeFocusOut; + + private: + /*! Member set to newly assigned object's ID in storeNewData() + and used in storeDataBlock(). This is needed because usually, + storeDataBlock() can be called from storeNewData() and in this case + dialog has not yet assigned valid identifier (it has just negative temp. number). + \sa KexiDialogBase::id() + */ + int m_newlyAssignedID; + + /*! Mode for this view. Initialized by KexiDialogBase::switchToViewMode(). + Can be useful when single class is used for more than one view (e.g. KexiDBForm). */ + int m_viewMode; + + QPtrList<KexiViewBase> m_children; + + bool m_dirty : 1; + + friend class KexiDialogBase; +}; + +#endif + |